diff options
Diffstat (limited to 'src')
190 files changed, 8637 insertions, 3949 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index ac43d84b7..021b070e0 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -47,6 +47,10 @@ android { jniLibs.useLegacyPackaging = true } + androidResources { + generateLocaleConfig = true + } + defaultConfig { // TODO If this is ever modified, change application_id in strings.xml applicationId = "org.yuzu.yuzu_emu" diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index a67351727..f10131b24 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -26,7 +26,6 @@ SPDX-License-Identifier: GPL-3.0-or-later android:supportsRtl="true" android:isGame="true" android:appCategory="game" - android:localeConfig="@xml/locales_config" android:banner="@drawable/tv_banner" android:fullBackupContent="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules_api_31" 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 115f72710..9ebd6c732 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 @@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu import android.app.Dialog import android.content.DialogInterface +import android.net.Uri import android.os.Bundle import android.text.Html import android.text.method.LinkMovementMethod @@ -16,7 +17,7 @@ import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder import java.lang.ref.WeakReference import org.yuzu.yuzu_emu.activities.EmulationActivity -import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath +import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable @@ -68,7 +69,7 @@ object NativeLibrary { @Keep @JvmStatic fun openContentUri(path: String?, openmode: String?): Int { - return if (isNativePath(path!!)) { + return if (DocumentsTree.isNativePath(path!!)) { YuzuApplication.documentsTree!!.openContentUri(path, openmode) } else { FileUtil.openContentUri(path, openmode) @@ -78,7 +79,7 @@ object NativeLibrary { @Keep @JvmStatic fun getSize(path: String?): Long { - return if (isNativePath(path!!)) { + return if (DocumentsTree.isNativePath(path!!)) { YuzuApplication.documentsTree!!.getFileSize(path) } else { FileUtil.getFileSize(path) @@ -88,23 +89,41 @@ object NativeLibrary { @Keep @JvmStatic fun exists(path: String?): Boolean { - return if (isNativePath(path!!)) { + return if (DocumentsTree.isNativePath(path!!)) { YuzuApplication.documentsTree!!.exists(path) } else { - FileUtil.exists(path) + FileUtil.exists(path, suppressLog = true) } } @Keep @JvmStatic fun isDirectory(path: String?): Boolean { - return if (isNativePath(path!!)) { + return if (DocumentsTree.isNativePath(path!!)) { YuzuApplication.documentsTree!!.isDirectory(path) } else { FileUtil.isDirectory(path) } } + @Keep + @JvmStatic + fun getParentDirectory(path: String): String = + if (DocumentsTree.isNativePath(path)) { + YuzuApplication.documentsTree!!.getParentDirectory(path) + } else { + path + } + + @Keep + @JvmStatic + fun getFilename(path: String): String = + if (DocumentsTree.isNativePath(path)) { + YuzuApplication.documentsTree!!.getFilename(path) + } else { + FileUtil.getFilename(Uri.parse(path)) + } + /** * Returns true if pro controller isn't available and handheld is */ @@ -215,32 +234,6 @@ object NativeLibrary { external fun initGameIni(gameID: String?) - /** - * Gets the embedded icon within the given ROM. - * - * @param filename the file path to the ROM. - * @return a byte array containing the JPEG data for the icon. - */ - external fun getIcon(filename: String): ByteArray - - /** - * Gets the embedded title of the given ISO/ROM. - * - * @param filename The file path to the ISO/ROM. - * @return the embedded title of the ISO/ROM. - */ - external fun getTitle(filename: String): String - - external fun getDescription(filename: String): String - - external fun getGameId(filename: String): String - - external fun getRegions(filename: String): String - - external fun getCompany(filename: String): String - - external fun isHomebrew(filename: String): Boolean - external fun setAppDirectory(directory: String) /** @@ -259,7 +252,7 @@ object NativeLibrary { external fun reloadKeys(): Boolean - external fun initializeEmulation() + external fun initializeSystem(reload: Boolean) external fun defaultCPUCore(): Int @@ -294,11 +287,6 @@ object NativeLibrary { external fun stopEmulation() /** - * Resets the in-memory ROM metadata cache. - */ - external fun resetRomMetadata() - - /** * Returns true if emulation is running (or is paused). */ external fun isRunning(): Boolean @@ -474,12 +462,12 @@ object NativeLibrary { } fun setEmulationActivity(emulationActivity: EmulationActivity?) { - Log.verbose("[NativeLibrary] Registering EmulationActivity.") + Log.debug("[NativeLibrary] Registering EmulationActivity.") sEmulationActivity = WeakReference(emulationActivity) } fun clearEmulationActivity() { - Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") + Log.debug("[NativeLibrary] Unregistering EmulationActivity.") sEmulationActivity.clear() } @@ -518,6 +506,36 @@ object NativeLibrary { external fun initializeEmptyUserDirectory() /** + * Gets the launch path for a given applet. It is the caller's responsibility to also + * set the system's current applet ID before trying to launch the nca given by this function. + * + * @param id The applet entry ID + * @return The applet's launch path + */ + external fun getAppletLaunchPath(id: Long): String + + /** + * Sets the system's current applet ID before launching. + * + * @param appletId One of the ids in the Service::AM::Applets::AppletId enum + */ + external fun setCurrentAppletId(appletId: Int) + + /** + * Sets the cabinet mode for launching the cabinet applet. + * + * @param cabinetMode One of the modes that corresponds to the enum in Service::NFP::CabinetMode + */ + external fun setCabinetMode(cabinetMode: Int) + + /** + * Checks whether NAND contents are available and valid. + * + * @return 'true' if firmware is available + */ + external fun isFirmwareAvailable(): Boolean + + /** * Button type for use in onTouchEvent */ object ButtonType { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index 8c053670c..d114bd53d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt @@ -11,6 +11,7 @@ import java.io.File import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.GpuDriverHelper +import org.yuzu.yuzu_emu.utils.Log fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir @@ -49,6 +50,7 @@ class YuzuApplication : Application() { DirectoryInitialization.start() GpuDriverHelper.initializeDriverParameters() NativeLibrary.logDeviceInfo() + Log.logDeviceInfo() createNotificationChannels() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index e96a2059b..054e4b755 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -45,9 +45,9 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.Game -import org.yuzu.yuzu_emu.utils.ControllerMappingHelper import org.yuzu.yuzu_emu.utils.ForegroundService import org.yuzu.yuzu_emu.utils.InputHandler +import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.MemoryUtil import org.yuzu.yuzu_emu.utils.NfcReader import org.yuzu.yuzu_emu.utils.ThemeHelper @@ -57,17 +57,16 @@ import kotlin.math.roundToInt class EmulationActivity : AppCompatActivity(), SensorEventListener { private lateinit var binding: ActivityEmulationBinding - private var controllerMappingHelper: ControllerMappingHelper? = null - var isActivityRecreated = false private lateinit var nfcReader: NfcReader - private lateinit var inputHandler: InputHandler private val gyro = FloatArray(3) private val accel = FloatArray(3) private var motionTimestamp: Long = 0 private var flipMotionOrientation: Boolean = false + private var controllerIds = InputHandler.getGameControllerIds() + private val actionPause = "ACTION_EMULATOR_PAUSE" private val actionPlay = "ACTION_EMULATOR_PLAY" private val actionMute = "ACTION_EMULATOR_MUTE" @@ -82,6 +81,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { } override fun onCreate(savedInstanceState: Bundle?) { + Log.gameLaunched = true ThemeHelper.setTheme(this) super.onCreate(savedInstanceState) @@ -95,8 +95,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { isActivityRecreated = savedInstanceState != null - controllerMappingHelper = ControllerMappingHelper() - // Set these options now so that the SurfaceView the game renders into is the right size. enableFullscreenImmersive() @@ -105,12 +103,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { nfcReader = NfcReader(this) nfcReader.initialize() - inputHandler = InputHandler() - inputHandler.initialize() + InputHandler.initialize() val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { - if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) { + if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) { Toast.makeText( this, getString( @@ -162,6 +159,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { super.onResume() nfcReader.startScanning() startMotionSensorListener() + InputHandler.updateControllerIds() buildPictureInPictureParams() } @@ -195,7 +193,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { return super.dispatchKeyEvent(event) } - return inputHandler.dispatchKeyEvent(event) + return InputHandler.dispatchKeyEvent(event) } override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { @@ -210,7 +208,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { return true } - return inputHandler.dispatchGenericMotionEvent(event) + return InputHandler.dispatchGenericMotionEvent(event) } override fun onSensorChanged(event: SensorEvent) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt new file mode 100644 index 000000000..a21a705c1 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.adapters + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.content.res.ResourcesCompat +import androidx.fragment.app.FragmentActivity +import androidx.navigation.findNavController +import androidx.recyclerview.widget.RecyclerView +import org.yuzu.yuzu_emu.HomeNavigationDirections +import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.databinding.CardAppletOptionBinding +import org.yuzu.yuzu_emu.model.Applet +import org.yuzu.yuzu_emu.model.AppletInfo +import org.yuzu.yuzu_emu.model.Game + +class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) : + RecyclerView.Adapter<AppletAdapter.AppletViewHolder>(), + View.OnClickListener { + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): AppletAdapter.AppletViewHolder { + CardAppletOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .apply { root.setOnClickListener(this@AppletAdapter) } + .also { return AppletViewHolder(it) } + } + + override fun onBindViewHolder(holder: AppletViewHolder, position: Int) = + holder.bind(applets[position]) + + override fun getItemCount(): Int = applets.size + + override fun onClick(view: View) { + val applet = (view.tag as AppletViewHolder).applet + val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId) + if (appletPath.isEmpty()) { + Toast.makeText( + YuzuApplication.appContext, + R.string.applets_error_applet, + Toast.LENGTH_SHORT + ).show() + return + } + + if (applet.appletInfo == AppletInfo.Cabinet) { + view.findNavController() + .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment) + return + } + + NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId) + val appletGame = Game( + title = YuzuApplication.appContext.getString(applet.titleId), + path = appletPath + ) + val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) + view.findNavController().navigate(action) + } + + inner class AppletViewHolder(val binding: CardAppletOptionBinding) : + RecyclerView.ViewHolder(binding.root) { + lateinit var applet: Applet + + init { + itemView.tag = this + } + + fun bind(applet: Applet) { + this.applet = applet + + binding.title.setText(applet.titleId) + binding.description.setText(applet.descriptionId) + binding.icon.setImageDrawable( + ResourcesCompat.getDrawable( + binding.icon.context.resources, + applet.iconId, + binding.icon.context.theme + ) + ) + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt new file mode 100644 index 000000000..e7b7c0f2f --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.adapters + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.res.ResourcesCompat +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.RecyclerView +import org.yuzu.yuzu_emu.HomeNavigationDirections +import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.databinding.DialogListItemBinding +import org.yuzu.yuzu_emu.model.CabinetMode +import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder +import org.yuzu.yuzu_emu.model.AppletInfo +import org.yuzu.yuzu_emu.model.Game + +class CabinetLauncherDialogAdapter(val fragment: Fragment) : + RecyclerView.Adapter<CabinetModeViewHolder>(), + View.OnClickListener { + private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder { + DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) } + .also { return CabinetModeViewHolder(it) } + } + + override fun getItemCount(): Int = cabinetModes.size + + override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) = + holder.bind(cabinetModes[position]) + + override fun onClick(view: View) { + val mode = (view.tag as CabinetModeViewHolder).cabinetMode + val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId) + NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId) + NativeLibrary.setCabinetMode(mode.id) + val appletGame = Game( + title = YuzuApplication.appContext.getString(R.string.cabinet_applet), + path = appletPath + ) + val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) + fragment.findNavController().navigate(action) + } + + inner class CabinetModeViewHolder(val binding: DialogListItemBinding) : + RecyclerView.ViewHolder(binding.root) { + lateinit var cabinetMode: CabinetMode + + init { + itemView.tag = this + } + + fun bind(cabinetMode: CabinetMode) { + this.cabinetMode = cabinetMode + binding.icon.setImageDrawable( + ResourcesCompat.getDrawable( + binding.icon.context.resources, + cabinetMode.iconId, + binding.icon.context.theme + ) + ) + binding.title.setText(cabinetMode.titleId) + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index f9f88a1d2..0c82cdba8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -147,7 +147,7 @@ class GameAdapter(private val activity: AppCompatActivity) : private class DiffCallback : DiffUtil.ItemCallback<Game>() { override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean { - return oldItem.gameId == newItem.gameId + return oldItem.programId == newItem.programId } override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt new file mode 100644 index 000000000..1f66b440d --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +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.navigation.findNavController +import androidx.recyclerview.widget.GridLayoutManager +import com.google.android.material.transition.MaterialSharedAxis +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.adapters.AppletAdapter +import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding +import org.yuzu.yuzu_emu.model.Applet +import org.yuzu.yuzu_emu.model.AppletInfo +import org.yuzu.yuzu_emu.model.HomeViewModel + +class AppletLauncherFragment : Fragment() { + private var _binding: FragmentAppletLauncherBinding? = null + private val binding get() = _binding!! + + private val homeViewModel: HomeViewModel by activityViewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) + returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentAppletLauncherBinding.inflate(inflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + homeViewModel.setNavigationVisibility(visible = false, animated = true) + homeViewModel.setStatusBarShadeVisibility(visible = false) + + binding.toolbarApplets.setNavigationOnClickListener { + binding.root.findNavController().popBackStack() + } + + val applets = listOf( + Applet( + R.string.album_applet, + R.string.album_applet_description, + R.drawable.ic_album, + AppletInfo.PhotoViewer + ), + Applet( + R.string.cabinet_applet, + R.string.cabinet_applet_description, + R.drawable.ic_nfc, + AppletInfo.Cabinet + ), + Applet( + R.string.mii_edit_applet, + R.string.mii_edit_applet_description, + R.drawable.ic_mii, + AppletInfo.MiiEdit + ) + ) + + binding.listApplets.apply { + layoutManager = GridLayoutManager( + requireContext(), + resources.getInteger(R.integer.grid_columns) + ) + adapter = AppletAdapter(requireActivity(), applets) + } + + setInsets() + } + + private fun setInsets() = + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { _: View, windowInsets: WindowInsetsCompat -> + val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) + + val leftInsets = barInsets.left + cutoutInsets.left + val rightInsets = barInsets.right + cutoutInsets.right + + val mlpAppBar = binding.toolbarApplets.layoutParams as ViewGroup.MarginLayoutParams + mlpAppBar.leftMargin = leftInsets + mlpAppBar.rightMargin = rightInsets + binding.toolbarApplets.layoutParams = mlpAppBar + + val mlpListApplets = + binding.listApplets.layoutParams as ViewGroup.MarginLayoutParams + mlpListApplets.leftMargin = leftInsets + mlpListApplets.rightMargin = rightInsets + binding.listApplets.layoutParams = mlpListApplets + + binding.listApplets.updatePadding(bottom = barInsets.bottom) + + windowInsets + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CabinetLauncherDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CabinetLauncherDialogFragment.kt new file mode 100644 index 000000000..5933677fd --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CabinetLauncherDialogFragment.kt @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.fragments + +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter +import org.yuzu.yuzu_emu.databinding.DialogListBinding + +class CabinetLauncherDialogFragment : DialogFragment() { + private lateinit var binding: DialogListBinding + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogListBinding.inflate(layoutInflater) + binding.dialogList.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = CabinetLauncherDialogAdapter(this@CabinetLauncherDialogFragment) + } + + return MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.cabinet_launcher) + .setView(binding.root) + .create() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return binding.root + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 598a9d42b..c32fa0d7e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -10,11 +10,11 @@ import android.content.DialogInterface import android.content.SharedPreferences import android.content.pm.ActivityInfo import android.content.res.Configuration -import android.graphics.Color import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Looper +import android.os.SystemClock import android.view.* import android.widget.TextView import android.widget.Toast @@ -25,6 +25,7 @@ import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.drawerlayout.widget.DrawerLayout +import androidx.drawerlayout.widget.DrawerLayout.DrawerListener import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle @@ -153,9 +154,34 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } binding.surfaceEmulation.holder.addCallback(this) - binding.showFpsText.setTextColor(Color.YELLOW) binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } + binding.drawerLayout.addDrawerListener(object : DrawerListener { + override fun onDrawerSlide(drawerView: View, slideOffset: Float) { + binding.surfaceInputOverlay.dispatchTouchEvent( + MotionEvent.obtain( + SystemClock.uptimeMillis(), + SystemClock.uptimeMillis() + 100, + MotionEvent.ACTION_UP, + 0f, + 0f, + 0 + ) + ) + } + + override fun onDrawerOpened(drawerView: View) { + // No op + } + + override fun onDrawerClosed(drawerView: View) { + // No op + } + + override fun onDrawerStateChanged(newState: Int) { + // No op + } + }) binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = game.title @@ -284,6 +310,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { ViewUtils.showView(binding.surfaceInputOverlay) ViewUtils.hideView(binding.loadingIndicator) + emulationState.updateSurface() + // Setup overlay updateShowFpsOverlay() } @@ -384,12 +412,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { val FRAMETIME = 2 val SPEED = 3 perfStatsUpdater = { - if (emulationViewModel.emulationStarted.value == true) { + if (emulationViewModel.emulationStarted.value) { val perfStats = NativeLibrary.getPerfStats() - if (perfStats[FPS] > 0 && _binding != null) { + if (_binding != null) { binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) } - perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) + perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) } } perfStatsUpdateHandler.post(perfStatsUpdater!!) @@ -434,7 +462,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { // Restrict emulation and overlays to the top of the screen binding.emulationContainer.layoutParams.height = it.bounds.top - binding.overlayContainer.layoutParams.height = it.bounds.top // Restrict input and menu drawer to the bottom of the screen binding.inputContainer.layoutParams.height = it.bounds.bottom binding.inGameMenu.layoutParams.height = it.bounds.bottom @@ -448,7 +475,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { if (!isFolding) { binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT - binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT isInFoldableLayout = false updateOrientation() @@ -456,7 +482,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } binding.emulationContainer.requestLayout() binding.inputContainer.requestLayout() - binding.overlayContainer.requestLayout() binding.inGameMenu.requestLayout() } @@ -682,24 +707,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } v.setPadding(left, cutInsets.top, right, 0) - - // Ensure FPS text doesn't get cut off by rounded display corners - val sidePadding = resources.getDimensionPixelSize(R.dimen.spacing_xtralarge) - if (cutInsets.left == 0) { - binding.showFpsText.setPadding( - sidePadding, - cutInsets.top, - cutInsets.right, - cutInsets.bottom - ) - } else { - binding.showFpsText.setPadding( - cutInsets.left, - cutInsets.top, - cutInsets.right, - cutInsets.bottom - ) - } windowInsets } } @@ -777,6 +784,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } @Synchronized + fun updateSurface() { + if (surface != null) { + NativeLibrary.surfaceChanged(surface) + } + } + + @Synchronized fun clearSurface() { if (surface == null) { Log.warning("[EmulationFragment] clearSurface called, but surface already null.") 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 fd9785075..4720daec4 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 @@ -26,10 +26,11 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.findNavController import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialSharedAxis import org.yuzu.yuzu_emu.BuildConfig import org.yuzu.yuzu_emu.HomeNavigationDirections +import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding @@ -41,6 +42,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.GpuDriverHelper +import org.yuzu.yuzu_emu.utils.Log class HomeSettingsFragment : Fragment() { private var _binding: FragmentHomeSettingsBinding? = null @@ -85,28 +87,6 @@ class HomeSettingsFragment : Fragment() { ) add( HomeSetting( - R.string.open_user_folder, - R.string.open_user_folder_description, - R.drawable.ic_folder_open, - { openFileManager() } - ) - ) - add( - HomeSetting( - R.string.preferences_theme, - R.string.theme_and_color_description, - R.drawable.ic_palette, - { - val action = HomeNavigationDirections.actionGlobalSettingsActivity( - null, - Settings.MenuTag.SECTION_THEME - ) - binding.root.findNavController().navigate(action) - } - ) - ) - add( - HomeSetting( R.string.gpu_driver_manager, R.string.install_gpu_driver_description, R.drawable.ic_build, @@ -122,6 +102,20 @@ class HomeSettingsFragment : Fragment() { ) add( HomeSetting( + R.string.applets, + R.string.applets_description, + R.drawable.ic_applet, + { + binding.root.findNavController() + .navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment) + }, + { NativeLibrary.isFirmwareAvailable() }, + R.string.applets_error_firmware, + R.string.applets_error_description + ) + ) + add( + HomeSetting( R.string.manage_yuzu_data, R.string.manage_yuzu_data_description, R.drawable.ic_install, @@ -157,6 +151,28 @@ class HomeSettingsFragment : Fragment() { ) add( HomeSetting( + R.string.open_user_folder, + R.string.open_user_folder_description, + R.drawable.ic_folder_open, + { openFileManager() } + ) + ) + add( + HomeSetting( + R.string.preferences_theme, + R.string.theme_and_color_description, + R.drawable.ic_palette, + { + val action = HomeNavigationDirections.actionGlobalSettingsActivity( + null, + Settings.MenuTag.SECTION_THEME + ) + binding.root.findNavController().navigate(action) + } + ) + ) + add( + HomeSetting( R.string.about, R.string.about_description, R.drawable.ic_info_outline, @@ -186,7 +202,8 @@ class HomeSettingsFragment : Fragment() { } binding.homeSettingsList.apply { - layoutManager = LinearLayoutManager(requireContext()) + layoutManager = + GridLayoutManager(requireContext(), resources.getInteger(R.integer.grid_columns)) adapter = HomeSettingAdapter( requireActivity() as AppCompatActivity, viewLifecycleOwner, @@ -296,19 +313,32 @@ class HomeSettingsFragment : Fragment() { } } + // Share the current log if we just returned from a game but share the old log + // if we just started the app and the old log exists. private fun shareLog() { - val file = DocumentFile.fromSingleUri( + val currentLog = DocumentFile.fromSingleUri( mainActivity, DocumentsContract.buildDocumentUri( DocumentProvider.AUTHORITY, "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt" ) )!! - if (file.exists()) { - val intent = Intent(Intent.ACTION_SEND) - .setDataAndType(file.uri, FileUtil.TEXT_PLAIN) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .putExtra(Intent.EXTRA_STREAM, file.uri) + val oldLog = DocumentFile.fromSingleUri( + mainActivity, + DocumentsContract.buildDocumentUri( + DocumentProvider.AUTHORITY, + "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt.old.txt" + ) + )!! + + val intent = Intent(Intent.ACTION_SEND) + .setDataAndType(currentLog.uri, FileUtil.TEXT_PLAIN) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + if (!Log.gameLaunched && oldLog.exists()) { + intent.putExtra(Intent.EXTRA_STREAM, oldLog.uri) + startActivity(Intent.createChooser(intent, getText(R.string.share_log))) + } else if (currentLog.exists()) { + intent.putExtra(Intent.EXTRA_STREAM, currentLog.uri) startActivity(Intent.createChooser(intent, getText(R.string.share_log))) } else { Toast.makeText( 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 541b22f47..a6183d19e 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 @@ -8,6 +8,7 @@ import android.content.DialogInterface import android.content.Intent import android.net.Uri import android.os.Bundle +import android.text.Html import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentActivity import androidx.fragment.app.activityViewModels @@ -32,7 +33,9 @@ class MessageDialogFragment : DialogFragment() { if (titleId != 0) dialog.setTitle(titleId) if (titleString.isNotEmpty()) dialog.setTitle(titleString) - if (descriptionId != 0) dialog.setMessage(descriptionId) + if (descriptionId != 0) { + dialog.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY)) + } if (descriptionString.isNotEmpty()) dialog.setMessage(descriptionString) if (helpLinkId != 0) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Applet.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Applet.kt new file mode 100644 index 000000000..8677674a3 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Applet.kt @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.model + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import org.yuzu.yuzu_emu.R + +data class Applet( + @StringRes val titleId: Int, + @StringRes val descriptionId: Int, + @DrawableRes val iconId: Int, + val appletInfo: AppletInfo, + val cabinetMode: CabinetMode = CabinetMode.None +) + +// Combination of Common::AM::Applets::AppletId enum and the entry id +enum class AppletInfo(val appletId: Int, val entryId: Long = 0) { + None(0x00), + Application(0x01), + OverlayDisplay(0x02), + QLaunch(0x03), + Starter(0x04), + Auth(0x0A), + Cabinet(0x0B, 0x0100000000001002), + Controller(0x0C), + DataErase(0x0D), + Error(0x0E), + NetConnect(0x0F), + ProfileSelect(0x10), + SoftwareKeyboard(0x11), + MiiEdit(0x12, 0x0100000000001009), + Web(0x13), + Shop(0x14), + PhotoViewer(0x015, 0x010000000000100D), + Settings(0x16), + OfflineWeb(0x17), + LoginShare(0x18), + WebAuth(0x19), + MyPage(0x1A) +} + +// Matches enum in Service::NFP::CabinetMode with extra metadata +enum class CabinetMode( + val id: Int, + @StringRes val titleId: Int = 0, + @DrawableRes val iconId: Int = 0 +) { + None(-1), + StartNicknameAndOwnerSettings(0, R.string.cabinet_nickname_and_owner, R.drawable.ic_edit), + StartGameDataEraser(1, R.string.cabinet_game_data_eraser, R.drawable.ic_refresh), + StartRestorer(2, R.string.cabinet_restorer, R.drawable.ic_restore), + StartFormatter(3, R.string.cabinet_formatter, R.drawable.ic_clear) +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index 6527c64ab..de84b2adb 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt @@ -11,16 +11,15 @@ import kotlinx.serialization.Serializable @Parcelize @Serializable class Game( - val title: String, - val description: String, - val regions: String, + val title: String = "", val path: String, - val gameId: String, - val company: String, - val isHomebrew: Boolean + val programId: String = "", + val developer: String = "", + val version: String = "", + val isHomebrew: Boolean = false ) : Parcelable { - val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime" - val keyLastPlayedTime get() = "${gameId}_LastPlayed" + val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime" + val keyLastPlayedTime get() = "${programId}_LastPlayed" override fun equals(other: Any?): Boolean { if (other !is Game) { @@ -32,11 +31,9 @@ class Game( override fun hashCode(): Int { var result = title.hashCode() - result = 31 * result + description.hashCode() - result = 31 * result + regions.hashCode() result = 31 * result + path.hashCode() - result = 31 * result + gameId.hashCode() - result = 31 * result + company.hashCode() + result = 31 * result + programId.hashCode() + result = 31 * result + developer.hashCode() result = 31 * result + isHomebrew.hashCode() return result } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt index 6e09fa81d..8512ed17c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt @@ -14,15 +14,13 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.MissingFieldException import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.utils.GameHelper +import org.yuzu.yuzu_emu.utils.GameMetadata -@OptIn(ExperimentalSerializationApi::class) class GamesViewModel : ViewModel() { val games: StateFlow<List<Game>> get() = _games private val _games = MutableStateFlow(emptyList<Game>()) @@ -49,26 +47,34 @@ class GamesViewModel : ViewModel() { // Retrieve list of cached games val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) .getStringSet(GameHelper.KEY_GAMES, emptySet()) - if (storedGames!!.isNotEmpty()) { - val deserializedGames = mutableSetOf<Game>() - storedGames.forEach { - val game: Game - try { - game = Json.decodeFromString(it) - } catch (e: MissingFieldException) { - return@forEach - } - val gameExists = - DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path)) - ?.exists() - if (gameExists == true) { - deserializedGames.add(game) + viewModelScope.launch { + withContext(Dispatchers.IO) { + if (storedGames!!.isNotEmpty()) { + val deserializedGames = mutableSetOf<Game>() + storedGames.forEach { + val game: Game + try { + game = Json.decodeFromString(it) + } catch (e: Exception) { + // We don't care about any errors related to parsing the game cache + return@forEach + } + + val gameExists = + DocumentFile.fromSingleUri( + YuzuApplication.appContext, + Uri.parse(game.path) + )?.exists() + if (gameExists == true) { + deserializedGames.add(game) + } + } + setGames(deserializedGames.toList()) } + reloadGames(false) } - setGames(deserializedGames.toList()) } - reloadGames(false) } fun setGames(games: List<Game>) { @@ -106,7 +112,7 @@ class GamesViewModel : ViewModel() { viewModelScope.launch { withContext(Dispatchers.IO) { - NativeLibrary.resetRomMetadata() + GameMetadata.resetMetadata() setGames(GameHelper.getGames()) _isReloading.value = false diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index 233aa4101..211b7cf69 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -403,6 +403,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } else { firmwarePath.deleteRecursively() cacheFirmwareDir.copyRecursively(firmwarePath, true) + NativeLibrary.initializeSystem(true) getString(R.string.save_file_imported_success) } } catch (e: Exception) { @@ -648,7 +649,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } // Reinitialize relevant data - NativeLibrary.initializeEmulation() + NativeLibrary.initializeSystem(true) gamesViewModel.reloadGames(false) return@newInstance getString(R.string.user_data_import_success) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt deleted file mode 100644 index eeefcdf20..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.yuzu.yuzu_emu.utils - -import android.view.InputDevice -import android.view.KeyEvent -import android.view.MotionEvent - -/** - * Some controllers have incorrect mappings. This class has special-case fixes for them. - */ -class ControllerMappingHelper { - /** - * Some controllers report extra button presses that can be ignored. - */ - fun shouldKeyBeIgnored(inputDevice: InputDevice, keyCode: Int): Boolean { - return if (isDualShock4(inputDevice)) { - // The two analog triggers generate analog motion events as well as a keycode. - // We always prefer to use the analog values, so throw away the button press - keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2 - } else { - false - } - } - - /** - * Scale an axis to be zero-centered with a proper range. - */ - fun scaleAxis(inputDevice: InputDevice, axis: Int, value: Float): Float { - if (isDualShock4(inputDevice)) { - // Android doesn't have correct mappings for this controller's triggers. It reports them - // as RX & RY, centered at -1.0, and with a range of [-1.0, 1.0] - // Scale them to properly zero-centered with a range of [0.0, 1.0]. - if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY) { - return (value + 1) / 2.0f - } - } else if (isXboxOneWireless(inputDevice)) { - // Same as the DualShock 4, the mappings are missing. - if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ) { - return (value + 1) / 2.0f - } - if (axis == MotionEvent.AXIS_GENERIC_1) { - // This axis is stuck at ~.5. Ignore it. - return 0.0f - } - } else if (isMogaPro2Hid(inputDevice)) { - // This controller has a broken axis that reports a constant value. Ignore it. - if (axis == MotionEvent.AXIS_GENERIC_1) { - return 0.0f - } - } - return value - } - - // Sony DualShock 4 controller - private fun isDualShock4(inputDevice: InputDevice): Boolean { - return inputDevice.vendorId == 0x54c && inputDevice.productId == 0x9cc - } - - // Microsoft Xbox One controller - private fun isXboxOneWireless(inputDevice: InputDevice): Boolean { - return inputDevice.vendorId == 0x45e && inputDevice.productId == 0x2e0 - } - - // Moga Pro 2 HID - private fun isMogaPro2Hid(inputDevice: InputDevice): Boolean { - return inputDevice.vendorId == 0x20d6 && inputDevice.productId == 0x6271 - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt index 3c9f6bad0..5e9a1176a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt @@ -15,7 +15,7 @@ object DirectoryInitialization { fun start() { if (!areDirectoriesReady) { initializeInternalStorage() - NativeLibrary.initializeEmulation() + NativeLibrary.initializeSystem(false) areDirectoriesReady = true } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt index eafcf9e42..738275297 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt @@ -42,6 +42,23 @@ class DocumentsTree { return node != null && node.isDirectory } + fun getParentDirectory(filepath: String): String { + val node = resolvePath(filepath)!! + val parentNode = node.parent + if (parentNode != null && parentNode.isDirectory) { + return parentNode.uri!!.toString() + } + return node.uri!!.toString() + } + + fun getFilename(filepath: String): String { + val node = resolvePath(filepath) + if (node != null) { + return node.name!! + } + return filepath + } + private fun resolvePath(filepath: String): DocumentsNode? { val tokens = StringTokenizer(filepath, File.separator, false) var iterator = root diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt index 5ee74a52c..8c3268e9c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt @@ -144,7 +144,7 @@ object FileUtil { * @param path Native content uri path * @return bool */ - fun exists(path: String?): Boolean { + fun exists(path: String?, suppressLog: Boolean = false): Boolean { var c: Cursor? = null try { val mUri = Uri.parse(path) @@ -152,7 +152,9 @@ object FileUtil { c = context.contentResolver.query(mUri, columns, null, null, null) return c!!.count > 0 } catch (e: Exception) { - Log.info("[FileUtil] Cannot find file from given path, error: " + e.message) + if (!suppressLog) { + Log.info("[FileUtil] Cannot find file from given path, error: " + e.message) + } } finally { closeQuietly(c) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index 9001ca9ab..e6aca6b44 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt @@ -71,27 +71,26 @@ object GameHelper { fun getGame(uri: Uri, addedToLibrary: Boolean): Game { val filePath = uri.toString() - var name = NativeLibrary.getTitle(filePath) + var name = GameMetadata.getTitle(filePath) // If the game's title field is empty, use the filename. if (name.isEmpty()) { name = FileUtil.getFilename(uri) } - var gameId = NativeLibrary.getGameId(filePath) + var programId = GameMetadata.getProgramId(filePath) // If the game's ID field is empty, use the filename without extension. - if (gameId.isEmpty()) { - gameId = name.substring(0, name.lastIndexOf(".")) + if (programId.isEmpty()) { + programId = name.substring(0, name.lastIndexOf(".")) } val newGame = Game( name, - NativeLibrary.getDescription(filePath).replace("\n", " "), - NativeLibrary.getRegions(filePath), filePath, - gameId, - NativeLibrary.getCompany(filePath), - NativeLibrary.isHomebrew(filePath) + programId, + GameMetadata.getDeveloper(filePath), + GameMetadata.getVersion(filePath), + GameMetadata.getIsHomebrew(filePath) ) if (addedToLibrary) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt index 9fe99fab1..654d62f52 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt @@ -18,7 +18,6 @@ import coil.key.Keyer import coil.memory.MemoryCache import coil.request.ImageRequest import coil.request.Options -import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.model.Game @@ -36,7 +35,7 @@ class GameIconFetcher( } private fun decodeGameIcon(uri: String): Bitmap? { - val data = NativeLibrary.getIcon(uri) + val data = GameMetadata.getIcon(uri) return BitmapFactory.decodeByteArray( data, 0, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt new file mode 100644 index 000000000..0f3542ac6 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.utils + +object GameMetadata { + external fun getTitle(path: String): String + + external fun getProgramId(path: String): String + + external fun getDeveloper(path: String): String + + external fun getVersion(path: String): String + + external fun getIcon(path: String): ByteArray + + external fun getIsHomebrew(path: String): Boolean + + external fun resetMetadata() +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt index e963dfbc1..47bde5081 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt @@ -3,17 +3,24 @@ package org.yuzu.yuzu_emu.utils +import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent import kotlin.math.sqrt import org.yuzu.yuzu_emu.NativeLibrary -class InputHandler { +object InputHandler { + private var controllerIds = getGameControllerIds() + fun initialize() { // Connect first controller NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device)) } + fun updateControllerIds() { + controllerIds = getGameControllerIds() + } + fun dispatchKeyEvent(event: KeyEvent): Boolean { val button: Int = when (event.device.vendorId) { 0x045E -> getInputXboxButtonKey(event.keyCode) @@ -35,7 +42,7 @@ class InputHandler { } return NativeLibrary.onGamePadButtonEvent( - getPlayerNumber(event.device.controllerNumber), + getPlayerNumber(event.device.controllerNumber, event.deviceId), button, action ) @@ -58,9 +65,14 @@ class InputHandler { return true } - private fun getPlayerNumber(index: Int): Int { + private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int { + var deviceIndex = index + if (deviceId != -1) { + deviceIndex = controllerIds[deviceId] ?: 0 + } + // TODO: Joycons are handled as different controllers. Find a way to merge them. - return when (index) { + return when (deviceIndex) { 2 -> NativeLibrary.Player2Device 3 -> NativeLibrary.Player3Device 4 -> NativeLibrary.Player4Device @@ -238,7 +250,7 @@ class InputHandler { } private fun setGenericAxisInput(event: MotionEvent, axis: Int) { - val playerNumber = getPlayerNumber(event.device.controllerNumber) + val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) when (axis) { MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> @@ -297,7 +309,7 @@ class InputHandler { private fun setJoyconAxisInput(event: MotionEvent, axis: Int) { // Joycon support is half dead. Right joystick doesn't work - val playerNumber = getPlayerNumber(event.device.controllerNumber) + val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) when (axis) { MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> @@ -325,7 +337,7 @@ class InputHandler { } private fun setRazerAxisInput(event: MotionEvent, axis: Int) { - val playerNumber = getPlayerNumber(event.device.controllerNumber) + val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) when (axis) { MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> @@ -362,4 +374,33 @@ class InputHandler { ) } } + + fun getGameControllerIds(): Map<Int, Int> { + val gameControllerDeviceIds = mutableMapOf<Int, Int>() + val deviceIds = InputDevice.getDeviceIds() + var controllerSlot = 1 + deviceIds.forEach { deviceId -> + InputDevice.getDevice(deviceId)?.apply { + // Don't over-assign controllers + if (controllerSlot >= 8) { + return gameControllerDeviceIds + } + + // Verify that the device has gamepad buttons, control sticks, or both. + if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD || + sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK + ) { + // This device is a game controller. Store its device ID. + if (deviceId and id and vendorId and productId != 0) { + // Additionally filter out devices that have no ID + gameControllerDeviceIds + .takeIf { !it.contains(deviceId) } + ?.put(deviceId, controllerSlot) + controllerSlot++ + } + } + } + } + return gameControllerDeviceIds + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt index a193e82a4..aebe84b0f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt @@ -3,38 +3,29 @@ package org.yuzu.yuzu_emu.utils -import android.util.Log -import org.yuzu.yuzu_emu.BuildConfig - -/** - * Contains methods that call through to [android.util.Log], but - * with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log - * levels in release builds. - */ +import android.os.Build + object Log { - private const val TAG = "Yuzu Frontend" + // Tracks whether we should share the old log or the current log + var gameLaunched = false - fun verbose(message: String) { - if (BuildConfig.DEBUG) { - Log.v(TAG, message) - } - } + external fun debug(message: String) - fun debug(message: String) { - if (BuildConfig.DEBUG) { - Log.d(TAG, message) - } - } + external fun warning(message: String) - fun info(message: String) { - Log.i(TAG, message) - } + external fun info(message: String) - fun warning(message: String) { - Log.w(TAG, message) - } + external fun error(message: String) - fun error(message: String) { - Log.e(TAG, message) + external fun critical(message: String) + + fun logDeviceInfo() { + info("Device Manufacturer - ${Build.MANUFACTURER}") + info("Device Model - ${Build.MODEL}") + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { + info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}") + info("SoC Model - ${Build.SOC_MODEL}") + } + info("Total System Memory - ${MemoryUtil.getDeviceRAM()}") } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt index aa4a5539a..9076a86c4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt @@ -27,7 +27,7 @@ object MemoryUtil { const val Pb = Tb * 1024 const val Eb = Pb * 1024 - private fun bytesToSizeUnit(size: Float): String = + private fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String = when { size < Kb -> { context.getString( @@ -39,63 +39,59 @@ object MemoryUtil { size < Mb -> { context.getString( R.string.memory_formatted, - (size / Kb).hundredths, + if (roundUp) ceil(size / Kb) else (size / Kb).hundredths, context.getString(R.string.memory_kilobyte) ) } size < Gb -> { context.getString( R.string.memory_formatted, - (size / Mb).hundredths, + if (roundUp) ceil(size / Mb) else (size / Mb).hundredths, context.getString(R.string.memory_megabyte) ) } size < Tb -> { context.getString( R.string.memory_formatted, - (size / Gb).hundredths, + if (roundUp) ceil(size / Gb) else (size / Gb).hundredths, context.getString(R.string.memory_gigabyte) ) } size < Pb -> { context.getString( R.string.memory_formatted, - (size / Tb).hundredths, + if (roundUp) ceil(size / Tb) else (size / Tb).hundredths, context.getString(R.string.memory_terabyte) ) } size < Eb -> { context.getString( R.string.memory_formatted, - (size / Pb).hundredths, + if (roundUp) ceil(size / Pb) else (size / Pb).hundredths, context.getString(R.string.memory_petabyte) ) } else -> { context.getString( R.string.memory_formatted, - (size / Eb).hundredths, + if (roundUp) ceil(size / Eb) else (size / Eb).hundredths, context.getString(R.string.memory_exabyte) ) } } - // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for - // the potential error created by memInfo.totalMem - private val totalMemory: Float + val totalMemory: Float get() { val memInfo = ActivityManager.MemoryInfo() with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { getMemoryInfo(memInfo) } - return ceil( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - memInfo.advertisedMem.toFloat() - } else { - memInfo.totalMem.toFloat() - } - ) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + memInfo.advertisedMem.toFloat() + } else { + memInfo.totalMem.toFloat() + } } fun isLessThan(minimum: Int, size: Float): Boolean = @@ -109,5 +105,7 @@ object MemoryUtil { else -> totalMemory < Kb && totalMemory < minimum } - fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory) + // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for + // the potential error created by memInfo.totalMem + fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true) } diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index e15d1480b..88a570f68 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -14,8 +14,11 @@ add_library(yuzu-android SHARED id_cache.cpp id_cache.h native.cpp + native.h native_config.cpp uisettings.cpp + game_metadata.cpp + native_log.cpp ) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) diff --git a/src/android/app/src/main/jni/game_metadata.cpp b/src/android/app/src/main/jni/game_metadata.cpp new file mode 100644 index 000000000..24d9df702 --- /dev/null +++ b/src/android/app/src/main/jni/game_metadata.cpp @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <core/core.h> +#include <core/file_sys/patch_manager.h> +#include <core/loader/nro.h> +#include <jni.h> +#include "core/loader/loader.h" +#include "jni/android_common/android_common.h" +#include "native.h" + +struct RomMetadata { + std::string title; + u64 programId; + std::string developer; + std::string version; + std::vector<u8> icon; + bool isHomebrew; +}; + +std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache; + +RomMetadata CacheRomMetadata(const std::string& path) { + const auto file = + Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path); + auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); + + RomMetadata entry; + loader->ReadTitle(entry.title); + loader->ReadProgramId(entry.programId); + loader->ReadIcon(entry.icon); + + const FileSys::PatchManager pm{ + entry.programId, EmulationSession::GetInstance().System().GetFileSystemController(), + EmulationSession::GetInstance().System().GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + + if (control.first != nullptr) { + entry.developer = control.first->GetDeveloperName(); + entry.version = control.first->GetVersionString(); + } else { + FileSys::NACP nacp; + if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) { + entry.developer = nacp.GetDeveloperName(); + } else { + entry.developer = ""; + } + + entry.version = "1.0.0"; + } + + if (loader->GetFileType() == Loader::FileType::NRO) { + auto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get()); + entry.isHomebrew = loader_nro->IsHomebrew(); + } else { + entry.isHomebrew = false; + } + + m_rom_metadata_cache[path] = entry; + + return entry; +} + +RomMetadata GetRomMetadata(const std::string& path) { + if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { + return search->second; + } + + return CacheRomMetadata(path); +} + +extern "C" { + +jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj, + jstring jpath) { + return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title); +} + +jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj, + jstring jpath) { + return ToJString(env, std::to_string(GetRomMetadata(GetJString(env, jpath)).programId)); +} + +jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj, + jstring jpath) { + return ToJString(env, GetRomMetadata(GetJString(env, jpath)).developer); +} + +jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj, + jstring jpath) { + return ToJString(env, GetRomMetadata(GetJString(env, jpath)).version); +} + +jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj, + jstring jpath) { + auto icon_data = GetRomMetadata(GetJString(env, jpath)).icon; + jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); + env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), + reinterpret_cast<jbyte*>(icon_data.data())); + return icon; +} + +jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj, + jstring jpath) { + return static_cast<jboolean>(GetRomMetadata(GetJString(env, jpath)).isHomebrew); +} + +void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) { + return m_rom_metadata_cache.clear(); +} + +} // extern "C" diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 598f4e8bf..1484cc224 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -33,7 +33,6 @@ #include "core/crypto/key_manager.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" -#include "core/file_sys/registered_cache.h" #include "core/file_sys/submission_package.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" @@ -48,520 +47,423 @@ #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" #include "core/hid/hid_types.h" -#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" -#include "core/perf_stats.h" #include "jni/android_common/android_common.h" -#include "jni/applets/software_keyboard.h" #include "jni/config.h" -#include "jni/emu_window/emu_window.h" #include "jni/id_cache.h" -#include "video_core/rasterizer_interface.h" +#include "jni/native.h" #include "video_core/renderer_base.h" #define jconst [[maybe_unused]] const auto #define jauto [[maybe_unused]] auto -namespace { +static EmulationSession s_instance; -class EmulationSession final { -public: - EmulationSession() { - m_vfs = std::make_shared<FileSys::RealVfsFilesystem>(); - } - - ~EmulationSession() = default; - - static EmulationSession& GetInstance() { - return s_instance; - } - - const Core::System& System() const { - return m_system; - } +EmulationSession::EmulationSession() { + m_vfs = std::make_shared<FileSys::RealVfsFilesystem>(); +} - Core::System& System() { - return m_system; - } +EmulationSession& EmulationSession::GetInstance() { + return s_instance; +} - const EmuWindow_Android& Window() const { - return *m_window; - } +const Core::System& EmulationSession::System() const { + return m_system; +} - EmuWindow_Android& Window() { - return *m_window; - } +Core::System& EmulationSession::System() { + return m_system; +} - ANativeWindow* NativeWindow() const { - return m_native_window; - } +const EmuWindow_Android& EmulationSession::Window() const { + return *m_window; +} - void SetNativeWindow(ANativeWindow* native_window) { - m_native_window = native_window; - } +EmuWindow_Android& EmulationSession::Window() { + return *m_window; +} - int InstallFileToNand(std::string filename, std::string file_extension) { - jconst copy_func = [](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; - } +ANativeWindow* EmulationSession::NativeWindow() const { + return m_native_window; +} - using namespace Common::Literals; - [[maybe_unused]] std::vector<u8> buffer(1_MiB); +void EmulationSession::SetNativeWindow(ANativeWindow* native_window) { + m_native_window = native_window; +} - for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { - jconst read = src->Read(buffer.data(), buffer.size(), i); - dest->Write(buffer.data(), read, i); - } - return true; - }; - - enum InstallResult { - Success = 0, - SuccessFileOverwritten = 1, - InstallError = 2, - ErrorBaseGame = 3, - ErrorFilenameExtension = 4, - }; - - m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); - m_system.GetFileSystemController().CreateFactories(*m_vfs); - - [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; - if (file_extension == "nsp") { - nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); - if (nsp->IsExtractedType()) { - return InstallError; - } - } else { - return ErrorFilenameExtension; +int EmulationSession::InstallFileToNand(std::string filename, std::string file_extension) { + jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, + std::size_t block_size) { + if (src == nullptr || dest == nullptr) { + return false; } - - if (!nsp) { - return InstallError; + if (!dest->Resize(src->GetSize())) { + return false; } - if (nsp->GetStatus() != Loader::ResultStatus::Success) { - return InstallError; - } + using namespace Common::Literals; + [[maybe_unused]] std::vector<u8> buffer(1_MiB); - jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry( - *nsp, true, copy_func); - - switch (res) { - case FileSys::InstallResult::Success: - return Success; - case FileSys::InstallResult::OverwriteExisting: - return SuccessFileOverwritten; - case FileSys::InstallResult::ErrorBaseInstall: - return ErrorBaseGame; - default: - return InstallError; + for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { + jconst read = src->Read(buffer.data(), buffer.size(), i); + dest->Write(buffer.data(), read, i); } - } + return true; + }; - void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, - const std::string& custom_driver_name, - const std::string& file_redirect_dir) { -#ifdef ARCHITECTURE_arm64 - void* handle{}; - const char* file_redirect_dir_{}; - int featureFlags{}; - - // Enable driver file redirection when renderer debugging is enabled. - if (Settings::values.renderer_debug && file_redirect_dir.size()) { - featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; - file_redirect_dir_ = file_redirect_dir.c_str(); - } + enum InstallResult { + Success = 0, + SuccessFileOverwritten = 1, + InstallError = 2, + ErrorBaseGame = 3, + ErrorFilenameExtension = 4, + }; - // Try to load a custom driver. - if (custom_driver_name.size()) { - handle = adrenotools_open_libvulkan( - RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), - custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); - } + m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); + m_system.GetFileSystemController().CreateFactories(*m_vfs); - // Try to load the system driver. - if (!handle) { - handle = - adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), - nullptr, nullptr, file_redirect_dir_, nullptr); + [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; + if (file_extension == "nsp") { + nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); + if (nsp->IsExtractedType()) { + return InstallError; } - - m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle); -#endif + } else { + return ErrorFilenameExtension; } - bool IsRunning() const { - return m_is_running; + if (!nsp) { + return InstallError; } - bool IsPaused() const { - return m_is_running && m_is_paused; + if (nsp->GetStatus() != Loader::ResultStatus::Success) { + return InstallError; } - const Core::PerfStatsResults& PerfStats() const { - std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); - return m_perf_stats; - } + jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, + copy_func); - void SurfaceChanged() { - if (!IsRunning()) { - return; - } - m_window->OnSurfaceChanged(m_native_window); + switch (res) { + case FileSys::InstallResult::Success: + return Success; + case FileSys::InstallResult::OverwriteExisting: + return SuccessFileOverwritten; + case FileSys::InstallResult::ErrorBaseInstall: + return ErrorBaseGame; + default: + return InstallError; } +} - void ConfigureFilesystemProvider(const std::string& filepath) { - const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read); - if (!file) { - return; - } - - auto loader = Loader::GetLoader(m_system, file); - if (!loader) { - return; - } - - const auto file_type = loader->GetFileType(); - if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { - return; - } +void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir, + const std::string& custom_driver_dir, + const std::string& custom_driver_name, + const std::string& file_redirect_dir) { +#ifdef ARCHITECTURE_arm64 + void* handle{}; + const char* file_redirect_dir_{}; + int featureFlags{}; - u64 program_id = 0; - const auto res2 = loader->ReadProgramId(program_id); - if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { - m_manual_provider->AddEntry(FileSys::TitleType::Application, - FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), - program_id, file); - } else if (res2 == Loader::ResultStatus::Success && - (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) { - const auto nsp = file_type == Loader::FileType::NSP - ? std::make_shared<FileSys::NSP>(file) - : FileSys::XCI{file}.GetSecurePartitionNSP(); - for (const auto& title : nsp->GetNCAs()) { - for (const auto& entry : title.second) { - m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first, - entry.second->GetBaseFile()); - } - } - } + // Enable driver file redirection when renderer debugging is enabled. + if (Settings::values.renderer_debug && file_redirect_dir.size()) { + featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; + file_redirect_dir_ = file_redirect_dir.c_str(); } - Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { - std::scoped_lock lock(m_mutex); - - // Create the render window. - m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, - m_vulkan_library); - - m_system.SetFilesystem(m_vfs); - m_system.GetUserChannel().clear(); - - // Initialize system. - jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); - m_software_keyboard = android_keyboard.get(); - m_system.SetShuttingDown(false); - m_system.ApplySettings(); - Settings::LogSettings(); - m_system.HIDCore().ReloadInputDevices(); - m_system.SetAppletFrontendSet({ - nullptr, // Amiibo Settings - nullptr, // Controller Selector - nullptr, // Error Display - nullptr, // Mii Editor - nullptr, // Parental Controls - nullptr, // Photo Viewer - nullptr, // Profile Selector - std::move(android_keyboard), // Software Keyboard - nullptr, // Web Browser - }); - - // Initialize filesystem. - m_manual_provider = std::make_unique<FileSys::ManualContentProvider>(); - m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); - m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, - m_manual_provider.get()); - m_system.GetFileSystemController().CreateFactories(*m_vfs); - ConfigureFilesystemProvider(filepath); - - // Initialize account manager - m_profile_manager = std::make_unique<Service::Account::ProfileManager>(); - - // Load the ROM. - m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); - if (m_load_result != Core::SystemResultStatus::Success) { - return m_load_result; - } - - // Complete initialization. - m_system.GPU().Start(); - m_system.GetCpuManager().OnGpuReady(); - m_system.RegisterExitCallback([&] { HaltEmulation(); }); + // Try to load a custom driver. + if (custom_driver_name.size()) { + handle = adrenotools_open_libvulkan( + RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), + custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); + } - return Core::SystemResultStatus::Success; + // Try to load the system driver. + if (!handle) { + handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), + nullptr, nullptr, file_redirect_dir_, nullptr); } - void ShutdownEmulation() { - std::scoped_lock lock(m_mutex); + m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle); +#endif +} - m_is_running = false; +bool EmulationSession::IsRunning() const { + return m_is_running; +} - // Unload user input. - m_system.HIDCore().UnloadInputDevices(); +bool EmulationSession::IsPaused() const { + return m_is_running && m_is_paused; +} - // Shutdown the main emulated process - if (m_load_result == Core::SystemResultStatus::Success) { - m_system.DetachDebugger(); - m_system.ShutdownMainProcess(); - m_detached_tasks.WaitForAllTasks(); - m_load_result = Core::SystemResultStatus::ErrorNotInitialized; - m_window.reset(); - OnEmulationStopped(Core::SystemResultStatus::Success); - return; - } +const Core::PerfStatsResults& EmulationSession::PerfStats() { + m_perf_stats = m_system.GetAndResetPerfStats(); + return m_perf_stats; +} - // Tear down the render window. - m_window.reset(); +void EmulationSession::SurfaceChanged() { + if (!IsRunning()) { + return; } + m_window->OnSurfaceChanged(m_native_window); +} - void PauseEmulation() { - std::scoped_lock lock(m_mutex); - m_system.Pause(); - m_is_paused = true; +void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) { + const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read); + if (!file) { + return; } - void UnPauseEmulation() { - std::scoped_lock lock(m_mutex); - m_system.Run(); - m_is_paused = false; + auto loader = Loader::GetLoader(m_system, file); + if (!loader) { + return; } - void HaltEmulation() { - std::scoped_lock lock(m_mutex); - m_is_running = false; - m_cv.notify_one(); + const auto file_type = loader->GetFileType(); + if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { + return; } - void RunEmulation() { - { - std::scoped_lock lock(m_mutex); - m_is_running = true; - } - - // Load the disk shader cache. - if (Settings::values.use_disk_shader_cache.GetValue()) { - LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); - m_system.Renderer().ReadRasterizer()->LoadDiskResources( - m_system.GetApplicationProcessProgramID(), std::stop_token{}, - LoadDiskCacheProgress); - LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); + u64 program_id = 0; + const auto res2 = loader->ReadProgramId(program_id); + if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { + m_manual_provider->AddEntry(FileSys::TitleType::Application, + FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), + program_id, file); + } else if (res2 == Loader::ResultStatus::Success && + (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) { + const auto nsp = file_type == Loader::FileType::NSP + ? std::make_shared<FileSys::NSP>(file) + : FileSys::XCI{file}.GetSecurePartitionNSP(); + for (const auto& title : nsp->GetNCAs()) { + for (const auto& entry : title.second) { + m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first, + entry.second->GetBaseFile()); + } } + } +} - void(m_system.Run()); +void EmulationSession::InitializeSystem(bool reload) { + if (!reload) { + // Initialize logging system + Common::Log::Initialize(); + Common::Log::SetColorConsoleBackendEnabled(true); + Common::Log::Start(); + } + + // Initialize filesystem. + m_system.SetFilesystem(m_vfs); + m_system.GetUserChannel().clear(); + m_manual_provider = std::make_unique<FileSys::ManualContentProvider>(); + m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); + m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, + m_manual_provider.get()); + m_system.GetFileSystemController().CreateFactories(*m_vfs); +} + +Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) { + std::scoped_lock lock(m_mutex); + + // Create the render window. + m_window = + std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library); + + // Initialize system. + jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); + m_software_keyboard = android_keyboard.get(); + m_system.SetShuttingDown(false); + m_system.ApplySettings(); + Settings::LogSettings(); + m_system.HIDCore().ReloadInputDevices(); + m_system.SetAppletFrontendSet({ + nullptr, // Amiibo Settings + nullptr, // Controller Selector + nullptr, // Error Display + nullptr, // Mii Editor + nullptr, // Parental Controls + nullptr, // Photo Viewer + nullptr, // Profile Selector + std::move(android_keyboard), // Software Keyboard + nullptr, // Web Browser + }); + + // Initialize filesystem. + ConfigureFilesystemProvider(filepath); + + // Initialize account manager + m_profile_manager = std::make_unique<Service::Account::ProfileManager>(); + + // Load the ROM. + m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); + if (m_load_result != Core::SystemResultStatus::Success) { + return m_load_result; + } + + // Complete initialization. + m_system.GPU().Start(); + m_system.GetCpuManager().OnGpuReady(); + m_system.RegisterExitCallback([&] { HaltEmulation(); }); - if (m_system.DebuggerEnabled()) { - m_system.InitializeDebugger(); - } - - OnEmulationStarted(); + return Core::SystemResultStatus::Success; +} - while (true) { - { - [[maybe_unused]] std::unique_lock lock(m_mutex); - if (m_cv.wait_for(lock, std::chrono::milliseconds(800), - [&]() { return !m_is_running; })) { - // Emulation halted. - break; - } - } - { - // Refresh performance stats. - std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); - m_perf_stats = m_system.GetAndResetPerfStats(); - } - } - } +void EmulationSession::ShutdownEmulation() { + std::scoped_lock lock(m_mutex); - std::string GetRomTitle(const std::string& path) { - return GetRomMetadata(path).title; - } + m_is_running = false; - std::vector<u8> GetRomIcon(const std::string& path) { - return GetRomMetadata(path).icon; - } + // Unload user input. + m_system.HIDCore().UnloadInputDevices(); - bool GetIsHomebrew(const std::string& path) { - return GetRomMetadata(path).isHomebrew; + // Shutdown the main emulated process + if (m_load_result == Core::SystemResultStatus::Success) { + m_system.DetachDebugger(); + m_system.ShutdownMainProcess(); + m_detached_tasks.WaitForAllTasks(); + m_load_result = Core::SystemResultStatus::ErrorNotInitialized; + m_window.reset(); + OnEmulationStopped(Core::SystemResultStatus::Success); + return; } - void ResetRomMetadata() { - m_rom_metadata_cache.clear(); - } + // Tear down the render window. + m_window.reset(); +} - bool IsHandheldOnly() { - jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); +void EmulationSession::PauseEmulation() { + std::scoped_lock lock(m_mutex); + m_system.Pause(); + m_is_paused = true; +} - if (npad_style_set.fullkey == 1) { - return false; - } +void EmulationSession::UnPauseEmulation() { + std::scoped_lock lock(m_mutex); + m_system.Run(); + m_is_paused = false; +} - if (npad_style_set.handheld == 0) { - return false; - } +void EmulationSession::HaltEmulation() { + std::scoped_lock lock(m_mutex); + m_is_running = false; + m_cv.notify_one(); +} - return !Settings::IsDockedMode(); +void EmulationSession::RunEmulation() { + { + std::scoped_lock lock(m_mutex); + m_is_running = true; } - void SetDeviceType([[maybe_unused]] int index, int type) { - jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); + // Load the disk shader cache. + if (Settings::values.use_disk_shader_cache.GetValue()) { + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); + m_system.Renderer().ReadRasterizer()->LoadDiskResources( + m_system.GetApplicationProcessProgramID(), std::stop_token{}, LoadDiskCacheProgress); + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); } - void OnGamepadConnectEvent([[maybe_unused]] int index) { - jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - - // Ensure that player1 is configured correctly and handheld disconnected - if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { - jauto handheld = - m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); + void(m_system.Run()); - if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { - handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); - handheld->Disconnect(); - } - } + if (m_system.DebuggerEnabled()) { + m_system.InitializeDebugger(); + } - // Ensure that handheld is configured correctly and player 1 disconnected - if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { - jauto player1 = - m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + OnEmulationStarted(); - if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { - player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); - player1->Disconnect(); + while (true) { + { + [[maybe_unused]] std::unique_lock lock(m_mutex); + if (m_cv.wait_for(lock, std::chrono::milliseconds(800), + [&]() { return !m_is_running; })) { + // Emulation halted. + break; } } - - if (!controller->IsConnected()) { - controller->Connect(); - } } +} + +bool EmulationSession::IsHandheldOnly() { + jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); - void OnGamepadDisconnectEvent([[maybe_unused]] int index) { - jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - controller->Disconnect(); + if (npad_style_set.fullkey == 1) { + return false; } - SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() { - return m_software_keyboard; + if (npad_style_set.handheld == 0) { + return false; } -private: - struct RomMetadata { - std::string title; - std::vector<u8> icon; - bool isHomebrew; - }; + return !Settings::IsDockedMode(); +} - RomMetadata GetRomMetadata(const std::string& path) { - if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { - return search->second; - } +void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) { + jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); +} - return CacheRomMetadata(path); - } +void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) { + jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - RomMetadata CacheRomMetadata(const std::string& path) { - jconst file = Core::GetGameFileFromPath(m_vfs, path); - jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); + // Ensure that player1 is configured correctly and handheld disconnected + if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { + jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); - RomMetadata entry; - loader->ReadTitle(entry.title); - loader->ReadIcon(entry.icon); - if (loader->GetFileType() == Loader::FileType::NRO) { - jauto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get()); - entry.isHomebrew = loader_nro->IsHomebrew(); - } else { - entry.isHomebrew = false; + if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { + handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + handheld->Disconnect(); } - - m_rom_metadata_cache[path] = entry; - - return entry; } -private: - static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) { - JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), - IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage), - static_cast<jint>(progress), static_cast<jint>(max)); - } + // Ensure that handheld is configured correctly and player 1 disconnected + if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { + jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); - static void OnEmulationStarted() { - JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), - IDCache::GetOnEmulationStarted()); + if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { + player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); + player1->Disconnect(); + } } - static void OnEmulationStopped(Core::SystemResultStatus result) { - JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), - IDCache::GetOnEmulationStopped(), static_cast<jint>(result)); + if (!controller->IsConnected()) { + controller->Connect(); } +} -private: - static EmulationSession s_instance; - - // Frontend management - std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache; - - // Window management - std::unique_ptr<EmuWindow_Android> m_window; - ANativeWindow* m_native_window{}; - - // Core emulation - Core::System m_system; - InputCommon::InputSubsystem m_input_subsystem; - Common::DetachedTasks m_detached_tasks; - Core::PerfStatsResults m_perf_stats{}; - std::shared_ptr<FileSys::VfsFilesystem> m_vfs; - Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; - std::atomic<bool> m_is_running = false; - std::atomic<bool> m_is_paused = false; - SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; - std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; - std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; +void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) { + jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + controller->Disconnect(); +} - // GPU driver parameters - std::shared_ptr<Common::DynamicLibrary> m_vulkan_library; +SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { + return m_software_keyboard; +} - // Synchronization - std::condition_variable_any m_cv; - mutable std::mutex m_perf_stats_mutex; - mutable std::mutex m_mutex; -}; +void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, + int max) { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), + IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage), + static_cast<jint>(progress), static_cast<jint>(max)); +} -/*static*/ EmulationSession EmulationSession::s_instance; +void EmulationSession::OnEmulationStarted() { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted()); +} -} // Anonymous namespace +void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(), + static_cast<jint>(result)); +} static Core::SystemResultStatus RunEmulation(const std::string& filepath) { - Common::Log::Initialize(); - Common::Log::SetColorConsoleBackendEnabled(true); - Common::Log::Start(); - MicroProfileOnThreadCreate("EmuThread"); SCOPE_EXIT({ MicroProfileShutdown(); }); @@ -657,10 +559,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass cla EmulationSession::GetInstance().HaltEmulation(); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) { - EmulationSession::GetInstance().ResetRomMetadata(); -} - jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) { return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning()); } @@ -766,51 +664,15 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c } } -jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz, - jstring j_filename) { - jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename)); - jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); - env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), - reinterpret_cast<jbyte*>(icon_data.data())); - return icon; -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz, - jstring j_filename) { - jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename)); - return env->NewStringUTF(title.c_str()); -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz, - jstring j_filename) { - return j_filename; -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz, - jstring j_filename) { - return j_filename; -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz, - jstring j_filename) { - return env->NewStringUTF(""); -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz, - jstring j_filename) { - return env->NewStringUTF(""); -} - -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz, - jstring j_filename) { - return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); -} - -void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, + jboolean reload) { // Create the default config.ini. Config{}; // Initialize the emulated system. - EmulationSession::GetInstance().System().Initialize(); + if (!reload) { + EmulationSession::GetInstance().System().Initialize(); + } + EmulationSession::GetInstance().InitializeSystem(reload); } jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) { @@ -898,4 +760,49 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv* } } +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, jclass clazz, + jlong jid) { + auto bis_system = + EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents(); + if (!bis_system) { + return ToJString(env, ""); + } + + auto applet_nca = + bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program); + if (!applet_nca) { + return ToJString(env, ""); + } + + return ToJString(env, applet_nca->GetFullPath()); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz, + jint jappletId) { + EmulationSession::GetInstance().System().GetAppletManager().SetCurrentAppletId( + static_cast<Service::AM::Applets::AppletId>(jappletId)); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass clazz, + jint jcabinetMode) { + EmulationSession::GetInstance().System().GetAppletManager().SetCabinetMode( + static_cast<Service::NFP::CabinetMode>(jcabinetMode)); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env, jclass clazz) { + auto bis_system = + EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents(); + if (!bis_system) { + return false; + } + + // Query an applet to see if it's available + auto applet_nca = + bis_system->GetEntry(0x010000000000100Dull, FileSys::ContentRecordType::Program); + if (!applet_nca) { + return false; + } + return true; +} + } // extern "C" diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h new file mode 100644 index 000000000..6b02c44b5 --- /dev/null +++ b/src/android/app/src/main/jni/native.h @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <android/native_window_jni.h> +#include "common/detached_tasks.h" +#include "core/core.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/perf_stats.h" +#include "jni/applets/software_keyboard.h" +#include "jni/emu_window/emu_window.h" +#include "video_core/rasterizer_interface.h" + +#pragma once + +class EmulationSession final { +public: + explicit EmulationSession(); + ~EmulationSession() = default; + + static EmulationSession& GetInstance(); + const Core::System& System() const; + Core::System& System(); + + const EmuWindow_Android& Window() const; + EmuWindow_Android& Window(); + ANativeWindow* NativeWindow() const; + void SetNativeWindow(ANativeWindow* native_window); + void SurfaceChanged(); + + int InstallFileToNand(std::string filename, std::string file_extension); + void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, + const std::string& custom_driver_name, + const std::string& file_redirect_dir); + + bool IsRunning() const; + bool IsPaused() const; + void PauseEmulation(); + void UnPauseEmulation(); + void HaltEmulation(); + void RunEmulation(); + void ShutdownEmulation(); + + const Core::PerfStatsResults& PerfStats(); + void ConfigureFilesystemProvider(const std::string& filepath); + void InitializeSystem(bool reload); + Core::SystemResultStatus InitializeEmulation(const std::string& filepath); + + bool IsHandheldOnly(); + void SetDeviceType([[maybe_unused]] int index, int type); + void OnGamepadConnectEvent([[maybe_unused]] int index); + void OnGamepadDisconnectEvent([[maybe_unused]] int index); + SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); + +private: + static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); + static void OnEmulationStarted(); + static void OnEmulationStopped(Core::SystemResultStatus result); + +private: + // Window management + std::unique_ptr<EmuWindow_Android> m_window; + ANativeWindow* m_native_window{}; + + // Core emulation + Core::System m_system; + InputCommon::InputSubsystem m_input_subsystem; + Common::DetachedTasks m_detached_tasks; + Core::PerfStatsResults m_perf_stats{}; + std::shared_ptr<FileSys::VfsFilesystem> m_vfs; + Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; + std::atomic<bool> m_is_running = false; + std::atomic<bool> m_is_paused = false; + SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; + std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; + std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; + + // GPU driver parameters + std::shared_ptr<Common::DynamicLibrary> m_vulkan_library; + + // Synchronization + std::condition_variable_any m_cv; + mutable std::mutex m_mutex; +}; diff --git a/src/android/app/src/main/jni/native_log.cpp b/src/android/app/src/main/jni/native_log.cpp new file mode 100644 index 000000000..33d691dc8 --- /dev/null +++ b/src/android/app/src/main/jni/native_log.cpp @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <common/logging/log.h> +#include <jni.h> + +#include "android_common/android_common.h" + +extern "C" { + +void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_WARNING(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_INFO(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_ERROR(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage)); +} + +} // extern "C" diff --git a/src/android/app/src/main/res/drawable/ic_album.xml b/src/android/app/src/main/res/drawable/ic_album.xml new file mode 100644 index 000000000..f2b63813f --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_album.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_applet.xml b/src/android/app/src/main/res/drawable/ic_applet.xml new file mode 100644 index 000000000..b154e6f56 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_applet.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M17,16l-4,-4V8.82C14.16,8.4 15,7.3 15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6c0,1.3 0.84,2.4 2,2.82V12l-4,4H3v5h5v-3.05l4,-4.2 4,4.2V21h5v-5h-4z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_edit.xml b/src/android/app/src/main/res/drawable/ic_edit.xml new file mode 100644 index 000000000..ac22ce8a5 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_mii.xml b/src/android/app/src/main/res/drawable/ic_mii.xml new file mode 100644 index 000000000..1271ec401 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_mii.xml @@ -0,0 +1,18 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M9,13m-1.25,0a1.25,1.25 0,1 1,2.5 0a1.25,1.25 0,1 1,-2.5 0" /> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M20.77,8.58l-0.92,2.01c0.09,0.46 0.15,0.93 0.15,1.41 0,4.41 -3.59,8 -8,8s-8,-3.59 -8,-8c0,-0.05 0.01,-0.1 0,-0.14 2.6,-0.98 4.69,-2.99 5.74,-5.55C11.58,8.56 14.37,10 17.5,10c0.45,0 0.89,-0.04 1.33,-0.1l-0.6,-1.32 -0.88,-1.93 -1.93,-0.88 -2.79,-1.27 2.79,-1.27 0.71,-0.32C14.87,2.33 13.47,2 12,2 6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10c0,-1.47 -0.33,-2.87 -0.9,-4.13l-0.33,0.71z" /> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M15,13m-1.25,0a1.25,1.25 0,1 1,2.5 0a1.25,1.25 0,1 1,-2.5 0" /> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M20.6,5.6L19.5,8l-1.1,-2.4L16,4.5l2.4,-1.1L19.5,1l1.1,2.4L23,4.5z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_refresh.xml b/src/android/app/src/main/res/drawable/ic_refresh.xml new file mode 100644 index 000000000..d0d87ecc2 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_refresh.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_restore.xml b/src/android/app/src/main/res/drawable/ic_restore.xml new file mode 100644 index 000000000..d6d9d4017 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_restore.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z" /> +</vector> diff --git a/src/android/app/src/main/res/layout/card_applet_option.xml b/src/android/app/src/main/res/layout/card_applet_option.xml new file mode 100644 index 000000000..19fbec9f1 --- /dev/null +++ b/src/android/app/src/main/res/layout/card_applet_option.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + style="?attr/materialCardViewOutlinedStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="16dp" + android:layout_marginVertical="12dp" + android:background="?attr/selectableItemBackground" + android:clickable="true" + android:focusable="true"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_gravity="center" + android:padding="24dp"> + + <ImageView + android:id="@+id/icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginEnd="20dp" + android:layout_gravity="center_vertical" + app:tint="?attr/colorOnSurface" /> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical" + android:layout_gravity="center_vertical"> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/title" + style="@style/TextAppearance.Material3.TitleMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="viewStart" + tools:text="@string/applets" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/description" + style="@style/TextAppearance.Material3.BodyMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="6dp" + android:textAlignment="viewStart" + tools:text="@string/applets_description" /> + + </LinearLayout> + + </LinearLayout> + +</com.google.android.material.card.MaterialCardView> diff --git a/src/android/app/src/main/res/layout/card_game.xml b/src/android/app/src/main/res/layout/card_game.xml index 1f5de219b..6340171ec 100644 --- a/src/android/app/src/main/res/layout/card_game.xml +++ b/src/android/app/src/main/res/layout/card_game.xml @@ -1,63 +1,54 @@ <?xml version="1.0" encoding="utf-8"?> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content"> <com.google.android.material.card.MaterialCardView - style="?attr/materialCardViewElevatedStyle" android:id="@+id/card_game" + style="?attr/materialCardViewElevatedStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_gravity="center" android:background="?attr/selectableItemBackground" android:clickable="true" android:clipToPadding="false" android:focusable="true" android:transitionName="card_game" - android:layout_gravity="center" - app:cardElevation="0dp" - app:cardCornerRadius="12dp"> + app:cardCornerRadius="4dp" + app:cardElevation="0dp"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="6dp"> - <com.google.android.material.card.MaterialCardView - style="?attr/materialCardViewElevatedStyle" - android:id="@+id/card_game_art" + <com.google.android.material.imageview.ShapeableImageView + android:id="@+id/image_game_screen" android:layout_width="150dp" android:layout_height="150dp" - app:cardCornerRadius="4dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> - - <ImageView - android:id="@+id/image_game_screen" - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:src="@drawable/default_icon" /> - - </com.google.android.material.card.MaterialCardView> + app:layout_constraintTop_toTopOf="parent" + app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.ExtraSmall" + tools:src="@drawable/default_icon" /> <com.google.android.material.textview.MaterialTextView - style="@style/TextAppearance.Material3.TitleMedium" android:id="@+id/text_game_title" + style="@style/TextAppearance.Material3.TitleMedium" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:textAlignment="center" - android:textSize="14sp" - android:singleLine="true" - android:marqueeRepeatLimit="marquee_forever" android:ellipsize="none" + android:marqueeRepeatLimit="marquee_forever" android:requiresFadingEdge="horizontal" - app:layout_constraintEnd_toEndOf="@+id/card_game_art" - app:layout_constraintStart_toStartOf="@+id/card_game_art" - app:layout_constraintTop_toBottomOf="@+id/card_game_art" + android:singleLine="true" + android:textAlignment="center" + android:textSize="14sp" + app:layout_constraintEnd_toEndOf="@+id/image_game_screen" + app:layout_constraintStart_toStartOf="@+id/image_game_screen" + app:layout_constraintTop_toBottomOf="@+id/image_game_screen" tools:text="The Legend of Zelda: Skyward Sword" /> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/src/android/app/src/main/res/layout/card_home_option.xml b/src/android/app/src/main/res/layout/card_home_option.xml index f9f1d89fb..6e8a232f9 100644 --- a/src/android/app/src/main/res/layout/card_home_option.xml +++ b/src/android/app/src/main/res/layout/card_home_option.xml @@ -16,7 +16,8 @@ <LinearLayout android:id="@+id/option_layout" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:layout_gravity="center_vertical"> <ImageView android:id="@+id/option_icon" diff --git a/src/android/app/src/main/res/layout/dialog_list.xml b/src/android/app/src/main/res/layout/dialog_list.xml new file mode 100644 index 000000000..7de2b2c3a --- /dev/null +++ b/src/android/app/src/main/res/layout/dialog_list.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/dialog_list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipToPadding="false" + android:fadeScrollbars="false" + android:paddingVertical="12dp" + android:scrollbars="vertical" /> + +</androidx.appcompat.widget.LinearLayoutCompat> diff --git a/src/android/app/src/main/res/layout/dialog_list_item.xml b/src/android/app/src/main/res/layout/dialog_list_item.xml new file mode 100644 index 000000000..39f3558ff --- /dev/null +++ b/src/android/app/src/main/res/layout/dialog_list_item.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:clickable="true" + android:focusable="true" + android:orientation="horizontal" + android:paddingHorizontal="24dp" + android:paddingVertical="16dp"> + + <ImageView + android:id="@+id/icon" + android:layout_width="20dp" + android:layout_height="20dp" + android:layout_gravity="center" + tools:src="@drawable/ic_nfc" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/title" + style="@style/TextAppearance.Material3.BodyMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_gravity="center_vertical|start" + android:textAlignment="viewStart" + tools:text="List option" /> + +</LinearLayout> diff --git a/src/android/app/src/main/res/layout/fragment_applet_launcher.xml b/src/android/app/src/main/res/layout/fragment_applet_launcher.xml new file mode 100644 index 000000000..fe8fae40f --- /dev/null +++ b/src/android/app/src/main/res/layout/fragment_applet_launcher.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/coordinator_applets" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/colorSurface"> + + <com.google.android.material.appbar.AppBarLayout + android:id="@+id/appbar_applets" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:fitsSystemWindows="true"> + + <com.google.android.material.appbar.MaterialToolbar + android:id="@+id/toolbar_applets" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + app:navigationIcon="@drawable/ic_back" + app:title="@string/applets" /> + + </com.google.android.material.appbar.AppBarLayout> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/list_applets" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipToPadding="false" + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> + +</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index 750ce094a..cd6360b45 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -134,16 +134,18 @@ <FrameLayout android:id="@+id/overlay_container" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:fitsSystemWindows="true"> - <TextView + <com.google.android.material.textview.MaterialTextView android:id="@+id/show_fps_text" + style="@style/TextAppearance.Material3.BodyMedium" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left" android:clickable="false" android:focusable="false" - android:shadowColor="@android:color/black" + android:paddingHorizontal="20dp" android:textColor="@android:color/white" android:textSize="12sp" tools:ignore="RtlHardcoded" /> diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml index 82749359d..6d4c1f86d 100644 --- a/src/android/app/src/main/res/navigation/home_navigation.xml +++ b/src/android/app/src/main/res/navigation/home_navigation.xml @@ -25,6 +25,9 @@ <action android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment" app:destination="@id/driverManagerFragment" /> + <action + android:id="@+id/action_homeSettingsFragment_to_appletLauncherFragment" + app:destination="@id/appletLauncherFragment" /> </fragment> <fragment @@ -102,5 +105,17 @@ android:id="@+id/driverManagerFragment" android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment" android:label="DriverManagerFragment" /> + <fragment + android:id="@+id/appletLauncherFragment" + android:name="org.yuzu.yuzu_emu.fragments.AppletLauncherFragment" + android:label="AppletLauncherFragment" > + <action + android:id="@+id/action_appletLauncherFragment_to_cabinetLauncherDialogFragment" + app:destination="@id/cabinetLauncherDialogFragment" /> + </fragment> + <dialog + android:id="@+id/cabinetLauncherDialogFragment" + android:name="org.yuzu.yuzu_emu.fragments.CabinetLauncherDialogFragment" + android:label="CabinetLauncherDialogFragment" /> </navigation> diff --git a/src/android/app/src/main/res/resources.properties b/src/android/app/src/main/res/resources.properties new file mode 100644 index 000000000..467b3efec --- /dev/null +++ b/src/android/app/src/main/res/resources.properties @@ -0,0 +1 @@ +unqualifiedResLocale=en-US diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml new file mode 100644 index 000000000..07dffffe8 --- /dev/null +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -0,0 +1,385 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + + <string name="emulation_notification_channel_name">المحاكي نشط</string> + <string name="emulation_notification_channel_description">اظهار اشعار دائم عندما يكون المحاكي نشطاً</string> + <string name="emulation_notification_running">يوزو يعمل</string> + <string name="notice_notification_channel_name">الإشعارات والأخطاء</string> + <string name="notice_notification_channel_description">اظهار اشعار عند حصول اي مشكلة.</string> + <string name="notification_permission_not_granted">لم يتم منح إذن الإشعار</string> + + <!-- Setup strings --> + <string name="welcome">مرحبًا</string> + <string name="welcome_description">والانتقال إلى المحاكاة <b>يوزو</b> تعرف على كيفية إعداد.</string> + <string name="get_started">لنبدأ</string> + <string name="keys">المفاتيح</string> + <string name="keys_description">اختر ملف <b>prod.keys</b> من الزر ادناه</string> + <string name="select_keys">إختيار المفاتيح</string> + <string name="games">الألعاب</string> + <string name="games_description">اختر مجلد <b>العابك</b> من الزر ادناه.</string> + <string name="done">إنهاء</string> + <string name="done_description">كل شيء جاهز./n استمتع بألعابك!</string> + <string name="text_continue">استمر</string> + <string name="next">التالي</string> + <string name="back">عودة</string> + <string name="add_games">إضافة ألعاب</string> + <string name="add_games_description">إختار مجلد ألعابك</string> + <string name="step_complete">مكتمل</string> + + <!-- Home strings --> + <string name="home_games">الألعاب</string> + <string name="home_search">البحث</string> + <string name="home_settings">الإعدادات</string> + <string name="empty_gamelist">لم يتم العثور على ملفات او لم يتم تحديد مسار العاب.</string> + <string name="search_and_filter_games">بحث وتصفية الألعاب</string> + <string name="select_games_folder">تحديد مجلد الألعاب</string> + <string name="select_games_folder_description">يسمح لـ يوزو بملء قائمة الألعاب</string> + <string name="add_games_warning">تخطُ اختيار مجلد الالعاب؟</string> + <string name="add_games_warning_description">لن يتم عرض الألعاب في قائمة الألعاب إذا لم يتم تحديد مجلد</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">البحث عن ألعاب</string> + <string name="search_settings">إعدادات البحث</string> + <string name="games_dir_selected">تم تحديد مجلد الألعاب</string> + <string name="install_prod_keys">تثبيت prod.keys</string> + <string name="install_prod_keys_description">مطلوب لفك تشفير ألعاب البيع بالتجزئة</string> + <string name="install_prod_keys_warning">تخطي إضافة المفاتيح؟</string> + <string name="install_prod_keys_warning_description">مطلوب مفاتيح صالحة لمحاكاة ألعاب البيع بالتجزئة. ستعمل تطبيقات البيرة المنزلية فقط إذا تابعت</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">التنبيهات</string> + <string name="notifications_description">امنح إذن الإشعار باستخدام الزر أدناه</string> + <string name="give_permission">منح الإذن</string> + <string name="notification_warning">تخطي منح إذن الإشعارات؟</string> + <string name="notification_warning_description">لن يتمكن يوزو من إشعارك بالمعلومات المهمة</string> + <string name="permission_denied">تم رفض الإذن</string> + <string name="permission_denied_description">لقد رفضت هذا الإذن عدة مرات ويتعين عليك الآن منحه يدويًا في إعدادات النظام</string> + <string name="about">حول</string> + <string name="about_description">بناء الإصدار، والاعتمادات، وأكثر من ذلك</string> + <string name="warning_help">مساعدة</string> + <string name="warning_skip">تخطي</string> + <string name="warning_cancel">إلغاء</string> + <string name="install_amiibo_keys">تثبيت مفاتيح أميبو</string> + <string name="install_amiibo_keys_description">مطلوب لاستخدام أميبو في اللعبة</string> + <string name="invalid_keys_file">تم تحديد ملف مفاتيح غير صالح</string> + <string name="install_keys_success">تم تثبيت المفاتيح بنجاح</string> + <string name="reading_keys_failure">خطأ في قراءة مفاتيح التشفير</string> + <string name="invalid_keys_error">مفاتيح التشفير غير صالحة</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">الملف المحدد غير صحيح أو تالف. يرجى إعادة المفاتيح الخاصة بك</string> + <string name="install_gpu_driver">GPU تثبيت برنامج تشغيل</string> + <string name="install_gpu_driver_description">قم بتثبيت برامج تشغيل بديلة للحصول على أداء أو دقة أفضل</string> + <string name="advanced_settings">إعدادات متقدمة</string> + <string name="advanced_settings_game">إعدادات متقدمة: %1$s</string> + <string name="settings_description">تكوين إعدادات المحاكي</string> + <string name="search_recently_played">لعبت مؤخرا</string> + <string name="search_recently_added">أضيف مؤخرا</string> + <string name="search_retail">بيع بالتجزئة</string> + <string name="search_homebrew">البيرة المنزلية</string> + <string name="open_user_folder">فتح مجلد يوزو</string> + <string name="open_user_folder_description">إدارة ملفات يوزو الداخلية</string> + <string name="theme_and_color_description">تعديل مظهر التطبيق</string> + <string name="no_file_manager">لم يتم العثور على مدير الملفات</string> + <string name="notification_no_directory_link">لا يمكن فتح مجلد يوزو</string> + <string name="notification_no_directory_link_description">الرجاء تحديد موقع مجلد المستخدم باستخدام اللوحة الجانبية لمدير الملفات يدويًا</string> + <string name="manage_save_data">إدارة حفظ البيانات</string> + <string name="manage_save_data_description">حفظ البيانات التي تم العثور عليها. يرجى اختيار أحد الخيارات التالية</string> + <string name="import_export_saves_description">استيراد أو تصدير ملفات الحفظ</string> + <string name="save_file_imported_success">تم الاستيراد بنجاح</string> + <string name="save_file_invalid_zip_structure">بنية مجلد الحفظ غير صالحة</string> + <string name="save_file_invalid_zip_structure_description">يجب أن يكون اسم المجلد الفرعي الأول هو معرف عنوان اللعبة.</string> + <string name="import_saves">استيراد</string> + <string name="export_saves">تصدير</string> + <string name="install_firmware">تثبيت البرامج الثابتة</string> + <string name="firmware_installing">تثبيت البرامج الثابتة</string> + <string name="firmware_installed_success">تم تثبيت البرامج الثابتة بنجاح</string> + <string name="firmware_installed_failure">فشل تثبيت البرامج الثابتة</string> + <string name="share_log">مشاركة سجلات التصحيح</string> + <string name="share_log_description">مشاركة ملف سجل يوزو لتصحيح المشكلات</string> + <string name="share_log_missing">لم يتم العثور على ملف السجل</string> + <string name="install_game_content">تثبيت محتوى اللعبة</string> + <string name="install_game_content_description">DLC قم بتثبيت تحديثات اللعبة أو</string> + <string name="installing_game_content">جارٍ تثبيت المحتوى</string> + <string name="install_game_content_failure_base">لا يُسمح بتثبيت الألعاب الأساسية لتجنب التعارضات المحتملة.</string> + <string name="install_game_content_success_install">%1$d تم التثبيت بنجاح</string> + <string name="install_game_content_success_overwrite">%1$d تمت الكتابة فوقه بنجاح</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">برامج التشغيل المخصصة غير مدعومة</string> + <string name="custom_driver_not_supported_description">تحميل برنامج التشغيل المخصص غير معتمد حاليًا لهذا الجهاز.\nحدد هذا الخيار مرة أخرى في المستقبل لمعرفة ما إذا تمت إضافة الدعم!</string> + <string name="manage_yuzu_data">إدارة بيانات يوزو</string> + <string name="manage_yuzu_data_description">استيراد/تصدير البرامج الثابتة والمفاتيح وبيانات المستخدم والمزيد!</string> + <string name="share_save_file">مشاركة ملف الحفظ</string> + <string name="export_save_failed">فشل تصدير الحفظ</string> + + <string name="copied_to_clipboard">نسخ إلى الحافظة</string> + <string name="about_app_description">محاكي سويتش مفتوح المصدر</string> + <string name="contributors">المساهمين</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">المشاريع التي تجعل تطبيق يوزو لنظام أندرويد ممكنًا</string> + <string name="build">البناء</string> + <string name="user_data">بيانات المستخدم</string> + <string name="exporting_user_data">جارٍ تصدير بيانات المستخدم</string> + <string name="importing_user_data">جارٍ استيراد بيانات المستخدم</string> + <string name="import_user_data">استيراد بيانات المستخدم</string> + <string name="invalid_yuzu_backup">نسخة احتياطية يوزو غير صالحة</string> + <string name="user_data_export_success">تم تصدير بيانات المستخدم بنجاح</string> + <string name="user_data_import_success">تم استيراد بيانات المستخدم بنجاح</string> + <string name="user_data_export_cancelled">تم إلغاء التصدير</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">الوصول المبكر</string> + <string name="get_early_access">احصل على الوصول المبكر</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">الميزات المتطورة، والوصول المبكر إلى التحديثات، وأكثر من ذلك</string> + <string name="early_access_benefits">مزايا الوصول المبكر</string> + <string name="cutting_edge_features">ميزات متطورة</string> + <string name="early_access_updates">الوصول المبكر إلى التحديثات</string> + <string name="no_manual_installation">لا يوجد التثبيت اليدوي</string> + <string name="prioritized_support">الدعم ذو الأولوية</string> + <string name="helping_game_preservation">المساعدة في الحفاظ على اللعبة</string> + <string name="our_eternal_gratitude">امتناننا الأبدي</string> + <string name="are_you_interested">هل انت مهتم؟</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">الحد من السرعة</string> + <string name="frame_limit_enable_description">يحد من سرعة المحاكاة بنسبة محددة من السرعة العادية</string> + <string name="frame_limit_slider">الحد من السرعة في المئة</string> + <string name="frame_limit_slider_description">يحدد النسبة المئوية للحد من سرعة المحاكاة. 100% هي السرعة الطبيعية. ستؤدي القيم الأعلى أو الأدنى إلى زيادة أو تقليل حد السرعة.</string> + <string name="cpu_accuracy">دقة وحدة المعالجة المركزية</string> + <string name="value_with_units">%1$s%2$s</string> + + <!-- System settings strings --> + <string name="use_docked_mode">وضع الإرساء</string> + <string name="use_docked_mode_description">زيادة الدقة، وانخفاض الأداء. يتم استخدام الوضع المحمول عند تعطيله، مما يؤدي إلى خفض الدقة وزيادة الأداء.</string> + <string name="emulated_region">المنطقة التي تمت محاكاتها</string> + <string name="emulated_language">لغة المحاكاه</string> + <string name="select_rtc_date">حدد التاريخ و الساعة في الوقت الحقيقي</string> + <string name="select_rtc_time">حدد وقت الساعة في الوقت الفعلي</string> + <string name="use_custom_rtc">ساعة مخصصة في الوقت الحقيقي</string> + <string name="use_custom_rtc_description">يسمح لك بتعيين ساعة مخصصة في الوقت الفعلي منفصلة عن وقت النظام الحالي لديك</string> + <string name="set_custom_rtc">تعيين ساعة مخصصة في الوقت الحقيقي</string> + + <!-- Graphics settings strings --> + <string name="renderer_accuracy">مستوى الدقة</string> + <string name="renderer_resolution">(Handheld/Docked) الدقة</string> + <string name="renderer_vsync">VSync وضع</string> + <string name="renderer_screen_layout">الاتجاه</string> + <string name="renderer_aspect_ratio">تناسب الابعاد</string> + <string name="renderer_anti_aliasing">طريقة مكافحة التعرج</string> + <string name="renderer_asynchronous_shaders">استخدم تظليل غير متزامن</string> + <string name="renderer_asynchronous_shaders_description">يجمع التظليل بشكل غير متزامن، مما يقلل من التأتأة ولكنه قد يؤدي إلى حدوث بعض الأخطاء.</string> + <string name="renderer_reactive_flushing">استخدم التنظيف التفاعلي</string> + <string name="renderer_reactive_flushing_description">تحسين دقة العرض في بعض الألعاب على حساب الأداء</string> + <string name="use_disk_shader_cache_description">يقلل من التأتأة عن طريق تخزين وتحميل التظليلات التي تم إنشاؤها محليًا.</string> + + <!-- Debug settings strings --> + <string name="cpu">وحدة المعالج المركزية</string> + <string name="cpu_debug_mode">تصحيح أخطاء وحدة المعالجة المركزية</string> + <string name="cpu_debug_mode_description">يضع وحدة المعالجة المركزية في وضع التصحيح البطيء.</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">تصحيح الأخطاء الرسومية</string> + <string name="renderer_debug_description">يضبط واجهة برمجة تطبيقات الرسومات على وضع تصحيح الأخطاء البطيء.</string> + <string name="fastmem">Fastmem</string> + + <!-- Audio settings strings --> + <string name="audio_output_engine">محرك الإخراج</string> + <string name="audio_volume">حجم</string> + <string name="audio_volume_description">يحدد حجم إخراج الصوت</string> + + <!-- Miscellaneous --> + <string name="slider_default">افتراضي</string> + <string name="ini_saved">الإعدادات المحفوظة</string> + <string name="gameid_saved">الإعدادات المحفوظة لـ %1$s</string> + <string name="unimplemented_menu">القائمة غير المنفذة</string> + <string name="loading">جاري تحميل</string> + <string name="shutting_down">إيقاف تشغيل</string> + <string name="reset_setting_confirmation">هل تريد إعادة تعيين هذا الإعداد مرة أخرى إلى قيمته الافتراضية؟</string> + <string name="reset_to_default">إعادة تعيين إلى الافتراضي</string> + <string name="reset_all_settings">إعادة تعيين جميع الإعدادات؟</string> + <string name="reset_all_settings_description">سيتم إعادة تعيين كافة الإعدادات المتقدمة إلى تكوينها الافتراضي. هذا لا يمكن التراجع عنها.</string> + <string name="settings_reset">إعادة تعيين الأعدادات</string> + <string name="close">إغلاق</string> + <string name="learn_more">معرفة المزيد</string> + <string name="auto">تلقائي</string> + <string name="submit">إرسال</string> + <string name="string_null">قيمه خاليه</string> + <string name="string_import">استيراد</string> + <string name="export">تصدير</string> + <string name="export_failed">فشل التصدير</string> + <string name="import_failed">فشل الاستيراد</string> + <string name="cancelling">إلغاء</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">GPU حدد برنامج تشغيل</string> + <string name="select_gpu_driver_title">الحالي الخاص بك؟ GPU هل ترغب في استبدال برنامج تشغيل</string> + <string name="select_gpu_driver_install">تثبيت</string> + <string name="select_gpu_driver_default">افتراضي</string> + <string name="select_gpu_driver_use_default">يستخدم تعريف معالج الرسوميات الافتراضي</string> + <string name="select_gpu_driver_error">تم تحديد برنامج تشغيل غير صالح ، باستخدام النظام الافتراضي</string> + <string name="system_gpu_driver">تعريف معالج الرسوميات الخاص بالنظام</string> + <string name="installing_driver">جارٍ تثبيت برنامج التشغيل…</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">إعدادات</string> + <string name="preferences_general">عام</string> + <string name="preferences_system">النظام</string> + <string name="preferences_graphics">الرسوميات</string> + <string name="preferences_audio">الصوت</string> + <string name="preferences_theme">السمة واللون</string> + <string name="preferences_debug">تصحيح الأخطاء</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">الخاص بك ROM تم تشفير</string> + <string name="loader_error_video_core">حدث خطأ أثناء تهيئة مركز الفيديو</string> + <string name="loader_error_invalid_format">ROM غير قادر على تحميل</string> + <string name="loader_error_file_not_found">غير موجود ROM ملف</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">الخروج من المحاكاة</string> + <string name="emulation_done">منجز</string> + <string name="emulation_fps_counter">عداد إطار/ثانية</string> + <string name="emulation_toggle_controls">تبديل عناصر التحكم</string> + <string name="emulation_rel_stick_center">مركز العصا النسبي</string> + <string name="emulation_dpad_slide">مزلاق أزرار الاتجاهات</string> + <string name="emulation_haptics">الاهتزازات الديناميكية</string> + <string name="emulation_show_overlay">عرض التراكب</string> + <string name="emulation_toggle_all">تبديل الكل</string> + <string name="emulation_control_adjust">ضبط التراكب</string> + <string name="emulation_control_scale">حجم</string> + <string name="emulation_control_opacity">العتامه</string> + <string name="emulation_touch_overlay_reset">إعادة تعيين التراكب</string> + <string name="emulation_touch_overlay_edit">تحرير التراكب</string> + <string name="emulation_pause">إيقاف المحاكاة مؤقتًا</string> + <string name="emulation_unpause">إلغاء الإيقاف المؤقت للمضاهاة</string> + <string name="emulation_input_overlay">خيارات التراكب</string> + + <string name="load_settings">جارٍ تحميل الإعدادات</string> + + <!-- Software keyboard --> + <string name="software_keyboard">لوحة المفاتيح البرمجية</string> + + <!-- Errors and warnings --> + <string name="abort_button">إلغاء</string> + <string name="continue_button">استمر</string> + <string name="system_archive_not_found">لم يتم العثور على أرشيف النظام</string> + <string name="system_archive_general">أرشيف النظام</string> + <string name="save_load_error">خطأ في الحفظ/التحميل</string> + <string name="fatal_error">خطا فادح</string> + <string name="performance_warning">سيؤدي إيقاف تشغيل هذا الإعداد إلى تقليل أداء المحاكاة بشكل ملحوظ! للحصول على أفضل تجربة، يوصى بترك هذا الإعداد ممكنًا.</string> + <string name="memory_formatted">%1$s %2$s</string> + <string name="no_game_present">لا توجد لعبة قابلة للتمهيد</string> + + <!-- Region Names --> + <string name="region_japan">اليابان</string> + <string name="region_usa">الولايات المتحدة الأمريكية</string> + <string name="region_europe">أوروبا</string> + <string name="region_australia">أستراليا</string> + <string name="region_china">الصين</string> + <string name="region_korea">كوريا</string> + <string name="region_taiwan">تايوان</string> + + <!-- Memory Sizes --> + <string name="memory_byte">Byte</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">لاشيء</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">عادي</string> + <string name="renderer_accuracy_high">عالي</string> + <string name="renderer_accuracy_extreme">Extreme (بطيء)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (بطيء)</string> + <string name="resolution_three">3X (2160p/3240p) (بطيء)</string> + <string name="resolution_four">4X (2880p/4320p) (بطيء)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Immediate (Off)</string> + <string name="renderer_vsync_mailbox">Mailbox</string> + <string name="renderer_vsync_fifo">FIFO (On)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Nearest Neighbor</string> + <string name="scaling_filter_bilinear">Bilinear</string> + <string name="scaling_filter_bicubic">Bicubic</string> + <string name="scaling_filter_gaussian">Gaussian</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">لا شيء</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Screen Layouts --> + <string name="screen_layout_landscape">افقي</string> + <string name="screen_layout_portrait">عمودي</string> + <string name="screen_layout_auto">تلقائي</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">(16:9) افتراضي</string> + <string name="ratio_force_four_three">4:3 فرض</string> + <string name="ratio_force_twenty_one_nine">21:9 فرض</string> + <string name="ratio_force_sixteen_ten">16:10 فرض</string> + <string name="ratio_stretch">تمتد إلى النافذة</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">دقه</string> + <string name="cpu_accuracy_unsafe">غير آمن</string> + <string name="cpu_accuracy_paranoid">Paranoid (Slow)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">أزرار الاتجاهات</string> + <string name="gamepad_left_stick">العصا اليسرى</string> + <string name="gamepad_right_stick">العصا اليمنى</string> + <string name="gamepad_home">شاشة الإستقبال</string> + <string name="gamepad_screenshot">لقطة شاشة</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">تحضير التظليل</string> + <string name="building_shaders">بناء التظليل</string> + + <!-- Theme options --> + <string name="change_app_theme">تغيير سمة التطبيق</string> + <string name="theme_default">افتراضي</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">تغيير وضع السمة</string> + <string name="theme_mode_follow_system">اتبع النظام</string> + <string name="theme_mode_light">فاتح</string> + <string name="theme_mode_dark">غامق</string> + + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">خلفيات سوداء</string> + <string name="use_black_backgrounds_description">عند استخدام المظهر الداكن، قم بتطبيق خلفيات سوداء.</string> + + <!-- Picture-In-Picture --> + <string name="picture_in_picture">صورة داخل صورة</string> + <string name="picture_in_picture_description">تصغير النافذة عند وضعها في الخلفية</string> + <string name="pause">توقف</string> + <string name="play">تشغيل</string> + <string name="mute">كتم</string> + <string name="unmute">إلغاء الكتم</string> + + <!-- Licenses screen strings --> + <string name="licenses">التراخيص</string> + <string name="license_fidelityfx_fsr_description">AMD ترقية عالية الجودة من</string> + </resources> diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml new file mode 100644 index 000000000..d2e5fee19 --- /dev/null +++ b/src/android/app/src/main/res/values-ckb/strings.xml @@ -0,0 +1,336 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + + <string name="app_disclaimer">ئەم نەرمەکاڵایە یارییەکانی کۆنسۆلی نینتێندۆ سویچ کارپێدەکات. هیچ ناونیشانێکی یاری و کلیلی تێدا نییە..<br /><br />پێش ئەوەی دەست پێ بکەیت، تکایە شوێنی فایلی <![CDATA[<b> prod.keys </b>]]> دیاریبکە لە نێو کۆگای ئامێرەکەت.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">زیاتر فێربە</a>]]></string> + <string name="emulation_notification_channel_name">ئیمولەیشن کارایە</string> + <string name="emulation_notification_channel_description">ئاگادارکردنەوەیەکی بەردەوام نیشان دەدات کاتێک ئیمولەیشن کاردەکات.</string> + <string name="emulation_notification_running">یوزو کاردەکات</string> + <string name="notice_notification_channel_name">ئاگاداری و هەڵەکان</string> + <string name="notice_notification_channel_description">ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت.</string> + <string name="notification_permission_not_granted">مۆڵەتی ئاگادارکردنەوە نەدراوە!</string> + + <!-- Setup strings --> + <string name="welcome">بەخێربێیت!</string> + <string name="welcome_description">فێربە چۆن <b>yuzu</b> ڕێکبخەیت و بچییە ناو ئیمولەیشن.</string> + <string name="get_started">دەست پێبکە</string> + <string name="keys">کلیلەکان</string> + <string name="keys_description">فایلی <b>prod.keys</b> هەڵبژێرە بە دوگمەی خوارەوە.</string> + <string name="select_keys">کلیلەکان هەڵبژێرە</string> + <string name="games">یاریەکان</string> + <string name="games_description">فۆڵدەری <b>Games</b> هەڵبژێرە بە دوگمەی خوارەوە.</string> + <string name="done">تەواو</string> + <string name="done_description">تۆ تەواو ئامادەیت.\nچێژ لە یارییەکانت وەربگرە!</string> + <string name="text_continue">بەردەوام بوون</string> + <string name="next">دواتر</string> + <string name="back">گەڕانەوە</string> + <string name="add_games">زیادکردنی یاری</string> + <string name="add_games_description">فۆڵدەری یارییەکانت هەڵبژێرە</string> + <!-- Home strings --> + <string name="home_games">یاریەکان</string> + <string name="home_search">گەڕان</string> + <string name="home_settings">ڕێکخستنەکان</string> + <string name="empty_gamelist">تا ئێستا هیچ فایلێک نەدۆزراوەتەوە یان هیچ ناونیشانێکی یاری هەڵنەبژێردراوە.</string> + <string name="search_and_filter_games">گەڕان و فلتەرکردنی یارییەکان</string> + <string name="select_games_folder">فۆڵدەری یارییەکان هەڵبژێرە</string> + <string name="select_games_folder_description">ڕێگە بە یوزو دەدات بۆ پڕکردنەوەی لیستی یارییەکان</string> + <string name="add_games_warning">هەڵبژاردنی فۆڵدەری یارییەکان تێپەڕدەکەیت؟</string> + <string name="add_games_warning_description">یارییەکان لە لیستی یارییەکاندا پیشان نادرێن ئەگەر فۆڵدەرێک هەڵنەبژێردرێت.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">گەڕان بەدوای یارییەکاندا</string> + <string name="games_dir_selected">ناونیشانی یارییەکان هەڵبژێردرا</string> + <string name="install_prod_keys">دابمەزرێنە prod.keys</string> + <string name="install_prod_keys_description">پێویستە بۆ کۆدکردنەوەى یارییە تاکەکەسییەکان</string> + <string name="install_prod_keys_warning">زیادکردنی کلیلەکان تێپەڕدەکەیت؟</string> + <string name="install_prod_keys_warning_description">کلیلی دروست پێویستە بۆ وەرگرتنی یارییەکانی تاکەکەسی. تەنها ئەپەکانی homebrew کاردەکەن ئەگەر بەردەوام بیت.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">ئاگادارکردنەوەکان</string> + <string name="notifications_description">بە دوگمەی خوارەوە مۆڵەتی ئاگادارکردنەوەکە بدە.</string> + <string name="give_permission">مۆڵەت بدە</string> + <string name="notification_warning">پێدانی مۆڵەتی ئاگادارکردنەوە تێپەڕدەکەیت؟</string> + <string name="notification_warning_description">یوزو ناتوانێت لە زانیاری گرنگ ئاگادارت بکاتەوە.</string> + <string name="permission_denied">مۆڵەت پێدان ڕەتکرایەوە</string> + <string name="permission_denied_description">زۆر جار ئەم مۆڵەتەت ڕەتکردۆتەوە و ئێستا دەبێت بە دەستی ڕێگەپێدان بکەیت لە ڕێکخستنەکانی سیستەمدا.</string> + <string name="about">دەربارە</string> + <string name="about_description">وەشانی دروستکردن، بیتبێن و زۆر شتیتر</string> + <string name="warning_help">یارمەتی</string> + <string name="warning_skip">پەڕاندن</string> + <string name="warning_cancel">ڕەتکردنەوە</string> + <string name="install_amiibo_keys">دامەزراندنی کلیلی Amiibo</string> + <string name="install_amiibo_keys_description">پێویستە بۆ بەکارهێنانی Amiibo لە یاریدا</string> + <string name="invalid_keys_file">فایلی کلیلێکی نادروست هەڵبژێردرا</string> + <string name="install_keys_success">کلیلەکان بە سەرکەوتوویی دامەزران</string> + <string name="reading_keys_failure">هەڵە لە خوێندنەوەی کۆدکردنی کلیل</string> + <string name="install_prod_keys_failure_extension_description">دڵنیابەوە کە فایلی کلیلەکانت درێژکراوەی .keys ی هەیە و دووبارە هەوڵبدەرەوە.</string> + <string name="install_amiibo_keys_failure_extension_description">دڵنیابە کە فایلی کلیلەکانت درێژکراوەی .bin ی هەیە و دووبارە هەوڵبدەرەوە.</string> + <string name="invalid_keys_error">کلیلی کۆدکردنی نادروستە</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">فایلە هەڵبژێردراوەکە هەڵەیە یان تێکچووە. تکایە دووبارە کلیلەکانت دەربێنەوە.</string> + <string name="install_gpu_driver">دامەزراندنی وەگەڕخەری GPU</string> + <string name="install_gpu_driver_description">دامەزراندنی وەگەڕخەری بەدیل بۆ ئەوەی بە ئەگەرێکی زۆرەوە کارایی باشتر یان وردبینی هەبێت</string> + <string name="advanced_settings">ڕێکخستنە پێشکەوتووەکان</string> + <string name="settings_description">سازدانی ڕێکخستنەکانی ئیمولەیتەر</string> + <string name="search_recently_played">بەم دواییە یاری کردووە</string> + <string name="search_recently_added">بەم دواییە زیادکرا</string> + <string name="search_retail">بەتاک</string> + <string name="search_homebrew">هۆم بریو</string> + <string name="open_user_folder">کردنەوەی فۆڵدەری یوزو</string> + <string name="open_user_folder_description">بەڕێوەبردنی فایلە ناوخۆییەکانی یوزو</string> + <string name="theme_and_color_description">دەستکاری کردنی شێوازی ئەپەکە</string> + <string name="no_file_manager">هیچ فایل بەڕێوەبەرێک نەدۆزرایەوە</string> + <string name="notification_no_directory_link">نەتوانرا ناونیشانی یوزو بکرێتەوە</string> + <string name="notification_no_directory_link_description">تکایە شوێنی فۆڵدەری بەکارهێنەر لەگەڵ پانێڵی لایەنی فایل بەڕێوەبارەکان بە دەست بدۆزەرەوە.</string> + <string name="manage_save_data">بەڕێوەبردنی داتای پاشەکەوتکراو</string> + <string name="manage_save_data_description">داتای پاشەکەوتکراو دۆزراوە. تکایە لە خوارەوە بژاردەیەک هەڵبژێرە.</string> + <string name="import_export_saves_description">هاوردەکردن یان هەناردەکردنی فایلی پاشەکەوتکراو</string> + <string name="save_file_imported_success">بە سەرکەوتوویی هاوردە کرا</string> + <string name="save_file_invalid_zip_structure">پێکهاتەی شوێنی پاشەکەوتکراو نادروستە</string> + <string name="save_file_invalid_zip_structure_description">ناوی یەکەمی فۆڵدەر دەبێت ناسنامەی ناونیشانی یارییەکە بێت.</string> + <string name="import_saves">هاوردەکردن</string> + <string name="export_saves">هەناردەکردن</string> + <string name="install_firmware">دامەزراندنی پتەوواڵا</string> + <string name="install_firmware_description">پتەوواڵا دەبێت لە ئەرشیفی زیپدا بێت و پێویستە بۆ بووتکردنی هەندێک یاری</string> + <string name="firmware_installing">دامەزرانی پتەوواڵا</string> + <string name="firmware_installed_success">پتەوواڵا بە سەرکەوتوویی دامەزرا</string> + <string name="firmware_installed_failure">دامەزراندنی پتەوواڵا شکستی هێنا</string> + <string name="share_log">هاوبەشی پێکردنی لۆگەکانی چاککردنەوە</string> + <string name="share_log_description">فایلە لۆگەکەی یوزو هاوبەش بکە بۆ چاککردنی کێشەکان</string> + <string name="share_log_missing">هیچ فایلێکی لۆگ نەدۆزراوە</string> + <string name="install_game_content">دامەزراندنی ناوەڕۆکی یاری</string> + <string name="install_game_content_description">دامەزراندنی نوێکاری یارییەکان یان DLC</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <!-- About screen strings --> + <string name="gaia_is_not_real">گایا ڕاستەقینە نییە</string> + <string name="copied_to_clipboard">کۆپی کرا بۆ تەختەی نووسین</string> + <string name="about_app_description">ئیمۆلیتەرێکی سەرچاوە-کراوەی سویچ</string> + <string name="contributors">بەشداربووان</string> + <string name="contributors_description">دروستکراوە لەگەڵ \u2764 لەلایەن تیمەکەی یوزو</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">ئەو پڕۆژانەی کە یوزوی بۆ ئەندرۆید ڕەخساند</string> + <string name="build">بونیات</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">بەزوویی دەسپێگەشتن</string> + <string name="get_early_access">بەدەستهێنانی بەزوویی دەسپێگەشتن</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">تایبەتمەندییە پێشکەوتووەکان، بەزوویی دەستگەیشتن بە نوێکارییەکان و زۆر شتی تر</string> + <string name="early_access_benefits">سوودەکانی بەزوویی دەسپێگەشتن</string> + <string name="cutting_edge_features">تایبەتمەندییە پێشکەوتووەکان</string> + <string name="early_access_updates">زوو دەستگەیشتن بە نوێکارییەکان</string> + <string name="no_manual_installation">چیتر دامەزراندنی دەستی نییە</string> + <string name="prioritized_support">پشتگیری لە پێشینە</string> + <string name="helping_game_preservation">یارمەتیدانی پاراستنی یارییەکان</string> + <string name="our_eternal_gratitude">سوپاس و پێزانینی هەمیشەییمان</string> + <string name="are_you_interested">ئایا تۆ خوازیاریت؟</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">سنووردارکردنی خێرایی</string> + <string name="frame_limit_enable_description">خێرایی ئیمولەیشن سنووردار دەکات بۆ ڕێژەیەکی دیاریکراو لە خێرایی ئاسایی.</string> + <string name="frame_limit_slider">سنووردارکردنی لەسەدای خێرایی</string> + <string name="frame_limit_slider_description">ڕێژەی سەدی دیاری دەکات بۆ سنووردارکردنی خێرایی ئیمولەیشن. 100% خێرایی ئاساییە. بەهایی بەرزتر یان نزمتر دەبێتە هۆی زیاد یان کەمکردنەوەی سنووری خێرایی.</string> + <string name="cpu_accuracy">وردی CPU</string> + <!-- System settings strings --> + <string name="use_docked_mode">دۆخی دۆککراو</string> + <string name="use_docked_mode_description">ڕوونی زیاد دەکات، کارایی کەم دەکاتەوە. دۆخی دەستی بەکاردێت کاتێک لەکاردەخرێت، ئەمەش ڕوونی دادەبەزێنێت و کارایی زیاد دەکات.</string> + <string name="emulated_region">ناوچەی ئیمولەیشن</string> + <string name="emulated_language">زمانی ئیمولەیتەر</string> + <string name="select_rtc_date">هەڵبژاردنی بەرواری RTC</string> + <string name="select_rtc_time">هەڵبژاردنی کاتی RTC</string> + <string name="use_custom_rtc">RTCی تایبەتمەند</string> + <string name="use_custom_rtc_description">ڕێگەت پێدەدات کاتژمێرێکی کاتی ڕاستەقینەی تایبەتمەند دابنێیت کە جیاوازە لە کاتی ئێستای سیستەمەکەت.</string> + <string name="set_custom_rtc">دانانی RTCی تایبەتمەند</string> + + <!-- Graphics settings strings --> + <string name="renderer_accuracy">ئاستی وردبینی</string> + <string name="renderer_resolution">ڕوونی (دۆخی دەستی/دۆخی دۆک)</string> + <string name="renderer_vsync">دۆخی VSync</string> + <string name="renderer_aspect_ratio">ڕێژەی ڕووبەری شاشە</string> + <string name="renderer_scaling_filter">فلتەری گونجاندنی پەنجەرە</string> + <string name="renderer_anti_aliasing">شێوازی دژە-خاوڕۆیی</string> + <string name="renderer_force_max_clock">ناچاریکردن بۆ زۆرترین کاتژمێر (تەنها ئەدرینۆ)</string> + <string name="renderer_force_max_clock_description">GPU ناچار دەکات بە زۆرترین کاتژمێر کاربکات (هێشتا سنووردارکردنی گەرمی جێبەجێ دەکرێت).</string> + <string name="renderer_asynchronous_shaders">بەکارهێنانی سێبەری ناهاوسەنگ</string> + <string name="renderer_asynchronous_shaders_description">سێبەرەکان بە شێوەیەکی ناهاوسەنگ کۆدەکاتەوە، پچڕپچڕی کەمدەکاتەوە بەڵام لەوانەیە گلێچ دروستکا.</string> + <string name="renderer_reactive_flushing">بەکارهێنانی بەرپێچدەرەوە</string> + <string name="renderer_reactive_flushing_description">وردی ڕێندەرکردن لە هەندێک یاریدا باشتر دەکات لەسەر تێچووی کارایی.</string> + <string name="use_disk_shader_cache">بیرگەخێرای سێبەری دیسک</string> + <string name="use_disk_shader_cache_description">پچڕپچڕی کەمدەکاتەوە بە هەڵگرتن و بارکردنی سێبەری دروستکراو لە ناوخۆدا.</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="renderer_api">API گرافیک</string> + <string name="renderer_debug">چاککردنەوەی گرافیک</string> + <string name="renderer_debug_description">API ی گرافیکەکان ڕێکدەخات بۆ دۆخی چاککردنی خاو.</string> + <string name="audio_volume">قەبارەی دەنگی</string> + <string name="audio_volume_description">دیاریکردنی قەبارەی دەنگی دەرچووی بیستۆک و بزوێنەری دەنگی دەرەکی.</string> + + <!-- Miscellaneous --> + <string name="slider_default">بنەڕەت</string> + <string name="ini_saved">ڕێکخستنە پاشەکەوتکراوەکان</string> + <string name="gameid_saved">ڕێکخستنە پاشەکەوتکراوەکان بۆ %1$s</string> + <string name="error_saving">هەڵە لە پاشەکەوتکردن %1$s.ini: %2$s</string> + <string name="loading">بارکردن...</string> + <string name="reset_setting_confirmation">ئایا دەتەوێت ئەم ڕێکخستنە بگەڕێنیتەوە بۆ بەهای بنەڕەتی خۆی؟</string> + <string name="reset_to_default">دوبارە ڕێکخستنەوەی بۆ بنەڕەت</string> + <string name="reset_all_settings">هەموو ڕێکخستنەکان دوبارە ڕێک دەخاتەوە؟</string> + <string name="reset_all_settings_description">هەموو ڕێکخستنە پێشکەوتووەکان دەگەڕێنەوە بۆ ڕێکخستنی بنەڕەتی خۆیان. پاشگەز بوونەوەی نییه.</string> + <string name="settings_reset">دوبارە ڕێککردنەوەی ڕێکخستنەکان</string> + <string name="close">داخستن</string> + <string name="learn_more">زیاتر فێربە</string> + <string name="auto">خودکار</string> + <string name="submit">پێشکەشکردن</string> + <string name="string_import">هاوردەکردن</string> + <string name="export">هەناردەکردن</string> + <!-- GPU driver installation --> + <string name="select_gpu_driver">هەڵبژاردنی وەگەڕخەری GPU</string> + <string name="select_gpu_driver_title">حەز دەکەیت وەگەڕخەری GPU ی ئێستات بگۆڕیت؟</string> + <string name="select_gpu_driver_install">دامەزراندن</string> + <string name="select_gpu_driver_default">بنەڕەت</string> + <string name="select_gpu_driver_use_default">بەکارهێنانی وەگەڕخەری GPU ی بنەڕەت</string> + <string name="select_gpu_driver_error">وەگەڕخەری نادروست هەڵبژێردرا، بە بەکارهێنانی بنەڕەتی سیستەم!</string> + <string name="system_gpu_driver">وەگەڕخەری GPU ی سیستەم</string> + <string name="installing_driver">دامەزراندنی وەگەڕخەر...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">ڕێکخستنەکان</string> + <string name="preferences_general">گشتی</string> + <string name="preferences_system">سیستەم</string> + <string name="preferences_graphics">گرافیک</string> + <string name="preferences_audio">دەنگ</string> + <string name="preferences_theme">ڕەنگ و ڕووکار</string> + <string name="preferences_debug">چاککردنەوە</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">ڕۆمەکەت کۆدکراوە</string> + <string name="loader_error_encrypted_keys_description"><![CDATA[تکایە دڵنیابەوە لەدامەزراوی <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> فایلەکەت بۆ ئەوەی بتوانرێت یارییەکان کۆد بکرێنەوە.]]></string> + <string name="loader_error_video_core">هەڵەیەک لە دەستپێکردنی ناوەکی ڤیدیۆکەدا ڕوویدا</string> + <string name="loader_error_video_core_description">ئەمەش بەزۆری بەهۆی وەگەڕخەرێکی ناتەبای GPU ەوەیە. دامەزراندنی وەگەڕخەری GPU ی تایبەتمەندکراو لەوانەیە ئەم کێشەیە چارەسەر بکات.</string> + <string name="loader_error_invalid_format">ناتوانرێت ڕۆم باربکرێت</string> + <string name="loader_error_file_not_found">فایلی ڕۆم بوونی نییە</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">دەرچوون لە ئیمولەیشن</string> + <string name="emulation_done">تەواو</string> + <string name="emulation_fps_counter">FPS ژمێر</string> + <string name="emulation_toggle_controls">گۆڕینی کۆنتڕۆڵ</string> + <string name="emulation_rel_stick_center">ناوەندی گێڕ بەنزیکەیی</string> + <string name="emulation_dpad_slide">خلیسکانی 4 دوگمەکە</string> + <string name="emulation_haptics">لەرینەوەی پەنجەلێدان</string> + <string name="emulation_show_overlay">نیشاندانی داپۆشەر</string> + <string name="emulation_toggle_all">گۆڕینی سەرجەم</string> + <string name="emulation_control_adjust">ڕێکخستنی داپۆشەر</string> + <string name="emulation_control_scale">پێوەر</string> + <string name="emulation_control_opacity">ڕوونی</string> + <string name="emulation_touch_overlay_reset">دووبارە ڕێکخستنەوەی داپۆشەر</string> + <string name="emulation_touch_overlay_edit">دەستکاریکردنی داپۆشەر</string> + <string name="emulation_pause">وەستاندنی ئیمولەیشن</string> + <string name="emulation_unpause">لادانی وەستاندنی ئیمولەیشن</string> + <string name="emulation_input_overlay">هەڵبژاردەکانی داپۆشەر</string> + + <string name="load_settings">بارکردنی ڕێکخستنەکان...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">کیبۆردی نەرمەکاڵا</string> + + <!-- Errors and warnings --> + <string name="abort_button">دەربارە</string> + <string name="continue_button">بەردەوام بوون</string> + <string name="system_archive_not_found">ئەرشیفی سیستەم نەدۆزراوە</string> + <string name="system_archive_not_found_message">%s دیار نییە. تکایە ئەرشیفی سیستەمەکەت فڕێ بدە.\nبەردەوامی ئیمولەیشن لەوانەیە ببێتە هۆی تێکچوون و فڕێدانەدەرەوە.</string> + <string name="system_archive_general">ئەرشیفێکی سیستەم</string> + <string name="save_load_error">هەڵەی پاشەکەوتکردن/بارکردن</string> + <string name="fatal_error">هەڵەی کوشندە</string> + <string name="fatal_error_message">هەڵەیەکی کوشندە ڕوویدا. بۆ وردەکارییەکان لۆگەکە بپشکنە.\nبەردەوامی ئیمولەیشن لەوانەیە ببێتە هۆی تێکچوون و فڕێدانەدەرەوە.</string> + <string name="performance_warning">کوژاندنەوەی ئەم ڕێکخستنە دەبێتە هۆی کەمکردنەوەی کارایی ئیمولەیشن! بۆ باشترین ئەزموون، باشترە ئەم ڕێکخستنە چالاک بهێڵیتەوە.</string> + <!-- Region Names --> + <string name="region_japan">ژاپۆن</string> + <string name="region_usa">ئەمریکا</string> + <string name="region_europe">ئەورووپا</string> + <string name="region_australia">ئوسترالیا</string> + <string name="region_china">چین</string> + <string name="region_korea">کۆریا</string> + <string name="region_taiwan">تایوان</string> + + <string name="memory_gigabyte">GB</string> + <!-- Renderer APIs --> + <string name="renderer_vulkan">ڤوڵکان</string> + <string name="renderer_none">هیچ</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">ئاسایی</string> + <string name="renderer_accuracy_high">بەرز</string> + <string name="renderer_accuracy_extreme">ئەوپەڕ (خاو)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (خاو)</string> + <string name="resolution_three">3X (2160p/3240p) (خاو)</string> + <string name="resolution_four">4X (2880p/4320p) (خاو)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">دەستبەجێ (کوژاوە)</string> + <string name="renderer_vsync_mailbox">سندوقی پۆستە</string> + <string name="renderer_vsync_fifo">FIFO (پێکراو)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO ئارام</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">نزیکترین دراوسێ</string> + <string name="scaling_filter_bilinear">دوو هێڵی</string> + <string name="scaling_filter_bicubic">دووخشتەکی</string> + <string name="scaling_filter_gaussian">گاوسی</string> + <string name="scaling_filter_scale_force">پێوەرهێز</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ سوپەر ووردبینی</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">هیچ</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <string name="screen_layout_auto">خودکار</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">بنەڕەت (16:9)</string> + <string name="ratio_force_four_three">ڕووبەری 4:3</string> + <string name="ratio_force_twenty_one_nine">ڕووبەری 21:9</string> + <string name="ratio_force_sixteen_ten">ڕووبەری 16:10</string> + <string name="ratio_stretch">کشانی پڕ بەشاشە</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">وورد</string> + <string name="cpu_accuracy_unsafe">ناسەقامگیر</string> + <string name="cpu_accuracy_paranoid">بەگومان (خاو)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">4 دوگمەکە</string> + <string name="gamepad_left_stick">گێڕی چەپ</string> + <string name="gamepad_right_stick">گێڕی ڕاست</string> + <string name="gamepad_home">ماڵەوە</string> + <string name="gamepad_screenshot">وێنەگرتنی شاشە</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">ئامادەکردنی سێبەرەکان</string> + <string name="building_shaders">دروستکردنی سێبەرەکان</string> + + <!-- Theme options --> + <string name="change_app_theme">گۆڕینی ڕووکاری ئەپەکە</string> + <string name="theme_default">بنەڕەت</string> + <string name="theme_material_you">کەرەستەی تۆ</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">گۆڕینی دۆخی ڕووکار</string> + <string name="theme_mode_follow_system">پەیڕەوی کردنی سیستەم</string> + <string name="theme_mode_light">ڕوناکی</string> + <string name="theme_mode_dark">تاریک</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">پاشبنەمای ڕەش</string> + <string name="use_black_backgrounds_description">لە کاتی بەکارهێنانی ڕووکاری تاریکدا، پاشبنەمای ڕەش دادەنێ.</string> + + <!-- Licenses screen strings --> + <string name="licenses">مۆڵەتەکان</string> + <string name="license_fidelityfx_fsr_description">بەرزکردنەوەی کوالێتی بەرز لە کۆمپانیای AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 72a47fbdb..9c6590b5e 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.<br /><br />Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string> <string name="emulation_notification_channel_name">Emulation ist aktiv</string> @@ -25,6 +25,7 @@ <string name="back">Zurück</string> <string name="add_games">Spiele hinzufügen</string> <string name="add_games_description">Spieleverzeichnis auswählen</string> + <string name="step_complete">Fertig!</string> <!-- Home strings --> <string name="home_games">Spiele</string> @@ -38,6 +39,7 @@ <string name="add_games_warning_description">Spiele werden in der Spieleliste nicht angezeigt, wenn kein Ordner ausgewählt ist.</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> <string name="home_search_games">Spiele suchen</string> + <string name="search_settings">Einstellungen suchen</string> <string name="games_dir_selected">Spieleverzeichnis ausgewählt</string> <string name="install_prod_keys">prod.keys installieren</string> <string name="install_prod_keys_description">Zum Entschlüsseln von Spielen benötigt</string> @@ -60,8 +62,11 @@ <string name="invalid_keys_file">Ungültige Schlüsseldatei ausgewählt</string> <string name="install_keys_success">Schlüssel erfolgreich installiert</string> <string name="reading_keys_failure">Fehler beim Lesen der Schlüssel</string> + <string name="install_prod_keys_failure_extension_description">Überprüfen Sie, ob Ihre Schlüsseldatei die Erweiterung \".keys\" hat, und versuchen Sie es erneut.</string> + <string name="install_amiibo_keys_failure_extension_description">Überprüfen Sie, ob Ihre Schlüsseldatei die Erweiterung \".bin\" hat, und versuchen Sie es erneut.</string> <string name="invalid_keys_error">Ungültige Schlüssel</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">Die ausgewählte Datei ist falsch oder beschädigt. Bitte kopieren Sie Ihre Schlüssel erneut.</string> <string name="install_gpu_driver">GPU-Treiber installieren</string> <string name="install_gpu_driver_description">Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren</string> <string name="advanced_settings">Erweiterte Einstellungen</string> @@ -84,7 +89,17 @@ <string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string> <string name="import_saves">Importieren</string> <string name="export_saves">Exportieren</string> - + <string name="install_firmware">Firmware installieren</string> + <string name="install_firmware_description">Die Firmware muss in einem ZIP-Archiv vorliegen und wird zum Booten einiger Spiele benötigt</string> + <string name="firmware_installing">Firmware wird installiert</string> + <string name="firmware_installed_success">Die Firmware wurde erfolgreich installiert!</string> + <string name="firmware_installed_failure">Bei der Firmware installation ist etwas fehlgeschlagen.</string> + <string name="share_log">Debug-Logs teilen</string> + <string name="share_log_description">Debug-Logs an yuzu zur Untersuchung absenden</string> + <string name="share_log_missing">Keine Log-Datei gefunden</string> + <string name="install_game_content">Spiel installieren</string> + <string name="install_game_content_description">Spiel Update oder DLC installieren</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia ist nicht real</string> <string name="copied_to_clipboard">In die Zwischenablage kopiert</string> @@ -92,7 +107,10 @@ <string name="contributors">Beitragende</string> <string name="contributors_description">Gemacht mit \u2764 vom yuzu Team</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Projekte, die yuzu für Android möglich machen </string> <string name="build">Build</string> + <string name="user_data">Nutzerdaten</string> + <string name="user_data_export_cancelled">Export abgebrochen</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> @@ -107,45 +125,39 @@ <string name="early_access_updates">Früherer Zugriff auf Updates</string> <string name="no_manual_installation">Keine manuelle Installation</string> <string name="prioritized_support">Priorisierte Unterstützung</string> + <string name="helping_game_preservation">Beitrag zur Erhaltung der Spiele</string> <string name="our_eternal_gratitude">Unsere ewige Dankbarkeit</string> <string name="are_you_interested">Bist du interessiert?</string> <!-- General settings strings --> - <string name="frame_limit_enable">Geschwindigkeitsbegrenzung aktivieren</string> - <string name="frame_limit_enable_description">Wenn aktiviert, wird die Emulationsgeschwindigkeit auf einen Prozentsatz der normalen Geschwindigkeit begrenzt.</string> + <string name="frame_limit_enable">Limitierte Geschwindigkeit</string> + <string name="frame_limit_enable_description">Limitiert die Geschwindigkeit auf einen von dir festgelegten Prozentsatz.</string> <string name="frame_limit_slider">Geschwindkeitsbegrenzung in Prozent</string> - <string name="frame_limit_slider_description">Legt den Prozentsatz der Bergrenzung der Emulationsgeschwindigkeit fest. Mit dem Standardwert von 100% wird die Emulation auf die normale Geschwindigkeit begrenzt. Höhere oder niedrigere Werte erhöhen oder verringern die Geschwindigkeitsbegrenzung.</string> + <string name="frame_limit_slider_description">Gibt die prozentuale Geschwindigkeit der Emulation an. 100% sind normal. Werte darüber oder drunter werden die Geschwindigkeit entsprechend verändern.</string> <string name="cpu_accuracy">CPU-Genauigkeit</string> - <!-- System settings strings --> - <string name="use_docked_mode">Dock-Modus</string> - <string name="use_docked_mode_description">Emuliert im Dock-Modus, was die Auflösung verbessert, aber die Leistung senkt.</string> + <string name="use_docked_mode">Gedockter Modus</string> + <string name="use_docked_mode_description">Der Docked Modus erhöht die Auflösung, verringert die aber die Leistung. Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die Leistung.</string> <string name="emulated_region">Emulierte Region</string> <string name="emulated_language">Emulierte Sprache</string> <string name="select_rtc_date">RTC-Datum auswählen</string> <string name="select_rtc_time">RTC-Zeit auswählen</string> - <string name="use_custom_rtc">Benutzerdefinierte RTC aktivieren</string> - <string name="use_custom_rtc_description">Mit dieser Einstellung kann eine benutzerdefinierte Echtzeituhr unabhängig von der aktuellen Systemzeit verwendet werden.</string> - <string name="set_custom_rtc">Benutzerdefinierte RTC einstellen</string> - + <string name="use_custom_rtc">Benutzerdefinierte Echtzeituhr</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">Genauigkeitsstufe</string> - <string name="renderer_resolution">Auflösung</string> <string name="renderer_vsync">VSync-Modus</string> + <string name="renderer_screen_layout">Orientierung</string> <string name="renderer_aspect_ratio">Seitenverhältnis</string> <string name="renderer_scaling_filter">Fensteranpassungsfilter</string> - <string name="renderer_anti_aliasing">Kantenglättungs-Methode</string> <string name="renderer_force_max_clock">Maximale Taktfrequenz erzwingen (nur Adreno)</string> <string name="renderer_force_max_clock_description">Erzwingt den Betrieb der GPU mit der maximal möglichen Taktfrequenz (Temperaturbeschränkungen werden weiterhin angewendet).</string> <string name="renderer_asynchronous_shaders">Asynchrone Shader nutzen</string> - <string name="renderer_asynchronous_shaders_description">Kompiliert Shader asynchron, was Ruckler reduziert, aber zu Glitches führen kann.</string> - <string name="renderer_debug">Grafik-Debugging aktivieren</string> - <string name="renderer_debug_description">Wenn aktiviert, schaltet die Grafik-API in einen langsameren Debugging-Modus.</string> - <string name="use_disk_shader_cache">Nutze Festplatten-Shader-Cache</string> - <string name="use_disk_shader_cache_description">Ruckeln wird durch das Speichern und Laden von generierten Shadern auf der Festplatte reduziert.</string> - - <!-- Audio settings strings --> + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">CPU Debugging</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">Graphik-Debugging</string> <string name="audio_volume">Lautstärke</string> <string name="audio_volume_description">Legt die Lautstärke der Audioausgabe fest.</string> @@ -154,14 +166,22 @@ <string name="ini_saved">Einstellungen gespeichert</string> <string name="gameid_saved">Einstellungen für %1$s gespeichert</string> <string name="error_saving">Fehler beim Speichern von %1$s.ini: %2$s</string> + <string name="unimplemented_menu">Unimplementiertes Menü</string> <string name="loading">Lädt...</string> <string name="reset_setting_confirmation">Möchtest du diese Einstellung auf den Standardwert zurücksetzen?</string> <string name="reset_to_default">Auf Standard zurücksetzen</string> <string name="reset_all_settings">Alle Einstellungen zurücksetzen?</string> - <string name="reset_all_settings_description">Alle erweiterten Einstellungen werden auf ihren Standardwert zurückgesetzt. Dies kann nicht rückgängig gemacht werden.</string> <string name="settings_reset">Einstellungen zurückgesetzt</string> <string name="close">Schließen</string> <string name="learn_more">Mehr erfahren</string> + <string name="auto">Auto</string> + <string name="submit">Absenden</string> + <string name="string_null">Null</string> + <string name="string_import">Importieren</string> + <string name="export">Exportieren</string> + <string name="export_failed">Export fehlgeschlagen</string> + <string name="import_failed">Import fehlgeschlagen</string> + <string name="cancelling">Abbrechen</string> <!-- GPU driver installation --> <string name="select_gpu_driver">GPU-Treiber auswählen</string> @@ -169,6 +189,7 @@ <string name="select_gpu_driver_install">Installieren</string> <string name="select_gpu_driver_default">Standard</string> <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string> + <string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string> <string name="system_gpu_driver">System GPU-Treiber</string> <string name="installing_driver">Treiber wird installiert...</string> @@ -179,6 +200,7 @@ <string name="preferences_graphics">Grafik</string> <string name="preferences_audio">Audio</string> <string name="preferences_theme">Theme und Farbe</string> + <string name="preferences_debug">Debug</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">Das ROM ist verschlüsselt</string> @@ -192,22 +214,15 @@ <string name="emulation_exit">Emulation beenden</string> <string name="emulation_done">Fertig</string> <string name="emulation_fps_counter">FPS Zähler</string> - <string name="emulation_toggle_controls">Steuerung umschalten</string> - <string name="emulation_rel_stick_center">Relative Stick-Mitte</string> - <string name="emulation_dpad_slide">DPad Slide</string> - <string name="emulation_haptics">Haptik</string> - <string name="emulation_show_overlay">Overlay anzeigen</string> <string name="emulation_toggle_all">Alle umschalten</string> <string name="emulation_control_adjust">Overlay anpassen</string> <string name="emulation_control_scale">Größe</string> <string name="emulation_control_opacity">Transparenz</string> <string name="emulation_touch_overlay_reset">Overlay zurücksetzen</string> <string name="emulation_touch_overlay_edit">Overlay bearbeiten</string> - <string name="emulation_pause">Emulation pausieren</string> - <string name="emulation_unpause">Emulation fortsetzen</string> <string name="emulation_input_overlay">Overlay-Optionen</string> - <string name="load_settings">Lädt Einstellungen...</string> + <string name="load_settings">Lade Einstellungen...</string> <!-- Software keyboard --> <string name="software_keyboard">Software-Tastatur</string> @@ -221,7 +236,7 @@ <string name="fatal_error">Schwerwiegender Fehler</string> <string name="fatal_error_message">Ein schwerwiegender Fehler ist aufgetreten. Einzelheiten wurden im Log protokolliert.\nDas Fortsetzen der Emulation kann zu Abstürzen und Bugs führen.</string> <string name="performance_warning">Das Deaktivieren dieser Einstellung führt zu erheblichen Leistungsverlusten! Für ein optimales Erlebnis wird empfohlen, sie aktiviert zu lassen.</string> - + <string name="memory_formatted">%1$s %2$s</string> <!-- Region Names --> <string name="region_japan">Japan</string> <string name="region_usa">USA</string> @@ -231,6 +246,15 @@ <string name="region_korea">Korea</string> <string name="region_taiwan">Taiwan</string> + <!-- Memory Sizes --> + <string name="memory_byte">Byte</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> + <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> <string name="renderer_none">Keiner</string> @@ -267,12 +291,15 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <string name="screen_layout_portrait">Portrait</string> + <string name="screen_layout_auto">Auto</string> + <!-- Aspect Ratios --> <string name="ratio_default">Standard (16:9)</string> <string name="ratio_force_four_three">4:3 erzwingen</string> <string name="ratio_force_twenty_one_nine">21:9 erzwingen</string> <string name="ratio_force_sixteen_ten">Erzwinge 16:10</string> - <string name="ratio_stretch">Auf Fenster anpassen</string> + <string name="ratio_stretch">Auf Bildschirmgröße anpsassen</string> <!-- CPU Accuracy --> <string name="cpu_accuracy_accurate">Akkurat</string> @@ -280,9 +307,9 @@ <string name="cpu_accuracy_paranoid">Paranoid (Langsam)</string> <!-- Gamepad Buttons --> - <string name="gamepad_d_pad">Steuerkreuz</string> - <string name="gamepad_left_stick">Linker Analogstick</string> - <string name="gamepad_right_stick">Rechter Analogstick</string> + <string name="gamepad_d_pad">D-Pad</string> + <string name="gamepad_left_stick">Linker Stick</string> + <string name="gamepad_right_stick">Rechter Stick</string> <string name="gamepad_home">Home</string> <string name="gamepad_screenshot">Screenshot</string> @@ -291,18 +318,30 @@ <string name="building_shaders">Shader werden erstellt</string> <!-- Theme options --> - <string name="change_app_theme">App-Theme ändern</string> + <string name="change_app_theme">App-Thema ändern</string> <string name="theme_default">Standard</string> <string name="theme_material_you">Material You</string> <!-- Theme Modes --> - <string name="change_theme_mode">Theme-Modus ändern</string> + <string name="change_theme_mode">Themen-Modus ändern</string> <string name="theme_mode_follow_system">System folgen</string> <string name="theme_mode_light">Hell</string> <string name="theme_mode_dark">Dunkel</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">Schwarze Hintergünde verwenden</string> + <string name="use_black_backgrounds">Schwarze Hintergründe</string> <string name="use_black_backgrounds_description">Bei Verwendung des dunklen Themes, schwarze Hintergründe verwenden.</string> -</resources> + <!-- Picture-In-Picture --> + <string name="picture_in_picture">Bild im Bild</string> + <string name="pause">Pause</string> + <string name="mute">Stummschalten</string> + <string name="unmute">Ton aktivieren</string> + + <!-- Licenses screen strings --> + <string name="licenses">Lizenzen</string> + <string name="license_fidelityfx_fsr_description">Hochwertiges Upscaling von AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index e5bdd5889..103ac6e65 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> - <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o keys no vienen incluidos.<br /><br />Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string> + <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o claves no vienen incluidos.<br /><br />Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string> <string name="emulation_notification_channel_name">Emulación activa</string> <string name="emulation_notification_channel_description">Muestra una notificación persistente cuando la emulación está activa.</string> <string name="emulation_notification_running">yuzu esta ejecutándose</string> @@ -25,6 +25,7 @@ <string name="back">Atrás</string> <string name="add_games">Añadir Juegos</string> <string name="add_games_description">Selecciona la carpeta de juegos</string> + <string name="step_complete">¡Completado!</string> <!-- Home strings --> <string name="home_games">Juegos</string> @@ -37,7 +38,8 @@ <string name="add_games_warning">¿Omitir la selección de la carpeta de juegos?</string> <string name="add_games_warning_description">No se mostrará ningún juego si no se ha seleccionado una carpeta de juegos.</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> - <string name="home_search_games">Buscar Juegos</string> + <string name="home_search_games">Buscar juegos</string> + <string name="search_settings">Buscar configuración</string> <string name="games_dir_selected">Directorio de juegos seleccionado</string> <string name="install_prod_keys">Instalar prod.keys</string> <string name="install_prod_keys_description">Requerido para descifrar juegos</string> @@ -58,15 +60,18 @@ <string name="warning_cancel">Cancelar</string> <string name="install_amiibo_keys">Instalar clave de Amiiboo</string> <string name="install_amiibo_keys_description">Necesario para usar Amiibo en el juego</string> - <string name="invalid_keys_file">Archivo de claves inválido seleccionado</string> + <string name="invalid_keys_file">Archivo de claves seleccionado inválido</string> <string name="install_keys_success">Claves instaladas correctamente</string> <string name="reading_keys_failure">Error al leer las claves de cifrado</string> + <string name="install_prod_keys_failure_extension_description">Compruebe que el archivo de claves tenga una extensión .keys y pruebe otra vez.</string> + <string name="install_amiibo_keys_failure_extension_description">Compruebe que el archivo de claves tenga una extensión .bin y pruebe otra vez.</string> <string name="invalid_keys_error">Claves de cifrado no válidas</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">El archivo seleccionado es incorrecto o está corrupto. Vuelva a redumpear sus claves.</string> <string name="install_gpu_driver">Instalar driver de GPU</string> <string name="install_gpu_driver_description">Instale drivers alternativos para obtener un rendimiento o una precisión potencialmente mejores</string> <string name="advanced_settings">Opciones avanzadas</string> + <string name="advanced_settings_game">Configuración avanzada: %1$s</string> <string name="settings_description">Configurar las opciones del emulador</string> <string name="search_recently_played">Jugado recientemente</string> <string name="search_recently_added">Añadido recientemente</string> @@ -86,6 +91,33 @@ <string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string> <string name="import_saves">Importar</string> <string name="export_saves">Exportar</string> + <string name="install_firmware">Instalar firmware</string> + <string name="install_firmware_description">El firmware debe estar en un archivo ZIP y es necesario para ejecutar algunos juegos</string> + <string name="firmware_installing">Instalando firmware</string> + <string name="firmware_installed_success">Firmware instalado con éxito</string> + <string name="firmware_installed_failure">Falló la instalación de firmware</string> + <string name="firmware_installed_failure_description">Asegúrese de que los archivos nca del firmware estén en la raíz del zip e inténtelo de nuevo.</string> + <string name="share_log">Compartir registros de depuración</string> + <string name="share_log_description">Comparta el archivo de registro de yuzu para depurar problemas</string> + <string name="share_log_missing">No se encontró ningún archivo de registro</string> + <string name="install_game_content">Instalar contenido de juego</string> + <string name="install_game_content_description">Instalar actualizaciones o DLC</string> + <string name="installing_game_content">Instalando contenido...</string> + <string name="install_game_content_failure">Error instalando archivo(s) a la NAND</string> + <string name="install_game_content_failure_description">Asegúrese de que el/los contenido(s) son válidos y que el archivo prod.keys esté instalado.</string> + <string name="install_game_content_failure_base">La instalación de los juegos base no está permitida para así evitar posibles conflictos.</string> + <string name="install_game_content_failure_file_extension">Sólo hay soporte para el contenido en NSP y XCI. Asegúrese de que el/los contenido(s) son válidos.</string> + <string name="install_game_content_failed_count">%1$d error(es) de instalación</string> + <string name="install_game_content_success">Contenido(s) de juego instalado/s con éxito</string> + <string name="install_game_content_success_install">%1$d instalado con éxito</string> + <string name="install_game_content_success_overwrite">%1$d sobreescrito con éxito</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">Drivers personalizados no soportados</string> + <string name="custom_driver_not_supported_description">En estos momentos, la carga de drivers personalizados no está disponible para este dispositivo..\n¡Comprueba esta opción en el futuro para ver si ya está añadido el soporte a ese dispositivo!</string> + <string name="manage_yuzu_data">Administrar datos de yuzu</string> + <string name="manage_yuzu_data_description">Importa/exporta el firmware, las keys, los datos de usuario, ¡y más!</string> + <string name="share_save_file">Compartir archivo de guardado</string> + <string name="export_save_failed">La exportación del guardado falló</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia no es real</string> @@ -94,7 +126,18 @@ <string name="contributors">Contribuidores</string> <string name="contributors_description">Hecho con \u2764 del equipo yuzu</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Proyectos que hacen que yuzu para Android sea una realidad</string> <string name="build">Versión</string> + <string name="user_data">Datos de usuario</string> + <string name="user_data_description">Importa/exporta todos los datos de usuario.\n\nCuando se importen los datos de usuario, ¡los demás datos de usuario existentes serán borrados!</string> + <string name="exporting_user_data">Exportando datos de usuario...</string> + <string name="importing_user_data">Importando datos de usuario...</string> + <string name="import_user_data">Importar datos de usuario</string> + <string name="invalid_yuzu_backup">Backup de válido</string> + <string name="user_data_export_success">Datos de usuario exportados con éxito</string> + <string name="user_data_import_success">Datos de usuario importados con éxito</string> + <string name="user_data_export_cancelled">Exportación cancelada</string> + <string name="user_data_import_failed_description">Asegúrese de que las carpetas de datos de usuario estén en la raíz de la carpeta del zip y contengan un archivo config en config/config.ini e inténtelo de nuevo.</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> @@ -114,41 +157,53 @@ <string name="are_you_interested">¿Estás interesado?</string> <!-- General settings strings --> - <string name="frame_limit_enable">Activar limite de velocidad</string> - <string name="frame_limit_enable_description">Cuando está habilitado, la velocidad de emulación se limitará a un porcentaje específico de la velocidad normal.</string> + <string name="frame_limit_enable">Limitar velocidad</string> + <string name="frame_limit_enable_description">Limita la velocidad de emulación a un porcentaje específico de la velocidad normal.</string> <string name="frame_limit_slider">Limitar porcentaje de velocidad</string> - <string name="frame_limit_slider_description">Especifica el porcentaje para limitar la velocidad de emulación. Con el valor predeterminado del 100 %, la emulación se limitará a la velocidad normal. Valores más altos o más bajos aumentarán o disminuirán el límite de velocidad.</string> + <string name="frame_limit_slider_description">Especifica el porcentaje para limitar la velocidad de emulación. 100% es la velocidad normal. Valores más altos o bajos incrementarán o disminuirán el límite de velocidad.</string> <string name="cpu_accuracy">Precisión de CPU</string> + <string name="value_with_units">%1$s%2$s</string> <!-- System settings strings --> - <string name="use_docked_mode">Modo sobremesa</string> - <string name="use_docked_mode_description">Emula en modo sobremesa, lo que aumenta la resolución perjudicando el rendimiento.</string> + <string name="use_docked_mode">Modo Sobremesa</string> + <string name="use_docked_mode_description">Incrementa la resolución al coste de reducir el rendimiento. El Modo Portátil es usado cuando está desactivado, reduciendo la resolución y mejorando así el rendimiento.</string> <string name="emulated_region">Región emulada</string> <string name="emulated_language">Idioma emulado</string> - <string name="select_rtc_date">Seleccionar Fecha RTC</string> - <string name="select_rtc_time">Seleccionar Tiempo RTC</string> - <string name="use_custom_rtc">Habilitar RTC Personalizado</string> - <string name="use_custom_rtc_description">Esta configuración le permite configurar un reloj de tiempo real personalizado diferente a la hora actual de su sistema</string> - <string name="set_custom_rtc">Establecer RTC Personalizado</string> + <string name="select_rtc_date">Seleccionar fecha RTC</string> + <string name="select_rtc_time">Seleccionar tiempo RTC</string> + <string name="use_custom_rtc">RTC personalizado</string> + <string name="use_custom_rtc_description">Te permite tener un reloj personalizado en tiempo real diferente del tiempo del propio sistema.</string> + <string name="set_custom_rtc">Configurar RTC personalizado</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">Nivel de precisión</string> - <string name="renderer_resolution">Resolución</string> + <string name="renderer_resolution">Resolución (Portátil/Sobremesa)</string> <string name="renderer_vsync">Modo VSync</string> + <string name="renderer_screen_layout">Orientación</string> <string name="renderer_aspect_ratio">Relación de aspecto</string> <string name="renderer_scaling_filter">Filtro de adaptación de ventana</string> - <string name="renderer_anti_aliasing">Metodo Anti Aliasing</string> + <string name="renderer_anti_aliasing">Método anti-aliasing</string> <string name="renderer_force_max_clock">Forzar velocidad al máximo (solo Adreno)</string> <string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string> <string name="renderer_asynchronous_shaders">Usar shaders asíncronos</string> - <string name="renderer_asynchronous_shaders_description">Compila shaders de forma asincrónica, lo que reducirá los parones pero puede introducir fallos.</string> - <string name="renderer_debug">Habilitar la depuración de gráficos</string> - <string name="renderer_debug_description">Cuando esté marcado, la API de gráficos entra en un modo de depuración más lento.</string> - <string name="use_disk_shader_cache">Usar caché de shaders en disco</string> - <string name="use_disk_shader_cache_description">Reduzca los parones almacenando y cargando shaders generados en el disco.</string> + <string name="renderer_asynchronous_shaders_description">Compila shaders de manera asíncrona, reduciendo los parones, pero puede introducir fallos.</string> + <string name="renderer_reactive_flushing">Usar limpieza reactiva</string> + <string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string> + <string name="use_disk_shader_cache">Caché de shaders en disco</string> + <string name="use_disk_shader_cache_description">Reduce los parones almacenando y cargando shaders generados.</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">Depuración de CPU</string> + <string name="cpu_debug_mode_description">Pone la CPU en un modo de depuración lento.</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">Depuración de gráficos</string> + <string name="renderer_debug_description">Configura la API gráfica a un modo de depuración lento.</string> + <string name="fastmem">Fastmem</string> <!-- Audio settings strings --> + <string name="audio_output_engine">Motor de salida</string> <string name="audio_volume">Volumen</string> <string name="audio_volume_description">Especifica el volumen de la salida de audio.</string> @@ -157,14 +212,24 @@ <string name="ini_saved">Configuración guardada</string> <string name="gameid_saved">Configuración guardada para %1$s</string> <string name="error_saving">Error guardando %1$s.ini: %2$s</string> + <string name="unimplemented_menu">Menú sin implementar</string> <string name="loading">Cargando...</string> + <string name="shutting_down">Saliendo...</string> <string name="reset_setting_confirmation">¿Desea restablecer esta configuración a su valor predeterminado?</string> <string name="reset_to_default">Restablecer a predeterminado</string> <string name="reset_all_settings">¿Restablecer todas las configuraciones?</string> - <string name="reset_all_settings_description">Todas las configuraciones avanzadas se restablecerán a su configuración predeterminada. Esto no se puede deshacer.</string> + <string name="reset_all_settings_description">Todas las opciones avanzadas se restablecerán a su configuración predeterminada. Esta acción no se puede deshacer.</string> <string name="settings_reset">Reiniciar la configuracion</string> <string name="close">Cerrar</string> - <string name="learn_more">Más información</string> + <string name="learn_more">Saber más</string> + <string name="auto">Auto</string> + <string name="submit">Enviar</string> + <string name="string_null">Null</string> + <string name="string_import">Importar</string> + <string name="export">Exportar</string> + <string name="export_failed">La exportación falló</string> + <string name="import_failed">La importación falló</string> + <string name="cancelling">Cancelando</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Seleccionar driver GPU</string> @@ -172,6 +237,7 @@ <string name="select_gpu_driver_install">Instalar</string> <string name="select_gpu_driver_default">Predeterminado</string> <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string> + <string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string> <string name="system_gpu_driver">Driver GPU del sistema</string> <string name="installing_driver">Instalando driver...</string> @@ -182,10 +248,11 @@ <string name="preferences_graphics">Gráficos</string> <string name="preferences_audio">Audio</string> <string name="preferences_theme">Tema y color</string> + <string name="preferences_debug">Depuración</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">Su ROM está encriptada</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga las guías para redumpear <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">cartuchos de juegos</a> o <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">titulos instalados</a>.]]></string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga las guías para redumpear<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartuchos de juegos</a> o <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">títulos instalados</a>.]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor, compruebe que su archivo <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado, para que los juegos sean descifrados.]]></string> <string name="loader_error_video_core">Ocurrió un error al inicializar el núcleo de video, posiblemente debido a una incompatibilidad con el driver seleccionado</string> <string name="loader_error_video_core_description">Esto suele deberse a un driver de GPU incompatible. La instalación de un controlador de GPU personalizado puede resolver este problema.</string> @@ -196,25 +263,25 @@ <string name="emulation_exit">Salir de la emulación</string> <string name="emulation_done">Hecho</string> <string name="emulation_fps_counter">Contador de FPS</string> - <string name="emulation_toggle_controls">Alternar Controles</string> - <string name="emulation_rel_stick_center">Centro Relativo del Stick</string> - <string name="emulation_dpad_slide">Deslizamiento de la Cruceta</string> - <string name="emulation_haptics">Hápticos</string> - <string name="emulation_show_overlay">Mostrar pantalla</string> - <string name="emulation_toggle_all">Alternar Todo</string> - <string name="emulation_control_adjust">Ajustar pantalla</string> + <string name="emulation_toggle_controls">Alternar controles</string> + <string name="emulation_rel_stick_center">Centro relativo del stick</string> + <string name="emulation_dpad_slide">Deslizamiento de la cruceta</string> + <string name="emulation_haptics">Toques hápticos</string> + <string name="emulation_show_overlay">Mostrar overlay</string> + <string name="emulation_toggle_all">Alternar todo</string> + <string name="emulation_control_adjust">Ajustar overlay</string> <string name="emulation_control_scale">Escala</string> <string name="emulation_control_opacity">Opacidad</string> - <string name="emulation_touch_overlay_reset">Reiniciar pantalla</string> - <string name="emulation_touch_overlay_edit">Editar pantalla</string> - <string name="emulation_pause">Pausar Emulación</string> - <string name="emulation_unpause">Reanudar Emulación</string> - <string name="emulation_input_overlay">Opciones de pantalla </string> + <string name="emulation_touch_overlay_reset">Reiniciar overlay</string> + <string name="emulation_touch_overlay_edit">Editar overlay</string> + <string name="emulation_pause">Pausar emulación</string> + <string name="emulation_unpause">Despausar emulación</string> + <string name="emulation_input_overlay">Opciones de overlay</string> <string name="load_settings">Cargando configuración...</string> <!-- Software keyboard --> - <string name="software_keyboard">Software del teclado</string> + <string name="software_keyboard">Teclado de software</string> <!-- Errors and warnings --> <string name="abort_button">Abortar</string> @@ -226,6 +293,9 @@ <string name="fatal_error">Error fatal</string> <string name="fatal_error_message">Ocurrió un error fatal. Consulte el registro para obtener más detalles.\nContinuar con la emulación puede provocar bloqueos y errores.</string> <string name="performance_warning">¡Desactivar esta configuración reducirá significativamente el rendimiento de la emulación! Para obtener la mejor experiencia, se recomienda dejar esta configuración habilitada.</string> + <string name="device_memory_inadequate">RAM de dispositivo: %1$s\nRecomendado: %2$s</string> + <string name="memory_formatted">%1$s %2$s</string> + <string name="no_game_present">¡No hay ningún juego ejecutable presente!</string> <!-- Region Names --> <string name="region_japan">Japón</string> @@ -236,7 +306,14 @@ <string name="region_korea">Corea</string> <string name="region_taiwan">Taiwán</string> - <!-- Language Names --> + <!-- Memory Sizes --> + <string name="memory_byte">Byte</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> @@ -274,6 +351,11 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <!-- Screen Layouts --> + <string name="screen_layout_landscape">Paisaje</string> + <string name="screen_layout_portrait">Retrato</string> + <string name="screen_layout_auto">Auto</string> + <!-- Aspect Ratios --> <string name="ratio_default">Predeterminado (16:9)</string> <string name="ratio_force_four_three">Forzar 4:3</string> @@ -298,7 +380,7 @@ <string name="building_shaders">Construyendo shaders</string> <!-- Theme options --> - <string name="change_app_theme">Cambiar Tema</string> + <string name="change_app_theme">Cambiar tema</string> <string name="theme_default">Predeterminado</string> <string name="theme_material_you">Material You</string> @@ -308,8 +390,22 @@ <string name="theme_mode_light">Claro</string> <string name="theme_mode_dark">Oscuro</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">Usar Fondos Negros</string> + <string name="use_black_backgrounds">Fondos oscuros</string> <string name="use_black_backgrounds_description">Cuando utilice el modo oscuro, aplique fondos negros.</string> -</resources> + <!-- Picture-In-Picture --> + <string name="picture_in_picture">Picture in Picture</string> + <string name="picture_in_picture_description">Minimizar ventana cuando esté en segundo plano</string> + <string name="pause">Pausar</string> + <string name="play">Jugar</string> + <string name="mute">Mutear</string> + <string name="unmute">Desmutear</string> + + <!-- Licenses screen strings --> + <string name="licenses">Licencias</string> + <string name="license_fidelityfx_fsr_description">Upscaling de alta calidad de AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index 1e02828aa..5a827c50b 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.<br /><br />Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string> <string name="emulation_notification_channel_name">L\'émulation est active</string> @@ -19,12 +19,13 @@ <string name="games">Jeux</string> <string name="games_description">Sélectionnez votre dossier <b>de Jeux</b> avec le bouton ci-dessous.</string> <string name="done">Terminé</string> - <string name="done_description">Vous êtes prêt.\nProfitez de vos jeux !</string> + <string name="done_description">Vous êtes prêt.\nProfitez de vos jeux !</string> <string name="text_continue">Continuer</string> <string name="next">Suivant</string> <string name="back">Retour</string> <string name="add_games">Ajouter des jeux</string> - <string name="add_games_description">Sélectionner votre dossier de jeux</string> + <string name="add_games_description">Sélectionner le dossier des jeux</string> + <string name="step_complete">Terminé !</string> <!-- Home strings --> <string name="home_games">Jeux</string> @@ -32,12 +33,13 @@ <string name="home_settings">Paramètres</string> <string name="empty_gamelist">Aucun fichier n\'a été trouvé ou aucun répertoire de jeu n\'a encore été sélectionné.</string> <string name="search_and_filter_games">Rechercher et filtrer les jeux</string> - <string name="select_games_folder">Sélectionner le dossier de jeux</string> + <string name="select_games_folder">Sélectionner le dossier des jeux</string> <string name="select_games_folder_description">Permet à yuzu de remplir la liste des jeux</string> - <string name="add_games_warning">Ne pas sélectionner le dossier des jeux ?</string> + <string name="add_games_warning">Ne pas sélectionner le dossier des jeux ?</string> <string name="add_games_warning_description">Les jeux ne seront pas affichés dans la liste des jeux si aucun dossier n\'est sélectionné.</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> <string name="home_search_games">Rechercher des jeux</string> + <string name="search_settings">Rechercher un paramètre</string> <string name="games_dir_selected">Répertoire de jeux sélectionné</string> <string name="install_prod_keys">Installer prod.keys</string> <string name="install_prod_keys_description">Nécessaire pour décrypter les jeux commerciaux.</string> @@ -46,7 +48,7 @@ <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> <string name="notifications">Notifications</string> <string name="notifications_description">Accordez l\'autorisation de notification avec le bouton ci-dessous.</string> - <string name="give_permission">Donner la permission</string> + <string name="give_permission">Accorder la permission</string> <string name="notification_warning">Ne pas accorder la permission de notification ?</string> <string name="notification_warning_description">yuzu ne pourra pas vous communiquer d\'informations importantes.</string> <string name="permission_denied">Permission refusée</string> @@ -61,12 +63,15 @@ <string name="invalid_keys_file">Fichier de clés sélectionné invalide</string> <string name="install_keys_success">Clés installées avec succès</string> <string name="reading_keys_failure">Erreur lors de la lecture des clés de chiffrement</string> + <string name="install_prod_keys_failure_extension_description">Vérifiez que votre fichier de clés a une extension .keys et réessayez.</string> + <string name="install_amiibo_keys_failure_extension_description">Vérifiez que votre fichier de clés a une extension .bin et réessayez.</string> <string name="invalid_keys_error">Clés de chiffrement invalides</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">Le fichier sélectionné est incorrect ou corrompu. Veuillez dumper à nouveau vos clés.</string> <string name="install_gpu_driver">Installer le pilote du GPU</string> - <string name="install_gpu_driver_description">Installez des pilotes alternatifs pour des performances ou une précision potentiellement meilleures</string> + <string name="install_gpu_driver_description">Installer des pilotes alternatifs pour des performances ou une précision potentiellement meilleures</string> <string name="advanced_settings">Paramètres avancés</string> + <string name="advanced_settings_game">Paramètres avancés : %1$s</string> <string name="settings_description">Configurer les paramètres de l\'émulateur</string> <string name="search_recently_played">Joué récemment</string> <string name="search_recently_added">Ajouté récemment</string> @@ -86,6 +91,33 @@ <string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string> <string name="import_saves">Importer</string> <string name="export_saves">Exporter</string> + <string name="install_firmware">Installer le firmware</string> + <string name="install_firmware_description">Le firmware doit être dans une archive ZIP et est nécessaire pour démarrer certains jeux.</string> + <string name="firmware_installing">Installation du firmware</string> + <string name="firmware_installed_success">Firmware installé avec succès</string> + <string name="firmware_installed_failure">L\'installation du firmware a échoué</string> + <string name="firmware_installed_failure_description">Assurez-vous que les fichiers NCA du firmware se trouvent à la racine du fichier ZIP, puis réessayez.</string> + <string name="share_log">Partager les logs de débogage</string> + <string name="share_log_description">Partagez le fichier de log de yuzu pour déboguer les problèmes.</string> + <string name="share_log_missing">Aucun fichier de log trouvé</string> + <string name="install_game_content">Installer le contenu du jeu</string> + <string name="install_game_content_description">Installer une mise à jour ou un DLC</string> + <string name="installing_game_content">Installation du contenu en cours...</string> + <string name="install_game_content_failure">Erreur lors de l\'installation du fichier dans la NAND</string> + <string name="install_game_content_failure_description">Veuillez vous assurer que le contenu est valide et que le fichier prod.keys est installé.</string> + <string name="install_game_content_failure_base">L\'installation de jeux de base n\'est pas autorisée afin d\'éviter d\'éventuels conflits.</string> + <string name="install_game_content_failure_file_extension">Seuls les contenus NSP et XCI sont pris en charge. Veuillez vérifier que le contenu du jeu est valide.</string> + <string name="install_game_content_failed_count">%1$d erreur(s) d\'installation</string> + <string name="install_game_content_success">Contenu du jeu installé avec succès</string> + <string name="install_game_content_success_install">%1$d installé avec succès</string> + <string name="install_game_content_success_overwrite">%1$d écrasé avec succès</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">Pilotes personnalisés non supporté</string> + <string name="custom_driver_not_supported_description">Le chargement des pilotes personnalisés ne sont pas actuellement pris en charge pour ce périphérique. Vérifiez à nouveau cette option à l\'avenir pour voir si la prise en charge a été ajoutée !</string> + <string name="manage_yuzu_data">Gérer les données de yuzu</string> + <string name="manage_yuzu_data_description">Importer/exporter le firmware, les clés, les données utilisateur, et bien plus encore !</string> + <string name="share_save_file">Partager le fichier de sauvegarde</string> + <string name="export_save_failed">Échec de l\'exportation de la sauvegarde</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia n\'est pas réel</string> @@ -94,7 +126,18 @@ <string name="contributors">Contributeurs</string> <string name="contributors_description">Fait avec \u2764 de l\'équipe yuzu</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Des projets qui rendent possible yuzu pour Android</string> <string name="build">Build</string> + <string name="user_data">Données utilisateur</string> + <string name="user_data_description">Importer/exporter toutes les données de l\'application.\n\nLors de l\'importation des données utilisateur, toutes les données utilisateur existantes seront supprimées !</string> + <string name="exporting_user_data">Exportation des données utilisateur...</string> + <string name="importing_user_data">Importation des données utilisateur...</string> + <string name="import_user_data">Importer des données utilisateur</string> + <string name="invalid_yuzu_backup">Backup yuzu invalide</string> + <string name="user_data_export_success">Les données utilisateur ont été exportés avec succès</string> + <string name="user_data_import_success">Les données utilisateur ont été importées avec succès</string> + <string name="user_data_export_cancelled">Exportation annulée</string> + <string name="user_data_import_failed_description">Assurez-vous que les dossiers de données utilisateur se trouvent à la racine du dossier ZIP et contiennent un fichier de configuration à config/config.ini, puis réessayez.</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> @@ -114,64 +157,87 @@ <string name="are_you_interested">Es tu intéressé ?</string> <!-- General settings strings --> - <string name="frame_limit_enable">Activer la vitesse limite</string> - <string name="frame_limit_enable_description">Lorsqu\'elle est activée, la vitesse d\'émulation sera limitée à un pourcentage spécifié de la vitesse normale.</string> + <string name="frame_limit_enable">Limitation de vitesse</string> + <string name="frame_limit_enable_description">Limiter la vitesse d\'émulation à un pourcentage spécifié de la vitesse normale</string> <string name="frame_limit_slider">Limite en pourcentage de vitesse</string> - <string name="frame_limit_slider_description">Spécifie le pourcentage pour limiter la vitesse d\'émulation. Avec la valeur par défaut de 100%, l\'émulation sera limitée à la vitesse normale. Des valeurs supérieures ou inférieures augmenteront ou diminueront la limite de vitesse.</string> + <string name="frame_limit_slider_description">Spécifier le pourcentage pour limiter la vitesse d\'émulation. 100% correspond à la vitesse normale. Des valeurs plus élevées ou plus basses augmenteront ou diminueront la limite de vitesse.</string> <string name="cpu_accuracy">Précision du CPU</string> + <string name="value_with_units">%1$s%2$s</string> <!-- System settings strings --> <string name="use_docked_mode">Mode TV</string> - <string name="use_docked_mode_description">Émuler en mode TV augmente la résolution au détriment des performances.</string> + <string name="use_docked_mode_description">Augmenter la résolution, ce qui diminue les performances. Le mode portable est utilisé lorsque la fonction est désactivée, ce qui réduit la résolution et améliore les performances.</string> <string name="emulated_region">Région émulée</string> <string name="emulated_language">Langue émulée</string> <string name="select_rtc_date">Sélectionner la date RTC</string> <string name="select_rtc_time">Sélectionner l\'heure RTC</string> - <string name="use_custom_rtc">Activer l\'horloge RTC personnalisée</string> - <string name="use_custom_rtc_description">Ce paramètre vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système.</string> + <string name="use_custom_rtc">RTC personnalisé</string> + <string name="use_custom_rtc_description">Vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système.</string> <string name="set_custom_rtc">Définir l\'horloge RTC personnalisée</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">Niveau de précision</string> - <string name="renderer_resolution">Résolution</string> + <string name="renderer_resolution">Résolution (Mode Portable/Mode TV)</string> <string name="renderer_vsync">Mode VSync</string> + <string name="renderer_screen_layout">Orientation</string> <string name="renderer_aspect_ratio">Format</string> <string name="renderer_scaling_filter">Filtre de fenêtre adaptatif</string> - <string name="renderer_anti_aliasing">Méthode d\'anticrénelage :</string> - <string name="renderer_force_max_clock">Forcer la fréquence d\'horloge maximale (Adreno uniquement)</string> - <string name="renderer_force_max_clock_description">Force le GPU à fonctionner au maximum d\'horloges possibles (les contraintes thermiques seront toujours appliquées).</string> + <string name="renderer_anti_aliasing">Méthode d\'anticrénelage</string> + <string name="renderer_force_max_clock">Forcer les fréquences maximales (Adreno uniquement)</string> + <string name="renderer_force_max_clock_description">Forcer le GPU à fonctionner à ses fréquences maximales possibles (les contraintes thermiques seront toujours appliquées).</string> <string name="renderer_asynchronous_shaders">Utiliser les shaders asynchrones</string> - <string name="renderer_asynchronous_shaders_description">Compile les shaders de manière asynchrone, ce qui réduira les saccades mais peut entraîner des problèmes visuels.</string> - <string name="renderer_debug">Activer le débogage des graphismes</string> - <string name="renderer_debug_description">Lorsque cette case est cochée, l\'API graphique entre dans un mode de débogage plus lent.</string> - <string name="use_disk_shader_cache">Utiliser les shader cache de disque</string> - <string name="use_disk_shader_cache_description">Réduire les saccades en stockant et en chargeant les shaders générés sur le disque.</string> + <string name="renderer_asynchronous_shaders_description">Compile les shaders de manière asynchrone, réduisant les saccades mais pouvant entraîner des problèmes visuels.</string> + <string name="renderer_reactive_flushing">Utiliser le vidage réactif</string> + <string name="renderer_reactive_flushing_description">Améliore la précision du rendu dans certains jeux au détriment des performances.</string> + <string name="use_disk_shader_cache">Utiliser les shader cache</string> + <string name="use_disk_shader_cache_description">Réduire les saccades en stockant et en chargeant localement les shaders générés</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">Débogage du CPU</string> + <string name="cpu_debug_mode_description">Place le CPU en mode lent de débogage.</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">Débogage des graphismes</string> + <string name="renderer_debug_description">Définit l\'API graphique en mode de débogage lent.</string> + <string name="fastmem">Fastmem</string> <!-- Audio settings strings --> + <string name="audio_output_engine">Moteur de sortie</string> <string name="audio_volume">Volume</string> - <string name="audio_volume_description">Spécifie le volume de la sortie audio.</string> + <string name="audio_volume_description">Spécifier le volume de la sortie audio.</string> <!-- Miscellaneous --> - <string name="slider_default">Défaut</string> + <string name="slider_default">Par défaut</string> <string name="ini_saved">Paramètres enregistrés</string> <string name="gameid_saved">Paramètres enregistrés pour %1$s</string> <string name="error_saving">Erreur lors de l\'enregistrement de %1$s.ini: %2$s</string> + <string name="unimplemented_menu">Menu non implémenté</string> <string name="loading">Chargement...</string> - <string name="reset_setting_confirmation">Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ?</string> + <string name="shutting_down">Extinction en cours...</string> + <string name="reset_setting_confirmation">Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ?</string> <string name="reset_to_default">Réinitialiser par défaut</string> <string name="reset_all_settings">Réinitialiser tous les réglages ?</string> <string name="reset_all_settings_description">Tous les paramètres avancés seront réinitialisés à leur configuration par défaut. Ça ne peut pas être annulé.</string> <string name="settings_reset">Paramètres réinitialisés</string> <string name="close">Fermer</string> - <string name="learn_more">Plus d\'informations</string> + <string name="learn_more">En savoir plus</string> + <string name="auto">Auto</string> + <string name="submit">Soumettre</string> + <string name="string_null">Nul</string> + <string name="string_import">Importer</string> + <string name="export">Exporter</string> + <string name="export_failed">L\'exportation a échoué</string> + <string name="import_failed">L\'importation a échoué</string> + <string name="cancelling">Annulation</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Sélectionner le pilote du GPU</string> <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string> <string name="select_gpu_driver_install">Installer</string> - <string name="select_gpu_driver_default">Défaut</string> - <string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string> + <string name="select_gpu_driver_default">Par défaut</string> + <string name="select_gpu_driver_use_default">Utilisation du pilote du GPU par défaut</string> + <string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string> <string name="system_gpu_driver">Pilote du GPU du système</string> <string name="installing_driver">Installation du pilote...</string> @@ -182,13 +248,14 @@ <string name="preferences_graphics">Vidéo</string> <string name="preferences_audio">Audio</string> <string name="preferences_theme">Thème et couleur</string> + <string name="preferences_debug">Débogage</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">Votre ROM est cryptée</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[Veuillez suivre les guides pour redumper vos <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">cartouches de jeu</a> ou <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">titres installés</a>.]]></string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Veuillez suivre les guides pour refaire un dump de vos <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartouches de jeu</a> ou de vos <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">titres installés</a>.]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[Veuillez vous assurer que votre fichier <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> est installé pour que les jeux puissent être déchiffrés.]]></string> <string name="loader_error_video_core">Une erreur s\'est produite lors de l\'initialisation du noyau vidéo</string> - <string name="loader_error_video_core_description">Cela est généralement dû à un pilote du GPU incompatible. L\'installation d\'un pilote du GPU personnalisé peut résoudre ce problème.</string> + <string name="loader_error_video_core_description">Cela est généralement dû à un pilote GPU incompatible. L\'installation d\'un pilote GPU personnalisé peut résoudre ce problème.</string> <string name="loader_error_invalid_format">Impossible de charger la ROM</string> <string name="loader_error_file_not_found">Le fichier ROM n\'existe pas</string> @@ -198,8 +265,8 @@ <string name="emulation_fps_counter">Compteur FPS</string> <string name="emulation_toggle_controls">Activer/Désactiver les contrôles</string> <string name="emulation_rel_stick_center">Centre du stick relatif</string> - <string name="emulation_dpad_slide">Glissement du DPad</string> - <string name="emulation_haptics">Haptique</string> + <string name="emulation_dpad_slide">Glissement du D-pad</string> + <string name="emulation_haptics">Toucher haptique</string> <string name="emulation_show_overlay">Afficher l\'overlay</string> <string name="emulation_toggle_all">Tout basculer</string> <string name="emulation_control_adjust">Ajuster l\'overlay</string> @@ -225,7 +292,10 @@ <string name="save_load_error">Erreur de sauvegarde/chargement</string> <string name="fatal_error">Erreur fatale</string> <string name="fatal_error_message">Une erreur fatale s\'est produite. Consultez les logs pour plus de détails.\nContinuer l\'émulation peut entraîner des plantages et des bogues.</string> - <string name="performance_warning">La désactivation de ce paramètre réduira considérablement les performances d\'émulation ! Pour une expérience optimale, il est recommandé de laisser ce paramètre activé.</string> + <string name="performance_warning">La désactivation de ce paramètre réduira considérablement les performances d\'émulation ! Pour une expérience optimale, il est recommandé de laisser ce paramètre activé.</string> + <string name="device_memory_inadequate">Mémoire RAM de l\'appareil : %1$s\nRecommandé : %2$s</string> + <string name="memory_formatted">%1$s %2$s</string> + <string name="no_game_present">Aucun jeu démarreable présent !</string> <!-- Region Names --> <string name="region_japan">Japon</string> @@ -236,7 +306,14 @@ <string name="region_korea">Corée</string> <string name="region_taiwan">Taïwan</string> - <!-- Language Names --> + <!-- Memory Sizes --> + <string name="memory_byte">Octet</string> + <string name="memory_kilobyte">Ko</string> + <string name="memory_megabyte">Mo</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">To</string> + <string name="memory_petabyte">Po</string> + <string name="memory_exabyte">Eo</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> @@ -274,6 +351,11 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <!-- Screen Layouts --> + <string name="screen_layout_landscape">Paysage</string> + <string name="screen_layout_portrait">Portrait</string> + <string name="screen_layout_auto">Auto</string> + <!-- Aspect Ratios --> <string name="ratio_default">Par défaut (16:9)</string> <string name="ratio_force_four_three">Forcer le 4:3</string> @@ -288,8 +370,8 @@ <!-- Gamepad Buttons --> <string name="gamepad_d_pad">Pavé directionnel</string> - <string name="gamepad_left_stick">Stick Gauche</string> - <string name="gamepad_right_stick">Stick Droit</string> + <string name="gamepad_left_stick">Stick gauche</string> + <string name="gamepad_right_stick">Stick droit</string> <string name="gamepad_home">Home</string> <string name="gamepad_screenshot">Capture d\'écran</string> @@ -299,7 +381,7 @@ <!-- Theme options --> <string name="change_app_theme">Changer le thème de l\'application</string> - <string name="theme_default">Défaut</string> + <string name="theme_default">Par défaut</string> <string name="theme_material_you">Material You</string> <!-- Theme Modes --> @@ -308,8 +390,22 @@ <string name="theme_mode_light">Lumineux</string> <string name="theme_mode_dark">Sombre</string> - <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">Utiliser des arrière-plans noirs</string> - <string name="use_black_backgrounds_description">Lorsque vous utilisez le thème sombre, appliquer des arrière-plans noirs.</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> -</resources> + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Arrière-plan noir</string> + <string name="use_black_backgrounds_description">Lorsque vous utilisez le thème sombre, appliquer un arrière-plan noir.</string> + + <!-- Picture-In-Picture --> + <string name="picture_in_picture">Lecteur réduit</string> + <string name="picture_in_picture_description">Réduire la fenêtre lorsqu\'elle est placée en arrière-plan</string> + <string name="pause">Pause</string> + <string name="play">Jouer</string> + <string name="mute">Couper le son</string> + <string name="unmute">Remettre le son</string> + + <!-- Licenses screen strings --> + <string name="licenses">Licences</string> + <string name="license_fidelityfx_fsr_description">Mise à l\'échelle de haute qualité par AMD.</string> + </resources> diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml new file mode 100644 index 000000000..0af78a57c --- /dev/null +++ b/src/android/app/src/main/res/values-he/strings.xml @@ -0,0 +1,367 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + + <string name="app_disclaimer">התוכנה תריץ משחקים לקונסולת ה Nintendo Switch. אף משחק או קבצים בעלי זכויות יוצרים נכללים.<br /><br /> לפני שאת/ה מתחיל בבקשה מצא את קובץ <![CDATA[<b>prod.keys</b>]]> על המכשיר.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">קרא עוד</a>]]></string> + <string name="emulation_notification_channel_name">אמולציה פעילה</string> + <string name="emulation_notification_channel_description">מציג התראה מתמשכת כאשר האמולציה פועלת.</string> + <string name="emulation_notification_running">yuzu רץ</string> + <string name="notice_notification_channel_name">התראות ותקלות</string> + <string name="notice_notification_channel_description">מציג התראות כאשר משהו הולך לא כשורה.</string> + <string name="notification_permission_not_granted">הרשאות התראות לא ניתנה!</string> + + <!-- Setup strings --> + <string name="welcome">ברוכים הבאים!</string> + <string name="welcome_description">למד איך להפעיל <b>yuzu</b> וקפוץ ישר לאמולציה.</string> + <string name="get_started">כדי להתחיל</string> + <string name="keys">מפתחות</string> + <string name="keys_description">בחר את קובץ ה <b>prod.keys</b> שלך עם הכפתור למטה.</string> + <string name="select_keys">בחר מפתחות</string> + <string name="games">משחקים</string> + <string name="games_description">בחר את התיקיית ה <b>Games</b> שלך עם הכפתור למטה.</string> + <string name="done">סיום</string> + <string name="done_description">את/ה מוכן. \nתהנה/י מהמשחקים שלך </string> + <string name="text_continue">המשך</string> + <string name="next">הבא</string> + <string name="back">אחורה</string> + <string name="add_games">הוסף משחקים</string> + <string name="add_games_description">בחר/י את תיקיית המשחקים שלך</string> + <string name="step_complete">הושלם!</string> + + <!-- Home strings --> + <string name="home_games">משחקים</string> + <string name="home_search">חפש</string> + <string name="home_settings">הגדרות</string> + <string name="empty_gamelist">לא נמצאו קבצים או לנבחרה ספריית קבצים בינתיים.</string> + <string name="search_and_filter_games">חפש וסנן משחקים</string> + <string name="select_games_folder">בחר תיקיית משחקים</string> + <string name="select_games_folder_description">אפשר ל yuzu לאכלס את רשימת המשחקים</string> + <string name="add_games_warning">לדלג על בחירת תיקיית המשחקים?</string> + <string name="add_games_warning_description">משחקים לא יוצגו ברשימת המשחקים אם לנבחרה תיקיית משחקים.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">חפש משחקים</string> + <string name="search_settings">חפש בהגדרות</string> + <string name="games_dir_selected">ספריית משחקים נבחרה</string> + <string name="install_prod_keys">התקן prod.keys</string> + <string name="install_prod_keys_description">הכרחי בכדי לפענח משחקים</string> + <string name="install_prod_keys_warning">לדלג על הוספת מפתחות?</string> + <string name="install_prod_keys_warning_description">מפתחות חוקיים הכרחיים כדי לשחק במשחקים. רק אפליקציות פירטיות יפעלו אם תמשיך.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">התראות</string> + <string name="notifications_description">תן גישה להתראות עם הכפתור למטה.</string> + <string name="give_permission">תן הרשאה</string> + <string name="notification_warning">דלג על מתן הרשאה להתראות?</string> + <string name="notification_warning_description">yuzu לא יוכל להתריע לך על מידע חשוב.</string> + <string name="permission_denied">הרשאה נדחתה</string> + <string name="permission_denied_description">את/ה דיחת את ההרשאה יותר מדי פעמים ועכשיו את/ה צריך/ה לתת גישה באופן ידני בהגדרות.</string> + <string name="about">אודות</string> + <string name="about_description">מספר גירסה, קרדיטים ועוד</string> + <string name="warning_help">עזרה</string> + <string name="warning_skip">דלג</string> + <string name="warning_cancel">ביטול</string> + <string name="install_amiibo_keys">התקן מפתחות Amiibo</string> + <string name="install_amiibo_keys_description">נחוץ כדי להשתמש ב Amiibo במשחק</string> + <string name="invalid_keys_file">קובץ מפתחות לא חוקי נבחר</string> + <string name="install_keys_success">מפתחות הותקנו בהצלחה</string> + <string name="reading_keys_failure">שגיאה בקריאת מפתחות ההצפנה</string> + <string name="install_prod_keys_failure_extension_description">ודא שלקובץ המפתחות שלך יש סיומת של key. ונסה/י שוב.</string> + <string name="install_amiibo_keys_failure_extension_description">ודא/י שלקובץ המפתחות שלך יש סיומת של bin. ונסה/י שוב.</string> + <string name="invalid_keys_error">מפתחות הצפנה לא חוקיים</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">קבוץ שנבחר מושחת או לא נכון. בבקשה הוצא מחדש את המפתחות שלך.</string> + <string name="install_gpu_driver">התקן דרייבר למעבד הגרפי</string> + <string name="install_gpu_driver_description">התקן דרייברים אחרים בשביל סיכוי לביצועים או דיוק גבוההים יותר</string> + <string name="advanced_settings">הגדרות מתקדמות</string> + <string name="advanced_settings_game">הגדרות מתקדמות: %1$s</string> + <string name="settings_description">הדר את הגדרות האמולטור</string> + <string name="search_recently_played">שוחק לאחרונה</string> + <string name="search_recently_added">הוסף לאחרונה</string> + <string name="search_retail">קמעונאי</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">פתח את תיקיית yuzu </string> + <string name="open_user_folder_description">נה ל את הקבצים הפנימיין של yuzu</string> + <string name="theme_and_color_description">ערוך את נראות האפליקציה</string> + <string name="no_file_manager">לא נמצא מנהל קבצים</string> + <string name="notification_no_directory_link">לא יכול לפתוח את ספריית yuzu</string> + <string name="notification_no_directory_link_description">בבקשה מקם את תיקיית המשתמש בפנל הצידי של מנהל הקבצים באופן ידני.</string> + <string name="manage_save_data">נהל מידע שמור</string> + <string name="manage_save_data_description">מידע שמור לא נמצא. בבקשה בחר/י אופציה מלמטה</string> + <string name="import_export_saves_description">יבא או יצא קבצי שמירה</string> + <string name="save_file_imported_success">יובא בהצלחה</string> + <string name="save_file_invalid_zip_structure">מבנה ספריית השמירות לא חוקי</string> + <string name="save_file_invalid_zip_structure_description">התת תיקייה הראשונה חייב להיות ה title ID של המשחק</string> + <string name="import_saves">ייבוא</string> + <string name="export_saves">ייצוא</string> + <string name="install_firmware">התקן firmware</string> + <string name="install_firmware_description">ה frimware חייב להיות בקובץ zip והוא הכרחי להפעלת חלק מהמשחקים</string> + <string name="firmware_installing">מתקין frimware</string> + <string name="firmware_installed_success">ה frimware הותקן בהצלחה</string> + <string name="firmware_installed_failure">התקנת ה frimware נכשלה</string> + <string name="firmware_installed_failure_description">ודא שקבצי ה firmware nca נמצאים בשורש ה zip ונסה שוב.</string> + <string name="share_log">שתף את יומני הרישום של מיפוי הבאגים</string> + <string name="share_log_description">שתף את קובץ יומני הרישום של yuzu בכדי לתקן בעיות</string> + <string name="share_log_missing">לא נמצא קובץ יומן רישום</string> + <string name="install_game_content">התקן תוכן משחק</string> + <string name="install_game_content_description">התקן עדכוני משחק או DLC</string> + <string name="installing_game_content">מתקין תוכן...</string> + <string name="install_game_content_failure">תקלה בהתקנת הקובץ (או קבצים) ל NAND</string> + <string name="install_game_content_failure_description">בבקשה ודא שהתוכן (או תכנים) חוקיים ושקובץ ה prod.keys מותקן.</string> + <string name="install_game_content_failure_base">התקנת משחק בסיס נדחת בכדי להימנע מקונפליקטים אפשריים.</string> + <string name="install_game_content_failure_file_extension">רק קבצי NSP ו XCI נתמכים. בבקשה ודא שתוכן (או תכנים) המשחק חוקי.</string> + <string name="install_game_content_failed_count">%1$dבעיה (בעיות) התקנה</string> + <string name="install_game_content_success">תוכן (או תכני) המשחק הותקנו בהצלחה</string> + <string name="install_game_content_success_install">%1$d הותקן בהצלחה</string> + <string name="install_game_content_success_overwrite">%1$d נדרס/נכתב מעל בהצלחה</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">דרייברים מותאמים אישית לא נתמכים</string> + <string name="custom_driver_not_supported_description">הטענת דרייבים מותאמים אישית לא נתמך כרגע על מכשיר זה. \nבבקשה בדוק אופציה זו בעתיד בכדי לראות אם נוספה תמיכה!</string> + <string name="manage_yuzu_data">נהל את המידע של yuzu</string> + <string name="manage_yuzu_data_description">יבא/יצא firmware, keys, מידע של משתמש ועוד!</string> + <string name="share_save_file">שתף קובץ שמירה</string> + <string name="export_save_failed">נכשל בייצוא שמירה</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia לא אמיתית</string> + <string name="copied_to_clipboard">הועתק ללוח</string> + <string name="about_app_description">אמולטור Switch עם קוד פתוח</string> + <string name="contributors">תורמים</string> + <string name="contributors_description">נוצר עם \u2764 מקבוצת yuzu</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">פרוייקטים שהופכים את yuzu ל Android אפשרי</string> + <string name="build">גרסה</string> + <string name="user_data">נתוני משתמש</string> + <string name="user_data_description">יבא/יצא את כל נתוני האפליקציה.\n\nכאשר מייבאים את נתוני המשתמש, כל נתוני המשתמש הקיימים ימחקו!</string> + <string name="exporting_user_data">מייצא נתוני משתמש...</string> + <string name="importing_user_data">מייבא נתוני משתמש...</string> + <string name="import_user_data">יבא נתוני משתמש</string> + <string name="invalid_yuzu_backup">גיבוי yuzu לא חוקי</string> + <string name="user_data_export_success">נתוני משתמש יוצאו בהצלחה</string> + <string name="user_data_import_success">נתוני משתמש יובאו בהצלחה</string> + <string name="user_data_export_cancelled">ייצוא בוטל</string> + <string name="user_data_import_failed_description">ודא שנתוני המשתמש נמצאים בשורש קובץ ה zip ושהוא מכיל קובץ סידור ב config/config.ini ונסה שוב.</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">גישה מוקדמת</string> + <string name="get_early_access">קבל גישה מוקדמת</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">תכונות חותכות קצה, גישה מוקדמת לעדכונים, ועוד</string> + <string name="early_access_benefits">יתרונות של גישה מקודמת</string> + <string name="cutting_edge_features">תכונות חותכות קצה</string> + <string name="early_access_updates">גישה מוקדמת לעדכונים</string> + <string name="no_manual_installation">ללא התקנה ידנית</string> + <string name="prioritized_support">תמיכה בעדיפות</string> + <string name="helping_game_preservation">עוזר בשמירת משחקים</string> + <string name="our_eternal_gratitude">התודה האינסופית שלנו</string> + <string name="are_you_interested">אתה מעוניין?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">הגבל מהירות</string> + <string name="frame_limit_enable_description">מגביל את מהירות האמולציה לאחוז מהירות המבוקש מהמהירות הרגילה.</string> + <string name="frame_limit_slider">הגבל את אחוז המהירות</string> + <string name="frame_limit_slider_description">מדייק את אחוז מהירות האמולציה. 100% זה מהירות רגילה. ערכים גדולים או קטנים יאיצו או יאטו את מהירות האמולציה.</string> + <string name="cpu_accuracy">דיוק המעבד</string> + <string name="value_with_units">%1$s%2$s</string> + + <!-- System settings strings --> + <string name="use_docked_mode">מצב עגינה</string> + <string name="use_docked_mode_description">מעלה את הרזולוציה, פוגע בביצועים. משתמש במצב נייד כאשר מנוטרל, מפחית את הרזולוציה ומעלה את הביצועים.</string> + <string name="emulated_region">אזור אמולציה</string> + <string name="emulated_language">שפת אמולציה</string> + <string name="select_rtc_date">בחר תאריך RTC</string> + <string name="select_rtc_time">בחר זמן RTC</string> + <string name="use_custom_rtc">RTC מותאם אישית</string> + <string name="use_custom_rtc_description">מאפשר לך לקבוע שעון זמן אמת נפרד משעון המערכת שלך.</string> + <string name="set_custom_rtc">קבע RTC מותאם אישית</string> + + <!-- Graphics settings strings --> + <string name="renderer_accuracy">רמת דיוק</string> + <string name="renderer_resolution">רזולוציה (מעוגן/נייד)</string> + <string name="renderer_vsync">מצב VSync</string> + <string name="renderer_screen_layout">כיוון</string> + <string name="renderer_aspect_ratio">יחס רוחב גובה</string> + <string name="renderer_scaling_filter">פילטר מתאם חלון</string> + <string name="renderer_anti_aliasing">שיטת Anti-aliasing</string> + <string name="renderer_force_max_clock">החזק מהירות שעון מקסימלית (רק ל Adreno)</string> + <string name="renderer_force_max_clock_description">מכריח לדחוף את מהירויות המעבד הגרפי למקסימום (הגבלות חום ימשיכו לתפקד).</string> + <string name="renderer_reactive_flushing_description">משפר את הדיוק של האמולציה במשחקים מסויימים במחיר של ביצועים.</string> + <!-- Debug settings strings --> + <string name="cpu">מעבד</string> + <string name="cpu_debug_mode_description">מכניס את המעבד למצב דיבאג איטי</string> + <string name="gpu">מעבד גרפי</string> + <!-- Audio settings strings --> + <string name="audio_output_engine">מנוע פלט</string> + <string name="audio_volume">עוצמת שמע</string> + <!-- Miscellaneous --> + <string name="slider_default">ברירת מחדל</string> + <string name="ini_saved">הגדרות שמורות</string> + <string name="gameid_saved">הגדרות שמורות עבור %1$s</string> + <string name="error_saving">תקלה בשמירת %1$s.ini: %2$s</string> + <string name="loading">טוען...</string> + <string name="shutting_down">כיבוי...</string> + <string name="reset_setting_confirmation">אתה מעוניין לאפס את ההגדרה הזו חזרה לברירת המחדל?</string> + <string name="reset_to_default">אפס לברירת המחדל</string> + <string name="reset_all_settings">לאפס את כל ההגדרות?</string> + <string name="reset_all_settings_description">כל ההגדרות המתקדמות יאופסו לברירת המחדל. לא ניתן לבטל פעולה זו.</string> + <string name="settings_reset">אפס הגדרות</string> + <string name="close">סגור</string> + <string name="learn_more">למד עוד</string> + <string name="auto">אוטומטי</string> + <string name="submit">שלח</string> + <string name="string_import">ייבוא</string> + <string name="export">ייצוא</string> + <string name="export_failed">ייצוא נכשל</string> + <string name="import_failed">ייבוא נכשל</string> + <string name="cancelling">מבטל</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">בחר דרייבר למעבד הגרפי</string> + <string name="select_gpu_driver_title">אתה מעוניין להחליף את הדרייבר של המעבד הגרפי שלך?</string> + <string name="select_gpu_driver_install">התקן</string> + <string name="select_gpu_driver_default">ברירת מחדל</string> + <string name="select_gpu_driver_use_default">משתמש בדרייבר ברירת המחדל של המעבד הגרפי</string> + <string name="select_gpu_driver_error">דרייבר לא חוקי נבחר, משתמש בברירת המחדל של המערכת!</string> + <string name="system_gpu_driver">דרייבר של המעבד הגרפי של המערכת</string> + <string name="installing_driver">מתקין דרייבר...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">הגדרות</string> + <string name="preferences_general">כללי</string> + <string name="preferences_system">מערכת</string> + <string name="preferences_graphics">גרפיקה</string> + <string name="preferences_audio">שמע</string> + <string name="preferences_theme">צבע ונושא</string> + <!-- ROM loading errors --> + <string name="loader_error_encrypted">המשחק שלך מוצפן</string> + <string name="loader_error_invalid_format">אין אפשרות לטעון את המשחק</string> + <string name="loader_error_file_not_found">קובץ המשחק לא קיים</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">צא מהאמולציה</string> + <string name="emulation_done">סיום</string> + <string name="emulation_fps_counter">סופר FPS</string> + <string name="emulation_control_scale">קנה מידה</string> + <string name="emulation_control_opacity">שקיפות</string> + <string name="emulation_pause">עצור אמולציה</string> + <string name="emulation_unpause">המשך אמולציה</string> + <string name="load_settings">טוען הגדרות...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">מקלדת תוכנה</string> + + <!-- Errors and warnings --> + <string name="abort_button">אודות</string> + <string name="continue_button">המשך</string> + <string name="system_archive_not_found">ארכיון מערכת לא נמצא</string> + <string name="system_archive_not_found_message">%s חסר. בבקשה הוצא תא ארכיוני המערכת שלך./nהמשכת האמולציה עלולה לגרום לקריסות ובאגים.</string> + <string name="system_archive_general">ארכיון מערכת</string> + <string name="save_load_error">בעיית שמירה/טעינה</string> + <string name="fatal_error">שגיאה חמורה</string> + <string name="device_memory_inadequate">RAM המכשיר: %1$s/nמומלץ: %2$s</string> + <string name="memory_formatted">%1$s%2$s</string> + <string name="no_game_present">אין משחק שניתן להריץ!</string> + + <!-- Region Names --> + <string name="region_japan">יפן</string> + <string name="region_usa">ארה״ב</string> + <string name="region_europe">אירופה</string> + <string name="region_australia">אוסטרליה</string> + <string name="region_china">סין</string> + <string name="region_korea">קוריאה</string> + <string name="region_taiwan">טייוואן</string> + + <!-- Memory Sizes --> + <string name="memory_byte">בייט</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">אין שום דבר</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">רגיל</string> + <string name="renderer_accuracy_high">גבוה</string> + <string name="renderer_accuracy_extreme">אקסטרים (איטי)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (איטי)</string> + <string name="resolution_three">3X (2160p/3240p) (איטי)</string> + <string name="resolution_four">4X (2880p/4320p) (איטי)</string> + + <string name="renderer_vsync_mailbox">תיבת דואר</string> + <string name="renderer_vsync_fifo">FIFO (On)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO נינוח</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">השכן הקרוב ביותר</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">אין שום דבר</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Screen Layouts --> + <string name="screen_layout_landscape">לרוחב</string> + <string name="screen_layout_portrait">לאורך</string> + <string name="screen_layout_auto">אוטומטי</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">ברירת מחדל (16:9)</string> + <string name="ratio_force_four_three">הכרח 4:3</string> + <string name="ratio_force_twenty_one_nine">הכרח 21:9</string> + <string name="ratio_force_sixteen_ten">הכרח 16:10</string> + <string name="ratio_stretch">הרחב לגודל המסך</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">מדויק</string> + <string name="cpu_accuracy_unsafe">לא בטוח</string> + <string name="cpu_accuracy_paranoid">פראנואידי (איטי)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">D-pad</string> + <string name="gamepad_left_stick">ג׳ויסטיק שמאלי</string> + <string name="gamepad_right_stick">ג׳ויסטיק ימני</string> + <string name="gamepad_home">בית</string> + <string name="gamepad_screenshot">צילום מסך</string> + + <!-- Theme options --> + <string name="change_app_theme">שנה את נושא האפליקצייה</string> + <string name="theme_default">ברירת מחדל</string> + <string name="theme_material_you">חומר אתה/מאטיריאל יו</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">שנה את מצב הנושא</string> + <string name="theme_mode_follow_system">עקוב אחרי המערכת</string> + <string name="theme_mode_light">בהיר</string> + <string name="theme_mode_dark">כהה</string> + + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">רקעים שחורים</string> + <string name="use_black_backgrounds_description">כשמתשמשים במצב כהה, שם רקעים שחורים.</string> + + <!-- Picture-In-Picture --> + <string name="picture_in_picture">תמונה בתוך תמונה</string> + <string name="picture_in_picture_description">הקטן את החלון כאשר נמצא ברקע</string> + <string name="pause">עצור</string> + <string name="play">שחק</string> + <string name="mute">השתק</string> + <string name="unmute">בטל השתקה</string> + + <!-- Licenses screen strings --> + <string name="licenses">רישיונות</string> + <string name="license_fidelityfx_fsr_description">אפסקיילינג באיכות גבוהה מ AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml new file mode 100644 index 000000000..6563ba288 --- /dev/null +++ b/src/android/app/src/main/res/values-hu/strings.xml @@ -0,0 +1,402 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + + <string name="app_disclaimer">Ez a szoftver Nintendo Switch játékkonzolhoz készült játékokat futtat. Nem tartalmaz játékokat vagy kulcsokat. .<br /><br />Mielőtt hozzákezdenél, kérjük, válaszd ki a <![CDATA[<b>prod.keys</b>]]> fájl helyét a készülék tárhelyén<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tudj meg többet</a>]]></string> + <string name="emulation_notification_channel_name">Emuláció aktív</string> + <string name="emulation_notification_channel_description">Állandó értesítést jelenít meg, amíg az emuláció fut.</string> + <string name="emulation_notification_running">A yuzu fut</string> + <string name="notice_notification_channel_name">Megjegyzések és hibák</string> + <string name="notice_notification_channel_description">Értesítések megjelenítése, ha valami rosszul sül el.</string> + <string name="notification_permission_not_granted">Nincs engedély az értesítés megjelenítéséhez!</string> + + <!-- Setup strings --> + <string name="welcome">Üdvözöljük!</string> + <string name="welcome_description">Ismerkedj meg a <b>yuzu</b> beállításával és ugorj bele az emulációba.</string> + <string name="get_started">Vágjunk bele</string> + <string name="keys">Kulcsok</string> + <string name="keys_description">Válaszd ki a(z) <b>prod.keys</b> fájlodat az alábbi gombbal.</string> + <string name="select_keys">Kulcsok kiválasztása</string> + <string name="games">Játékok</string> + <string name="games_description"> +Válaszd ki a(z) <b>Games</b> mappát az alábbi gombbal.</string> + <string name="done">Kész</string> + <string name="done_description">Minden kész.\nJó szórakozást!</string> + <string name="text_continue">Folytatás</string> + <string name="next">Következő</string> + <string name="back">Vissza</string> + <string name="add_games">Játékok hozzáadása</string> + <string name="add_games_description">Játékaid mappa kiválasztása</string> + <string name="step_complete">Kész!</string> + + <!-- Home strings --> + <string name="home_games">Játékok</string> + <string name="home_search">Keresés</string> + <string name="home_settings">Beállítások</string> + <string name="empty_gamelist">Nem található fájl, vagy még nincs kiválasztva könyvtár.</string> + <string name="search_and_filter_games">Játékok keresése és szűrése</string> + <string name="select_games_folder">Játékmappa kiválasztása</string> + <string name="add_games_warning">Kihagyod a játékok mappa kiválasztását?</string> + <string name="add_games_warning_description">A játékok nem jelennek meg a Játékok listában, ha egy mappa nincs kijelölve.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Játékok keresése</string> + <string name="search_settings">Beállítások keresése</string> + <string name="games_dir_selected">Játékok könyvtár kiválasztva</string> + <string name="install_prod_keys">prod.keys telepítése</string> + <string name="install_prod_keys_description">Kiskereskedelmi játékok dekódolásához szükséges</string> + <string name="install_prod_keys_warning">Kihagyod a kulcsok hozzáadását?</string> + <string name="install_prod_keys_warning_description">A kiskereskedelmi játékok emulálásához érvényes kulcsokra van szükség. Csak a homebrew alkalmazások fognak működni, ha folytatod.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Értesítések</string> + <string name="notifications_description">Értesítési engedélyek megadása az alábbi gombbal.</string> + <string name="give_permission">Engedély megadása</string> + <string name="notification_warning">Kihagyod az értesítési engedély megadását?</string> + <string name="notification_warning_description">yuzu nem fog tudni értesíteni a fontos imformációkról</string> + <string name="permission_denied">Engedély megtagadva</string> + <string name="permission_denied_description">Túl gyakran utasítottad el a hozzáférést, így manuálisan kell jóváhagynod a rendszer beállításokban.</string> + <string name="about">A programról</string> + <string name="about_description">Build verzió, készítők, és még több</string> + <string name="warning_help">Segítség</string> + <string name="warning_skip">Kihagyás</string> + <string name="warning_cancel">Mégse</string> + <string name="install_amiibo_keys">Amiibo kulcsok telepítése</string> + <string name="install_amiibo_keys_description">Amiibo használata szükséges a játékhoz</string> + <string name="invalid_keys_file">Érvénytelen titkosítófájlok kiválasztva</string> + <string name="install_keys_success">Kulcsok sikeresen telepítve</string> + <string name="reading_keys_failure">Hiba történt a titkosítókulcsok olvasása során</string> + <string name="install_prod_keys_failure_extension_description">Győződj meg róla, hogy a titkosító fájlod .keys kiterjesztéssel rendelkezik, majd próbáld újra.</string> + <string name="install_amiibo_keys_failure_extension_description">Győződj meg róla, hogy a titkosító fájlod .bin kiterjesztéssel rendelkezik, majd próbáld újra.</string> + <string name="invalid_keys_error">Érvénytelen titkosítókulcsok</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">A kiválasztott fájl helytelen, vagy sérült. Állíts össze egy új kulcsot.</string> + <string name="install_gpu_driver">GPU illesztőprogram telepítése</string> + <string name="install_gpu_driver_description">Alternatív illesztőprogramok telepítése az esetlegesen elérhető teljesítmény és pontosság érdekében</string> + <string name="advanced_settings">Haladó beállítások</string> + <string name="advanced_settings_game">Haladó beállítások: %1$s</string> + <string name="settings_description">Emulátorbeállítások konfigurálása</string> + <string name="search_recently_played">Nemrég játszva</string> + <string name="search_recently_added">Nemrég hozzáadva</string> + <string name="search_retail">Kiskereskedelmi</string> + <string name="open_user_folder">yuzu mappa megnyitása</string> + <string name="open_user_folder_description">yuzu belső fájljainak kezelése</string> + <string name="theme_and_color_description">Az alkalmazás megjelenésének módosítása</string> + <string name="no_file_manager">Nem található fájlkezelő</string> + <string name="notification_no_directory_link">Nem sikerült megnyitni a yuzu könyvtárat</string> + <string name="notification_no_directory_link_description">Kérjük, manuálisan keresd meg a felhasználói mappát a fájlkezelő oldalsó paneljével.</string> + <string name="manage_save_data">Mentésadatok kezelése</string> + <string name="manage_save_data_description">Mentés található. Kérjük, válassz egyet az alábbi opciók közül.</string> + <string name="import_export_saves_description">Mentési fájlok importálás vagy exportálása</string> + <string name="save_file_imported_success">Sikeresen importálva</string> + <string name="save_file_invalid_zip_structure">Érvénytelen mentési könyvtárstruktúra</string> + <string name="save_file_invalid_zip_structure_description">Az első almappa neve a játék azonosítója kell, hogy legyen.</string> + <string name="import_saves">Importálás</string> + <string name="export_saves">Exportálás</string> + <string name="install_firmware">Firmware telepítés</string> + <string name="install_firmware_description">A firmwarenek ZIP archívumban kell lennie, és szükséges a játékok indításához</string> + <string name="firmware_installing">Firmware telepítése</string> + <string name="firmware_installed_success">Firmware sikeresen telepítve</string> + <string name="firmware_installed_failure">Firmware telepítése sikertelen</string> + <string name="firmware_installed_failure_description">Győződj meg róla, hogy a firmware nca fájlok a zip gyökerénél vannak, és próbáld meg újra.</string> + <string name="share_log">Hibakereső logok megosztása</string> + <string name="share_log_description">A yuzu naplófájl megosztása a problémák elhárításához</string> + <string name="share_log_missing">Nem található log fájl</string> + <string name="install_game_content">Játéktartalom telepítése</string> + <string name="install_game_content_description">Játékfrissítések vagy DLC telepítése</string> + <string name="installing_game_content">Tartalom telepítése...</string> + <string name="install_game_content_failure">Hiba történt a fájl(ok) NAND-ra telepítése közben</string> + <string name="install_game_content_failure_description">Győződj meg róla, hogy a tartalom valós, és a prod.keys fájl telepítve van.</string> + <string name="install_game_content_failure_base">Az alapjátékok telepítése nem engedélyezett az esetleges konfliktusok elkerülése érdekében.</string> + <string name="install_game_content_failure_file_extension">Csak NSP és XCI tartalom támogatott. Győződj meg róla, hogy a játéktartalom érvényes.</string> + <string name="install_game_content_failed_count">%1$d telepítési hiba</string> + <string name="install_game_content_success">Játéktartalom sikeresen telepítve</string> + <string name="install_game_content_success_install">%1$d sikeresen telepítve</string> + <string name="install_game_content_success_overwrite">%1$d sikeresen felülírva</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">Egyéni illesztőprogramok nem támogatottak</string> + <string name="custom_driver_not_supported_description">Egyéni illesztőprogram telepítése jelenleg nem támogatott ezen az eszközön.\nNézz vissza később, hátha hozzáadtuk a támogatását!</string> + <string name="manage_yuzu_data">yuzu adatok kezelése</string> + <string name="manage_yuzu_data_description">Firmware, kulcsok, felhasználói adatok és egyebek importálása/exportálása</string> + <string name="share_save_file">Mentési fájl megosztása</string> + <string name="export_save_failed">A mentés exportálása sikertelen</string> + + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia nem valódi</string> + <string name="copied_to_clipboard">Másolva a vágólapra</string> + <string name="about_app_description">Egy nyílt forráskódú Switch emulátor</string> + <string name="contributors">Hozzájárulók</string> + <string name="contributors_description">\u2764 által készítve a yuzu csapattól</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Projektek, amik nélkül a yuzu nem jöhetett volna létre Androidra</string> + <string name="user_data">Felhasználói adatok</string> + <string name="user_data_description">Az összes alkalmazásadat importálása/exportálása.\n\nA felhasználói adatok importálásakor az összes meglévő felhasználói adat törlődik!</string> + <string name="exporting_user_data">Felhasználói adatok exportálása...</string> + <string name="importing_user_data">Felhasználói adatok importálása...</string> + <string name="import_user_data">Felhasználói adatok importálása</string> + <string name="invalid_yuzu_backup">Érvénytelen yuzu biztonsági másolat</string> + <string name="user_data_export_success">Felhasználói adatok sikeresen exportálva</string> + <string name="user_data_import_success">Felhasználói adatok sikeresen importálva</string> + <string name="user_data_export_cancelled">Exportálás megszakítva</string> + <string name="user_data_import_failed_description">Ellenőrizd, hogy a felhasználói adatok mappái a zip mappa gyökerében vannak, és tartalmaznak egy konfig fájlt a config/config.ini címen, majd próbáld meg újra.</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Korai hozzáférés</string> + <string name="get_early_access">Szerezz korai hozzáférést</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Legújabb funkciók, korai hozzáférés a frissítésekhez, és sok más</string> + <string name="early_access_benefits">Korai hozzáférés előnyei</string> + <string name="cutting_edge_features">Legújabb funkciók</string> + <string name="early_access_updates">Korai hozzáférés a frissítésekhez</string> + <string name="no_manual_installation">Automatikus telepítések</string> + <string name="prioritized_support">Priorizált támogatás</string> + <string name="our_eternal_gratitude">Valamint az örök hálánk</string> + <string name="are_you_interested">Érdekel a dolog?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Sebességkorlát</string> + <string name="frame_limit_enable_description">Korlátozza az emuláció sebességét a normál sebesség adott százalékára.</string> + <string name="frame_limit_slider">Sebességkorlát százaléka</string> + <string name="frame_limit_slider_description">Az emuláció sebességét határozza meg. 100% a normál sebesség. A magasabb értékek növelik, az alacsonyabbak csökkentik a sebességkorlátot.</string> + <string name="cpu_accuracy">CPU pontosság</string> + <string name="value_with_units">%1$s%2$s</string> + + <!-- System settings strings --> + <string name="use_docked_mode">Dokkolt mód</string> + <string name="use_docked_mode_description">Növeli a felbontást, de csökkenti a teljesítményt. Kikapcsolás esetén a Kézi mód van használatban, ami kisebb felbontást, de nagyobb teljesítményt eredményez.</string> + <string name="emulated_region">Emulált régió</string> + <string name="emulated_language">Emulált nyelv</string> + <string name="select_rtc_date">Válassz RTC dátumot</string> + <string name="select_rtc_time">Válassz RTC időt</string> + <string name="use_custom_rtc">Egyéni RTC</string> + <string name="use_custom_rtc_description">Megadhatsz egy valós idejű órát, amely eltér a rendszer által használt órától.</string> + <string name="set_custom_rtc">Egyéni RTC beállítása</string> + + <!-- Graphics settings strings --> + <string name="renderer_accuracy">Pontosság szintje</string> + <string name="renderer_resolution">Felbontás (Kézi/Dockolt)</string> + <string name="renderer_vsync">VSync mód</string> + <string name="renderer_screen_layout">Orientáció</string> + <string name="renderer_aspect_ratio">Képarány</string> + <string name="renderer_scaling_filter">Ablakhoz alkalmazkodó szűrő</string> + <string name="renderer_anti_aliasing">Élsimítási módszer</string> + <string name="renderer_force_max_clock">Maximum órajel kényszerítése (csak Adreno)</string> + <string name="renderer_force_max_clock_description">Kényszeríti a GPU-t a lehető legnagyobb órajelen működésre (a hőmérséklet korlátozások továbbra is érvényben maradnak).</string> + <string name="renderer_asynchronous_shaders">Aszinkron árnyékolók használata</string> + <string name="renderer_asynchronous_shaders_description">Aszinkron módon fordítja az árnyékolókat, ami csökkenti az akadozást, de hibákat okozhat.</string> + <string name="renderer_reactive_flushing">Reaktív ürítés használata</string> + <string name="renderer_reactive_flushing_description">Javítja a renderelési pontosságot néhány játékban a teljesítmény rovására.</string> + <string name="use_disk_shader_cache">Lemez árnyékoló gyorsítótár</string> + <string name="use_disk_shader_cache_description">Csökkenti az akadásokat azáltal, hogy helyileg tárolja és tölti be a generált árnyékolókat.</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">CPU hibakeresés</string> + <string name="cpu_debug_mode_description">Lassú hibakereső módba állítja a CPU-t.</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">Grafikai hibakeresés</string> + <string name="renderer_debug_description">Lassú hibakeresési módba állítja a grafikus API-t .</string> + <!-- Audio settings strings --> + <string name="audio_output_engine">Kimeneti rendszer</string> + <string name="audio_volume">Hangerő</string> + <string name="audio_volume_description">Hangkimenet hangerejének megadása</string> + + <!-- Miscellaneous --> + <string name="slider_default">Alapértelmezett</string> + <string name="ini_saved">Beállítások elmentve</string> + <string name="gameid_saved">Beállítások elmentve a következőhöz: %1$s</string> + <string name="error_saving">Mentési hiba%1$s .ini: %2$s</string> + <string name="unimplemented_menu">Nem implementált menü</string> + <string name="loading">Betöltés...</string> + <string name="shutting_down">Leállítás...</string> + <string name="reset_setting_confirmation">Szeretnéd visszaállítani a beállítások az alapértelmezett értékekre?</string> + <string name="reset_to_default">Alaphelyzetbe állítás</string> + <string name="reset_all_settings">Alaphelyzetbe állítod a beállításokat?</string> + <string name="reset_all_settings_description">Minden haladó beállítás vissza lesz állítva az alapértelmezett konfigurációra. Ez a művelet nem vonható vissza.</string> + <string name="settings_reset">Beállítások alaphelyzetbe állítva</string> + <string name="close">Bezárás</string> + <string name="learn_more">Tudj meg többet</string> + <string name="auto">Automatikus</string> + <string name="submit">Küldés</string> + <string name="string_null">Nulla</string> + <string name="string_import">Importálás</string> + <string name="export">Exportálás</string> + <string name="export_failed">Exportálás sikertelen</string> + <string name="import_failed">Importálás sikertelen</string> + <string name="cancelling">Megszakítás</string> + + <!-- GPU driver installation --> + <string name="select_gpu_driver">Válassz GPU illesztőprogramot</string> + <string name="select_gpu_driver_title">Szeretnéd lecserélni a jelenlegi GPU illesztőprogramot?</string> + <string name="select_gpu_driver_install">Telepítés</string> + <string name="select_gpu_driver_default">Alapértelmezett</string> + <string name="select_gpu_driver_use_default">Alapértelmezett GPU illesztőprogram használata</string> + <string name="select_gpu_driver_error">Érvénytelen driver kiválasztva, a rendszer alapértelmezett lesz használva!</string> + <string name="system_gpu_driver">Rendszer GPU illesztőprogram</string> + <string name="installing_driver">Illesztőprogram telepítése...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Beállítások</string> + <string name="preferences_general">Általános</string> + <string name="preferences_system">Rendszer</string> + <string name="preferences_graphics">Grafika</string> + <string name="preferences_audio">Hang</string> + <string name="preferences_theme">Téma és színek</string> + <string name="preferences_debug">Hibakeresés</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">ROM titkosítva</string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Győződj meg róla, hogy a <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> fájl telepítve van, hogy a játékok visszafejthetők legyenek.]]></string> + <string name="loader_error_video_core">Hiba lépett fel a videómag inicializása során</string> + <string name="loader_error_video_core_description">Ezt általában egy nem kompatibilis GPU illesztő okozza. Egyéni GPU illesztőprogram telepítése megoldhatja a problémát.</string> + <string name="loader_error_invalid_format">Nem sikerült betölteni a ROM-ot</string> + <string name="loader_error_file_not_found">ROM fájl nem létezik</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Emuláció bezárása</string> + <string name="emulation_done">Kész</string> + <string name="emulation_fps_counter">FPS számláló</string> + <string name="emulation_toggle_controls">Irányítás átkapcsolása</string> + <string name="emulation_dpad_slide">D-pad csúsztatása</string> + <string name="emulation_haptics">Érintés haptikája</string> + <string name="emulation_show_overlay">Átfedés mutatása</string> + <string name="emulation_toggle_all">Össze átkapcsolása</string> + <string name="emulation_control_adjust">Átfedés testreszabása</string> + <string name="emulation_control_scale">Skálázás</string> + <string name="emulation_control_opacity">Átlátszóság</string> + <string name="emulation_touch_overlay_reset">Átfedés visszaállítása</string> + <string name="emulation_touch_overlay_edit">Átfedés módosítása</string> + <string name="emulation_pause">Emuláció szünetelése</string> + <string name="emulation_unpause">Emuláció folytatása</string> + <string name="emulation_input_overlay">Átfedés beállításai</string> + + <string name="load_settings">Beállítások betöltése...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Szoftver billenytűzet</string> + + <!-- Errors and warnings --> + <string name="abort_button">Megszakítás</string> + <string name="continue_button">Folytatás</string> + <string name="system_archive_not_found">Nem található rendszerarchívum</string> + <string name="system_archive_not_found_message">%s hiányzik. Kérjük, mentsd ki a rendszerarchívumaidat.\nAz emuláció folytatása összeomlásokhoz és hibákhoz vezethet.</string> + <string name="system_archive_general">Egy rendszerarchívum</string> + <string name="save_load_error">Mentési/betöltési hiba</string> + <string name="fatal_error">Végzetes hiba</string> + <string name="fatal_error_message">Végzetes hiba történt. Ellenőrizd a logot a részletekért.\nAz emuláció folytatása összeomlást és hibákat eredményzhet.</string> + <string name="performance_warning">Ennek a beállításnak a kikapcsolása jelentős mértékben csökkenti a teljesítményt! A legjobb élmény érdekében javasolt a beállítás bekapcsolva tartása.</string> + <string name="device_memory_inadequate">Eszköz RAM: %1$s\nAjánlott: %2$s</string> + <string name="memory_formatted">%1$s %2$s</string> + <string name="no_game_present">Nincs indítható játék!</string> + + <!-- Region Names --> + <string name="region_japan">Japán</string> + <string name="region_usa">USA</string> + <string name="region_europe">Európa</string> + <string name="region_australia">Ausztrália</string> + <string name="region_china">Kína</string> + <string name="region_korea">Korea</string> + <string name="region_taiwan">Tajvan</string> + + <!-- Memory Sizes --> + <string name="memory_byte">Bájt</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> + + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">Nincs</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Normál</string> + <string name="renderer_accuracy_high">Magas</string> + <string name="renderer_accuracy_extreme">Extrém (Lassú)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Lassú)</string> + <string name="resolution_three">3X (2160p/3240p) (Lassú)</string> + <string name="resolution_four">4X (2880p/4320p) (Lassú)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Azonnali (Ki)</string> + <string name="renderer_vsync_mailbox">Postaláda</string> + <string name="renderer_vsync_fifo">FIFO (Be)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxált</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Legközelebbi szomszéd</string> + <string name="scaling_filter_bilinear">Bilineáris</string> + <string name="scaling_filter_bicubic">Bikubikus</string> + <string name="scaling_filter_gaussian">Gauss-féle</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Nincs</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <!-- Screen Layouts --> + <string name="screen_layout_landscape">Fekvő</string> + <string name="screen_layout_portrait">Álló</string> + <string name="screen_layout_auto">Automatikus</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Alapértelmezett (16:9)</string> + <string name="ratio_force_four_three">4:3 kényszerítése</string> + <string name="ratio_force_twenty_one_nine">21:9 kényszerítése</string> + <string name="ratio_force_sixteen_ten">16:10 kényszerítése</string> + <string name="ratio_stretch">Ablakhoz nyújtás</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Pontos</string> + <string name="cpu_accuracy_unsafe">Nem biztonságos</string> + <string name="cpu_accuracy_paranoid">Paranoid (Lassú)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">D-pad</string> + <string name="gamepad_left_stick">Bal kar</string> + <string name="gamepad_right_stick">Jobb kar</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">Képernyőmentés</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">Árnyékolók előkészítése</string> + <string name="building_shaders">Árnyékolók létrehozása</string> + + <!-- Theme options --> + <string name="change_app_theme">Alkalmazás témájának módosítása</string> + <string name="theme_default">Alapértelmezett</string> + <!-- Theme Modes --> + <string name="change_theme_mode">Téma váltása</string> + <string name="theme_mode_follow_system">Rendszerbeállítások használata</string> + <string name="theme_mode_light">Világos</string> + <string name="theme_mode_dark">Sötét</string> + + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Fekete háttér</string> + <string name="use_black_backgrounds_description">Sötét téma használatakor fekete háttér használata.</string> + + <!-- Picture-In-Picture --> + <string name="picture_in_picture">Kép a képben</string> + <string name="picture_in_picture_description">Ablak minimalizálása, amikor háttérbe kerül</string> + <string name="pause">Szünet</string> + <string name="play">Lejátszás</string> + <string name="mute">Némítás</string> + <string name="unmute">Némítás feloldása</string> + + <!-- Licenses screen strings --> + <string name="licenses">Licenszek</string> + <string name="license_fidelityfx_fsr_description">Magas minőségű felskálázás az AMD-től</string> + </resources> diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml index 09c9345b0..5afebb4c4 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.<br /><br />Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string> <string name="emulation_notification_channel_name">L\'emulatore è attivo</string> @@ -13,9 +13,9 @@ <string name="welcome">Benvenuto!</string> <string name="welcome_description">Scopri come configurare <b>yuzu</b> e passare all\'emulazione.</string> <string name="get_started">Iniziare</string> - <string name="keys">Pulsanti</string> + <string name="keys">Chiavi</string> <string name="keys_description">Seleziona il tuo file <b>prod.keys</b> con il pulsante in basso.</string> - <string name="select_keys">Selezione Pulsanti</string> + <string name="select_keys">Seleziona le chiavi</string> <string name="games">Giochi</string> <string name="games_description">Seleziona la cartella <b>Games</b> con il pulsante in basso.</string> <string name="done">Fatto</string> @@ -25,6 +25,7 @@ <string name="back">Indietro</string> <string name="add_games">Aggiungi giochi</string> <string name="add_games_description">Seleziona la cartella dei giochi</string> + <string name="step_complete">Completato!</string> <!-- Home strings --> <string name="home_games">Giochi</string> @@ -38,6 +39,7 @@ <string name="add_games_warning_description">I giochi non saranno mostrati nella lista dei giochi se una cartella non è selezionata.</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> <string name="home_search_games">Cerca giochi</string> + <string name="search_settings">Cerca impostazione</string> <string name="games_dir_selected">Cartella dei giochi selezionata</string> <string name="install_prod_keys">Installa prod.keys</string> <string name="install_prod_keys_description">Necessario per decrittografare i giochi</string> @@ -61,15 +63,18 @@ <string name="invalid_keys_file">Selezionate chiavi non valide</string> <string name="install_keys_success">Chiavi installate correttamente</string> <string name="reading_keys_failure">Errore durante la lettura delle chiavi di crittografia</string> + <string name="install_prod_keys_failure_extension_description">Controlla che le tue chiavi abbiano l\'estensione .keys e prova di nuovo.</string> + <string name="install_amiibo_keys_failure_extension_description">Controlla che le tue chiavi abbiano l\'estensione .bin e prova di nuovo</string> <string name="invalid_keys_error">Chiavi di crittografia non valide</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">Il file selezionato è incorretto o corrotto. Per favore riesegui il dump delle tue chiavi.</string> <string name="install_gpu_driver">Installa i driver GPU</string> <string name="install_gpu_driver_description">Installa driver alternativi per potenziali prestazioni migliori o accuratezza.</string> <string name="advanced_settings">Impostazioni avanzate</string> + <string name="advanced_settings_game">Impostazioni Avanzate: %1$s</string> <string name="settings_description">Configura le impostazioni dell\'emulatore</string> - <string name="search_recently_played">Giocato recentemente</string> - <string name="search_recently_added">Aggiunto recentemente</string> + <string name="search_recently_played">Giocati recentemente</string> + <string name="search_recently_added">Aggiunti recentemente</string> <string name="search_retail">Rivenditore</string> <string name="search_homebrew">Homebrew</string> <string name="open_user_folder">Apri la cartella di yuzu</string> @@ -86,6 +91,33 @@ <string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string> <string name="import_saves">Importa</string> <string name="export_saves">Esporta</string> + <string name="install_firmware">Installa firmware</string> + <string name="install_firmware_description">Il firmware deve essere in un archivio ZIP ed è necessario per avviare alcuni giochi</string> + <string name="firmware_installing">Installando il firmware</string> + <string name="firmware_installed_success">Firmware installato con successo</string> + <string name="firmware_installed_failure">L\'installazione del firmware è fallita</string> + <string name="firmware_installed_failure_description">Accertati che i file .nca del firmware siano contenuti direttamente nella radice dello .zip e riprova.</string> + <string name="share_log">Condividi log di debug</string> + <string name="share_log_description">Condividi i log di yuzu per ricevere supporto</string> + <string name="share_log_missing">Nessun file di log trovato</string> + <string name="install_game_content">Installa contenuti di gioco</string> + <string name="install_game_content_description">Installa aggiornamenti o DLC</string> + <string name="installing_game_content">Installazione dei contenuti...</string> + <string name="install_game_content_failure">Errore durante l\'installazione del contenuto in NAND.</string> + <string name="install_game_content_failure_description">Accertati che i contenuti da installare siano validi e che le prod.keys siano presenti.</string> + <string name="install_game_content_failure_base">Installare i giochi base in NAND non è permesso, perché potrebbe causare dei conflitti con altri tipi di contenuti(Aggiornamenti e DLC)</string> + <string name="install_game_content_failure_file_extension">Solo i tipi NSP e XCI sono supportati. Verifica che i contenuti di gioco siano validi.</string> + <string name="install_game_content_failed_count">Errori di installazione: %1$d</string> + <string name="install_game_content_success">Contenuto/i di gioco installato/i con successo.</string> + <string name="install_game_content_success_install">%1$dinstallato con successo.</string> + <string name="install_game_content_success_overwrite">%1$dsovrascritto con successo</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">I driver personalizzati non sono supportati.</string> + <string name="custom_driver_not_supported_description">I driver personalizzati non sono attualmente supportati su questo dispositivo.\n Ricontrolla in futuro.</string> + <string name="manage_yuzu_data">Gestisci i dati di Yuzu</string> + <string name="manage_yuzu_data_description">Importa/Esporta il firmware, le keys, i dati utente, e altro!</string> + <string name="share_save_file">Condividi i tuoi dati di salvataggio</string> + <string name="export_save_failed">Errore durante l\'esportazione del salvataggio</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia non è reale</string> @@ -94,7 +126,18 @@ <string name="contributors">Collaboratori</string> <string name="contributors_description">Realizzato con \u2764 dal team yuzu</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Progetti che rendono yuzu per Android possibile</string> <string name="build">Compilazione</string> + <string name="user_data">Dati Utente</string> + <string name="user_data_description">Importa/Esporta tutti i dati dell\'applicazione.\n\nDurante l\'importazione dei Dati Utente, quelli già esistenti verranno ELIMINATI.</string> + <string name="exporting_user_data">Esportazione dei Dati Utente...</string> + <string name="importing_user_data">Importazione dei Dati Utente...</string> + <string name="import_user_data">Importa i Dati Utente</string> + <string name="invalid_yuzu_backup">Backup di Yuzu Invalido</string> + <string name="user_data_export_success">Dati Utente esportati con successo</string> + <string name="user_data_import_success">Dati Utente importati con successo.</string> + <string name="user_data_export_cancelled">Esportazione annullata</string> + <string name="user_data_import_failed_description">Assicurati che la cartella dei Dati dell\'utente stiano nella radice del file.zip e che sia presente una cartella config in config/config.ini, poi, riprova.</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> @@ -114,41 +157,53 @@ <string name="are_you_interested">Sei interessato?</string> <!-- General settings strings --> - <string name="frame_limit_enable">Abilita il limite di velocità</string> - <string name="frame_limit_enable_description">Quando abilitato, la velocità di emulazione verrà limitata a una specifica percentuale della velocità normale.</string> + <string name="frame_limit_enable">Limita velocità</string> + <string name="frame_limit_enable_description">Limita la velocità dell\'emulazione a una specifica percentuale della velocità normale.</string> <string name="frame_limit_slider">Limite velocità percentuale</string> - <string name="frame_limit_slider_description">Specifica la percentuale del limite della velocità di emulazione. Con quella preimpostata al 100% l\'emulazione verrà limitata alla velocità normale. Valori più alti o bassi aumenteranno o diminuiranno il limite di velocità.</string> + <string name="frame_limit_slider_description">Specifica la percentuale per limitare la velocità di emulazione. 100% è la velocità normale. Valori maggiori o minori aumenteranno o diminuiranno il limite di velocità</string> <string name="cpu_accuracy">Accuratezza della CPU</string> + <string name="value_with_units">%1$s%2$s</string> <!-- System settings strings --> - <string name="use_docked_mode">Modalità docked</string> - <string name="use_docked_mode_description">Emula in modalità docked, questo aumenta la risoluzione a spese delle performance.</string> + <string name="use_docked_mode">Modalità Docked</string> + <string name="use_docked_mode_description">Aumenta la risoluzione, diminuendo le performance. La modalità portatile è usata quando disabilitato, diminuendo la risoluzione e aumentando le performance.</string> <string name="emulated_region">Regione emulata</string> <string name="emulated_language">Lingua emulata</string> - <string name="select_rtc_date">Seleziona la data dall\'orologio in tempo reale</string> - <string name="select_rtc_time">Seleziona il tempo dall\'orologio in tempo reale</string> - <string name="use_custom_rtc">Abilità l\'orologio in tempo reale personalizzato</string> - <string name="use_custom_rtc_description">Questa impostazione ti permette di impostare un orologio in tempo reale personalizzato separato da quello del tuo sistema corrente.</string> - <string name="set_custom_rtc">Imposta l\'orologio in tempo reale personalizzato</string> + <string name="select_rtc_date">Imposta la data </string> + <string name="select_rtc_time">Imposta l\'ora, i minuti e i secondi.</string> + <string name="use_custom_rtc">RTC Personalizzato</string> + <string name="use_custom_rtc_description">Ti permette di impostare un orologio in tempo reale personalizzato, completamente separato da quello di sistema.</string> + <string name="set_custom_rtc">Imposta un orologio in tempo reale personalizzato</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">Livello di accuratezza</string> - <string name="renderer_resolution">Risoluzione</string> + <string name="renderer_resolution">Risoluzione (Portatile/Docked)</string> <string name="renderer_vsync">Modalità VSync</string> - <string name="renderer_aspect_ratio">Rapporto d\'aspetto</string> - <string name="renderer_scaling_filter">Filtro di adattamento alla finestra</string> + <string name="renderer_screen_layout">Orientamento</string> + <string name="renderer_aspect_ratio">Rapporto d\'aspetto: </string> + <string name="renderer_scaling_filter">Filtro adattivo della finestra </string> <string name="renderer_anti_aliasing">Metodo di anti-aliasing</string> <string name="renderer_force_max_clock">Forza clock massimi (solo Adreno)</string> <string name="renderer_force_max_clock_description">Forza la GPU a girare col massimo clock possibile (i vincoli alla temperatura saranno comunque applicati)</string> <string name="renderer_asynchronous_shaders">Usa shaders asincrone</string> - <string name="renderer_asynchronous_shaders_description">Compila le shaders asincronamente, questo riduce lo shutter ma potrebbe introdurre dei glitch. </string> - <string name="renderer_debug">Abilità il debug grafico</string> - <string name="renderer_debug_description">Quando l\'opzione è selezionata, l\'API grafica entra in una modalità di debug più lenta</string> - <string name="use_disk_shader_cache">Usa cache shader su disco</string> - <string name="use_disk_shader_cache_description">Riduce lo stuttering salvando e caricando le shader generate sul disco.</string> + <string name="renderer_asynchronous_shaders_description">Compila le shader in modo asincrone, riducendo lo stutter. Può causare glitch grafici.</string> + <string name="renderer_reactive_flushing">Abilita il Reactive Flushing</string> + <string name="renderer_reactive_flushing_description">Migliora l\'accuratezza della grafica in alcuni giochi, al costo delle performance.</string> + <string name="use_disk_shader_cache">Usa la cache delle shader</string> + <string name="use_disk_shader_cache_description">Riduce lo stuttering caricando le shader già compilate all\'avvio.</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">Debug della CPU</string> + <string name="cpu_debug_mode_description">Imposta la CPU in modalità Debug (Più lento)</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">Debug GPU</string> + <string name="renderer_debug_description">Imposta l\'API grafica in uno stato dedicato al Debugging. Impatta di molto sulle performance.</string> + <string name="fastmem">Fastmem</string> <!-- Audio settings strings --> + <string name="audio_output_engine">Motore di Output</string> <string name="audio_volume">Volume</string> <string name="audio_volume_description">Specifica il volume dell\'audio in uscita.</string> @@ -157,14 +212,24 @@ <string name="ini_saved">Impostazioni salvate</string> <string name="gameid_saved">Impostazioni salvate per %1$s</string> <string name="error_saving">Errore nel salvare %1$s.ini %2$s</string> + <string name="unimplemented_menu">Menu non implementato</string> <string name="loading">Caricamento…</string> + <string name="shutting_down">Spegnimento...</string> <string name="reset_setting_confirmation">Vuoi ripristinare queste impostazioni al loro valore originale?</string> <string name="reset_to_default">Riportare alle impostazioni originali</string> <string name="reset_all_settings">Resettare tutte le impostazioni?</string> - <string name="reset_all_settings_description">Tutte le Impostazioni Avanzate saranno ripristinate a quelle originali. Questa operazione non è reversibile</string> + <string name="reset_all_settings_description">Le impostazione avanzate verranno completamente reimpostate. Questa operazione è IRREVERSIBILE.</string> <string name="settings_reset">Reimposta le impostazioni</string> <string name="close">Chiudi</string> <string name="learn_more">Per saperne di più</string> + <string name="auto">Automatico</string> + <string name="submit">Invia</string> + <string name="string_null">Nullo</string> + <string name="string_import">Importa</string> + <string name="export">Esporta</string> + <string name="export_failed">Esportazione Fallita</string> + <string name="import_failed">Importazione Fallita</string> + <string name="cancelling">Cancellazione</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Seleziona il driver della GPU</string> @@ -172,6 +237,7 @@ <string name="select_gpu_driver_install">Installa</string> <string name="select_gpu_driver_default">Predefinito</string> <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string> + <string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string> <string name="system_gpu_driver">Driver GPU del sistema</string> <string name="installing_driver">Installando i driver...</string> @@ -182,10 +248,11 @@ <string name="preferences_graphics">Grafica</string> <string name="preferences_audio">Audio</string> <string name="preferences_theme">Tema e colori</string> + <string name="preferences_debug">Debug</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">La tua ROM è criptata</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[Per favore segui la guida per eseguire il dump della <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">cartuccia di gioco</a> o i <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">titoli installati</a>.]]></string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Segui la nostra guida per fare il <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">dump delle tue cartucce di gioco</a>oppure <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">dei titoli già installati</a>.]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[Per favore assicurati che il file <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> sia installato in modo che i giochi possano essere decrittati.]]></string> <string name="loader_error_video_core">È stato riscontrato un errore nell\'inizializzazione del core video</string> <string name="loader_error_video_core_description">Questo è causato solitamente dal driver incompatibile di una GPU. L\'installazione di driver GPU personalizzati potrebbe risolvere questo problema.</string> @@ -193,28 +260,28 @@ <string name="loader_error_file_not_found">Il file della ROM non esiste</string> <!-- Emulation Menu --> - <string name="emulation_exit">Uscire dall\'emulazione</string> + <string name="emulation_exit">Arresta emulazione</string> <string name="emulation_done">Fatto</string> - <string name="emulation_fps_counter">Contatore degli FPS</string> + <string name="emulation_fps_counter">Contatore FPS</string> <string name="emulation_toggle_controls">Controlli a interruttore</string> <string name="emulation_rel_stick_center">Centro relativo degli Stick</string> - <string name="emulation_dpad_slide">Slittamento del Pad Direzionale</string> - <string name="emulation_haptics">Aptico</string> - <string name="emulation_show_overlay">Mostra Overlay</string> - <string name="emulation_toggle_all">Attiva/disattiva tutto</string> - <string name="emulation_control_adjust">Aggiusta Overlay</string> + <string name="emulation_dpad_slide">DPad A Scorrimento</string> + <string name="emulation_haptics">Feedback Aptico</string> + <string name="emulation_show_overlay">Mostra l\'Overlay</string> + <string name="emulation_toggle_all">Attiva/Disattiva tutto</string> + <string name="emulation_control_adjust">Modifica l\'Overlay</string> <string name="emulation_control_scale">Scala</string> <string name="emulation_control_opacity">Opacità</string> - <string name="emulation_touch_overlay_reset">Reimposta Overlay</string> - <string name="emulation_touch_overlay_edit">Modifica Overlay</string> - <string name="emulation_pause">Metti in pausa l\'emulazione</string> - <string name="emulation_unpause">Riprendi Emulazione</string> - <string name="emulation_input_overlay">Impostazioni Overlay</string> + <string name="emulation_touch_overlay_reset">Reimposta l\'Overlay</string> + <string name="emulation_touch_overlay_edit">Modifica l\'Overlay</string> + <string name="emulation_pause">Sospendi l\'emulazione</string> + <string name="emulation_unpause">Riprendi l\'emulazione</string> + <string name="emulation_input_overlay">Opzioni overlay</string> - <string name="load_settings">Caricamento delle impostazioni...</string> + <string name="load_settings">Carico le impostazioni...</string> <!-- Software keyboard --> - <string name="software_keyboard">Tastiera software</string> + <string name="software_keyboard">Tastiera Software</string> <!-- Errors and warnings --> <string name="abort_button">Interrompi</string> @@ -226,6 +293,9 @@ <string name="fatal_error">Errore Fatale</string> <string name="fatal_error_message">Un errore fatale è accaduto. Controlla i log per i dettagli.\nContinuare ad emulare potrebbe portare bug o causare crash.</string> <string name="performance_warning">Disattivare questa impostazione può ridurre significativamente le performance di emulazione! Per una migliore esperienza, è consigliato lasciare questa impostazione attivata.</string> + <string name="device_memory_inadequate">RAM Totale:%1$s\nRaccomandati: %2$s</string> + <string name="memory_formatted">%1$s%2$s</string> + <string name="no_game_present">Non è presente alcun gioco avviabile.</string> <!-- Region Names --> <string name="region_japan">Giappone</string> @@ -236,7 +306,14 @@ <string name="region_korea">Corea</string> <string name="region_taiwan">Taiwan</string> - <!-- Language Names --> + <!-- Memory Sizes --> + <string name="memory_byte">Byte</string> + <string name="memory_kilobyte">Kb</string> + <string name="memory_megabyte">Mb</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">Tb</string> + <string name="memory_petabyte">Pb</string> + <string name="memory_exabyte">Eb</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> @@ -274,12 +351,17 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <!-- Screen Layouts --> + <string name="screen_layout_landscape">Layout Orizzontale</string> + <string name="screen_layout_portrait">Layout Verticale</string> + <string name="screen_layout_auto">Automatico</string> + <!-- Aspect Ratios --> <string name="ratio_default">Predefinito (16:9)</string> <string name="ratio_force_four_three">Forza 4:3</string> <string name="ratio_force_twenty_one_nine">Forza 21:9</string> <string name="ratio_force_sixteen_ten">Forza 16:10</string> - <string name="ratio_stretch">Allunga a finestra</string> + <string name="ratio_stretch">Adatta alla finestra</string> <!-- CPU Accuracy --> <string name="cpu_accuracy_accurate">Accurata</string> @@ -287,9 +369,9 @@ <string name="cpu_accuracy_paranoid">Paranoico (Lento)</string> <!-- Gamepad Buttons --> - <string name="gamepad_d_pad">D-Pad</string> - <string name="gamepad_left_stick">Levetta sinistra</string> - <string name="gamepad_right_stick">Levetta destra</string> + <string name="gamepad_d_pad">D-pad</string> + <string name="gamepad_left_stick">Analogico sinistro</string> + <string name="gamepad_right_stick">Analogico destro</string> <string name="gamepad_home">Home</string> <string name="gamepad_screenshot">Screenshot</string> @@ -298,7 +380,7 @@ <string name="building_shaders">Costruendo gli shaders</string> <!-- Theme options --> - <string name="change_app_theme">Cambia il tema dell\'app</string> + <string name="change_app_theme">Cambia tema dell\'app</string> <string name="theme_default">Predefinito</string> <string name="theme_material_you">Material You</string> @@ -308,8 +390,22 @@ <string name="theme_mode_light">Chiaro</string> <string name="theme_mode_dark">Scuro</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">Usa sfondi neri</string> + <string name="use_black_backgrounds">Sfondi neri</string> <string name="use_black_backgrounds_description">Quando utilizzi il tema scuro, applica sfondi neri.</string> -</resources> + <!-- Picture-In-Picture --> + <string name="picture_in_picture">Picture in Picture</string> + <string name="picture_in_picture_description">Minimizza la finestra quando viene impostata in background</string> + <string name="pause">Pausa</string> + <string name="play">Gioca</string> + <string name="mute">Silenzia</string> + <string name="unmute">Riattiva</string> + + <!-- Licenses screen strings --> + <string name="licenses">Licenze</string> + <string name="license_fidelityfx_fsr_description">Upscaling di alta qualità da parte di AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml index a0ea78bef..3be4e7d26 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml @@ -1,11 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> - <string name="app_disclaimer">このソフトウェアは、Nintendo Switch用のゲームを実行します。 ゲームソフトやキーは含まれません。<br /><br />事前に、 <![CDATA[<b> prod.keys </b>]]> ファイルをデバイスのストレージに配置しておいてください。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string> + <string name="app_disclaimer">このソフトウェアでは、Nintendo Switchのゲームを実行できます。 ゲームソフトやキーは含まれません。<br /><br />事前に、 <![CDATA[<b> prod.keys </b>]]> ファイルをストレージに配置しておいてください。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string> <string name="emulation_notification_channel_name">エミュレーションが有効です</string> <string name="emulation_notification_channel_description">エミュレーションの実行中に常設通知を表示します。</string> <string name="emulation_notification_running">yuzu は実行中です</string> - <string name="notice_notification_channel_description">問題が発生したときに通知を表示します。</string> + <string name="notice_notification_channel_name">通知とエラー</string> + <string name="notice_notification_channel_description">問題の発生時に通知を表示します。</string> <string name="notification_permission_not_granted">通知が許可されていません!</string> <!-- Setup strings --> @@ -16,7 +17,7 @@ <string name="keys_description">下のボタンから <b>prod.keys</b> ファイルを選択してください。</string> <string name="select_keys">キーを選択</string> <string name="games">ゲーム</string> - <string name="games_description">下のボタンから<b>ゲーム</b>があるフォルダを選択してください。</string> + <string name="games_description">下のボタンから<b>ゲーム</b>のあるフォルダを選択してください。</string> <string name="done">完了</string> <string name="done_description">準備が完了しました。\nゲームをお楽しみください!</string> <string name="text_continue">続行</string> @@ -24,48 +25,53 @@ <string name="back">戻る</string> <string name="add_games">ゲームを追加</string> <string name="add_games_description">ゲームフォルダを選択</string> + <string name="step_complete">完了!</string> <!-- Home strings --> <string name="home_games">ゲーム</string> <string name="home_search">検索</string> <string name="home_settings">設定</string> - <string name="empty_gamelist">ファイルが見つからないか、ゲームディレクトリがまだ選択されていません。</string> + <string name="empty_gamelist">ファイルが存在しないかゲームフォルダが選択されていません。</string> <string name="search_and_filter_games">ゲームの検索と絞り込み</string> - <string name="select_games_folder">ゲームフォルダを選択</string> - <string name="select_games_folder_description">yuzu がゲームリストに追加できるようにします</string> + <string name="select_games_folder">ゲームフォルダ</string> + <string name="select_games_folder_description">ゲームをyuzuのゲームリストに追加します</string> <string name="add_games_warning">ゲームフォルダの選択をスキップしますか?</string> - <string name="add_games_warning_description">フォルダを選択しない場合、ゲームはゲームリストに表示されません。</string> + <string name="add_games_warning_description">フォルダを選択しないと、ゲームがリストに表示されません。</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> <string name="home_search_games">ゲームを検索</string> - <string name="games_dir_selected">ゲームディレクトリが選択されました</string> - <string name="install_prod_keys">prod.keys をインストール</string> - <string name="install_prod_keys_description">ゲームの復号化に必要</string> + <string name="search_settings">検索設定</string> + <string name="games_dir_selected">フォルダを選択しました</string> + <string name="install_prod_keys">prod.keys</string> + <string name="install_prod_keys_description">製品版ゲームの復号化に必要です</string> <string name="install_prod_keys_warning">キーの追加をスキップしますか?</string> <string name="install_prod_keys_warning_description">製品版ゲームのエミュレーションには、有効なキーが必要です。続行すると自作アプリしか機能しません。</string> <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> <string name="notifications">通知</string> - <string name="notifications_description">下のボタンで通知の権限を許可してください。</string> + <string name="notifications_description">下のボタンで通知を許可してください。</string> <string name="give_permission">許可</string> <string name="notification_warning">通知の許可をスキップしますか?</string> <string name="notification_warning_description">yuzuは重要なお知らせを通知できません。</string> <string name="permission_denied">権限が拒否されました</string> - <string name="permission_denied_description">この権限を複数回拒否したため、システム設定で手動で許可する必要があります。</string> + <string name="permission_denied_description">この権限を複数回拒否したため、設定から手動で許可する必要があります。</string> <string name="about">情報</string> <string name="about_description">ビルドバージョン、クレジットなど</string> <string name="warning_help">ヘルプ</string> <string name="warning_skip">スキップ</string> <string name="warning_cancel">キャンセル</string> - <string name="install_amiibo_keys">Amiibo キーをインストール</string> - <string name="install_amiibo_keys_description">ゲーム内での Amiibo の使用に必要</string> - <string name="invalid_keys_file">無効なキーファイルが選択されました</string> + <string name="install_amiibo_keys">Amiibo</string> + <string name="install_amiibo_keys_description">ゲーム内での Amiibo の使用に必要です</string> + <string name="invalid_keys_file">無効なキーファイルです</string> <string name="install_keys_success">正常にインストールされました</string> - <string name="reading_keys_failure">暗号化キーの読み取りエラー</string> - <string name="invalid_keys_error">暗号化キーが無効です</string> + <string name="reading_keys_failure">暗号化キーの読み込み失敗</string> + <string name="install_prod_keys_failure_extension_description">キーの拡張子が.keysであることを確認し、再度お試しください。</string> + <string name="install_amiibo_keys_failure_extension_description">キーの拡張子が.binであることを確認し、再度お試しください。</string> + <string name="invalid_keys_error">暗号化キーが無効</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> - <string name="install_keys_failure_description">選択されたファイルが不正または破損しています。キーを再ダンプしてください。</string> - <string name="install_gpu_driver">GPUドライバーをインストール</string> + <string name="install_keys_failure_description">ファイルが間違っているか破損しています。キーを再ダンプしてください。</string> + <string name="install_gpu_driver">GPUドライバー</string> <string name="install_gpu_driver_description">代替ドライバーをインストールしてパフォーマンスや精度を向上させます</string> <string name="advanced_settings">高度な設定</string> + <string name="advanced_settings_game">高度な設定: %1$s</string> <string name="settings_description">エミュレーターの設定を構成します</string> <string name="search_recently_played">最近プレイした</string> <string name="search_recently_added">最近追加された</string> @@ -77,15 +83,34 @@ <string name="no_file_manager">ファイルマネージャーが見つかりませんでした</string> <string name="notification_no_directory_link">yuzuのディレクトリを開けません</string> <string name="notification_no_directory_link_description">ファイルマネージャのサイドパネルでユーザーフォルダを手動で探してください。</string> - <string name="manage_save_data">セーブデータを管理</string> - <string name="manage_save_data_description">セーブデータが見つかりました。以下のオプションから選択してください。</string> + <string name="manage_save_data">セーブデータ</string> + <string name="manage_save_data_description">セーブデータが見つかりました。操作を選択してください。</string> <string name="import_export_saves_description">セーブファイルをインポート/エクスポート</string> <string name="save_file_imported_success">インポートが完了しました</string> - <string name="save_file_invalid_zip_structure">セーブデータのディレクトリ構造が無効です</string> + <string name="save_file_invalid_zip_structure">セーブデータのディレクトリ構造が無効</string> <string name="save_file_invalid_zip_structure_description">最初のサブフォルダ名は、ゲームのタイトルIDである必要があります。</string> <string name="import_saves">インポート</string> <string name="export_saves">エクスポート</string> - + <string name="install_firmware">ファームウェア</string> + <string name="install_firmware_description">ファームウェアはZIPアーカイブである必要があり、一部のゲームを起動するのに必要です</string> + <string name="firmware_installing">ファームウェアをインストール中</string> + <string name="firmware_installed_success">インストールが完了しました</string> + <string name="firmware_installed_failure">インストール失敗</string> + <string name="share_log">デバッグログ</string> + <string name="share_log_description">yuzuのログファイルを共有して問題をデバッグします</string> + <string name="share_log_missing">ログが見つかりません</string> + <string name="install_game_content">追加コンテンツ</string> + <string name="install_game_content_description">更新データやDLCをインストールします</string> + <string name="installing_game_content">コンテンツをインストール中...</string> + <string name="install_game_content_failure_file_extension">NSPとXCI形式のコンテンツのみサポートされています。ゲームコンテンツが有効なものであるかご確認ください。</string> + <string name="install_game_content_failed_count">%1$d のインストールエラー</string> + <string name="install_game_content_success">ゲームコンテンツのインストールに成功しました</string> + <string name="install_game_content_success_install">%1$d のインストールに成功しました</string> + <string name="install_game_content_success_overwrite">%1$d の上書きに成功しました</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">カスタムドライバはサポートされていません</string> + <string name="manage_yuzu_data">yuzu データを管理</string> + <string name="share_save_file">セーブファイルを共有</string> <!-- About screen strings --> <string name="gaia_is_not_real">ガイアは実在しない</string> <string name="copied_to_clipboard">クリップボードにコピーしました</string> @@ -93,7 +118,15 @@ <string name="contributors">貢献者</string> <string name="contributors_description">yuzuチームの\u2764で作られた</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">yuzu for Androidの作成を可能にしたプロジェクト</string> <string name="build">ビルド</string> + <string name="user_data">ユーザデータ</string> + <string name="exporting_user_data">ユーザデータをエクスポート中...</string> + <string name="importing_user_data">ユーザデータをインポート中...</string> + <string name="import_user_data">ユーザデータをインポート</string> + <string name="user_data_export_success">ユーザデータのエクスポートに成功しました</string> + <string name="user_data_import_success">ユーザデータのインポートに成功しました</string> + <string name="user_data_export_cancelled">エクスポートをキャンセルしました</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> @@ -105,72 +138,91 @@ <string name="get_early_access_description">最先端の機能、アップデートの早期アクセスなど</string> <string name="early_access_benefits">早期アクセスのメリット</string> <string name="cutting_edge_features">最先端の機能</string> - <string name="early_access_updates">アップデートの早期アクセス</string> + <string name="early_access_updates">アップデートへの早期アクセス</string> <string name="no_manual_installation">手動インストールが不要</string> - <string name="prioritized_support">優先的なサポート</string> + <string name="prioritized_support">優先サポート</string> <string name="helping_game_preservation">ゲームの保存に貢献</string> - <string name="our_eternal_gratitude">私たちの永遠の感謝</string> + <string name="our_eternal_gratitude">私たちから永遠の感謝</string> <string name="are_you_interested">興味がありますか?</string> <!-- General settings strings --> - <string name="frame_limit_enable">速度制限を有効化</string> - <string name="frame_limit_enable_description">有効にすると、エミュレーション速度が任意の割合に制限されます。</string> - <string name="frame_limit_slider">エミュレーション速度の制限</string> - <string name="frame_limit_slider_description">エミュレーション速度を制限する割合を指定します。デフォルトの100%では、エミュレーションは通常の速度に制限されます。値が高いまたは低いほど、速度制限が増加または減少します。</string> + <string name="frame_limit_enable">エミュレーション速度を制限</string> + <string name="frame_limit_enable_description">エミュレーション速度を指定した割合に制限します。</string> + <string name="frame_limit_slider">エミュレーション速度</string> + <string name="frame_limit_slider_description">エミュレーション速度を制限するパーセンテージを指定します。100%は通常速度です。値の増減で速度も増減します。</string> <string name="cpu_accuracy">CPU精度</string> - <!-- System settings strings --> <string name="use_docked_mode">TVモード</string> - <string name="use_docked_mode_description">TVモードでエミュレートします。パフォーマンスが犠牲になりますが、解像度が向上します。</string> + <string name="use_docked_mode_description">高解像度、低パフォーマンス。無効時には携帯モードが使用されます(低解像度、高パフォーマンス)。</string> <string name="emulated_region">地域</string> <string name="emulated_language">言語</string> <string name="select_rtc_date">RTCの日付を選択</string> <string name="select_rtc_time">RTCの時刻を選択</string> - <string name="use_custom_rtc">カスタムRTC</string> - <string name="use_custom_rtc_description">現在のシステム時間とは別にカスタムのリアルタイムクロックを設定できます。</string> + <string name="use_custom_rtc">カスタム RTC</string> + <string name="use_custom_rtc_description">現在のシステム時間とは別に、任意のリアルタイムクロックを設定できます。</string> <string name="set_custom_rtc">カスタムRTCを設定</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">精度</string> - <string name="renderer_resolution">解像度</string> + <string name="renderer_resolution">解像度(携帯モード/TVモード)</string> <string name="renderer_vsync">垂直同期モード</string> + <string name="renderer_screen_layout">画面の向き</string> <string name="renderer_aspect_ratio">アスペクト比</string> <string name="renderer_scaling_filter">ウィンドウ適応フィルター</string> <string name="renderer_anti_aliasing">アンチエイリアス方式</string> <string name="renderer_force_max_clock">最大クロックを強制 (Adrenoのみ)</string> - <string name="renderer_force_max_clock_description">GPUを可能な限り最大クロックで動作させます (過熱制限は引き続き適用されます)。</string> + <string name="renderer_force_max_clock_description">GPUを最大限可能な周波数で動作させます (過熱制限は引き続き適用されます)。</string> <string name="renderer_asynchronous_shaders">非同期シェーダー</string> <string name="renderer_asynchronous_shaders_description">シェーダーを非同期でコンパイルします。コマ落ちが軽減されますが、不具合が発生する可能性があります。</string> + <string name="renderer_reactive_flushing">即時書き込み</string> + <string name="renderer_reactive_flushing_description">一部のゲームにおいて、パフォーマンスを犠牲にしながらも、レンダリング精度を向上させます。</string> + <string name="use_disk_shader_cache">ディスクシェーダーキャッシュ</string> + <string name="use_disk_shader_cache_description">生成したシェーダーを端末に保存して読み込み、コマ落ちを軽減します。</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">CPU デバッギング</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> <string name="renderer_debug">グラフィックデバッグ</string> - <string name="renderer_debug_description">オンにすると、グラフィックAPI は低速のデバッグモードに入ります。</string> - <string name="use_disk_shader_cache">シェーダーキャッシュを使用</string> - <string name="use_disk_shader_cache_description">生成したシェーダーをディスクに保存して読み込むことで、コマ落ちを軽減します。</string> + <string name="renderer_debug_description">グラフィックAPIを低速デバッグモードに設定します。</string> + <string name="fastmem">Fastmem</string> <!-- Audio settings strings --> + <string name="audio_output_engine">出力エンジン</string> <string name="audio_volume">音量</string> <string name="audio_volume_description">オーディオ出力の音量を指定します</string> <!-- Miscellaneous --> <string name="slider_default">デフォルト</string> <string name="ini_saved">設定を保存しました</string> - <string name="gameid_saved">%1$sの設定を保存しました</string> + <string name="gameid_saved">%1$s の設定を保存しました</string> <string name="error_saving">%1$s.ini の保存エラー: %2$s</string> + <string name="unimplemented_menu">未実装のメニュー</string> <string name="loading">読み込み中…</string> + <string name="shutting_down">終了中...</string> <string name="reset_setting_confirmation">この設定を初期値にリセットしますか?</string> <string name="reset_to_default">初期設定に戻す</string> <string name="reset_all_settings">すべての設定をリセットしますか?</string> - <string name="reset_all_settings_description">すべての詳細設定が初期設定に戻されます。この操作は元に戻せません。</string> + <string name="reset_all_settings_description">すべての詳細設定が初期値に戻されます。この操作は元に戻せません。</string> <string name="settings_reset">設定をリセットしました</string> <string name="close">閉じる</string> <string name="learn_more">詳細情報</string> + <string name="auto">自動</string> + <string name="submit">送信</string> + <string name="string_import">インポート</string> + <string name="export">エクスポート</string> + <string name="export_failed">エクスポート失敗</string> + <string name="import_failed">インポート失敗</string> + <string name="cancelling">キャンセル中</string> <!-- GPU driver installation --> <string name="select_gpu_driver">GPUドライバを選択</string> - <string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string> + <string name="select_gpu_driver_title">現在のGPUドライバを置き換えますか?</string> <string name="select_gpu_driver_install">インストール</string> <string name="select_gpu_driver_default">デフォルト</string> - <string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string> + <string name="select_gpu_driver_use_default">デフォルトのドライバを使用します</string> + <string name="select_gpu_driver_error">選択されたドライバが無効、システムのデフォルトを使用します!</string> <string name="system_gpu_driver">システムのGPUドライバ</string> <string name="installing_driver">インストール中…</string> @@ -181,33 +233,34 @@ <string name="preferences_graphics">グラフィック</string> <string name="preferences_audio">サウンド</string> <string name="preferences_theme">テーマと色</string> + <string name="preferences_debug">デバッグ</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">ROMが暗号化されています</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">ゲームカートリッジ</a>や<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">インストール済みのタイトル</a>を再度ダンプするためのガイドに従ってください。]]></string> - <string name="loader_error_encrypted_keys_description"><![CDATA[ゲームを復号化するために <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> ファイルがインストールされていることを確認してください。]]></string> + <string name="loader_error_encrypted_keys_description"><![CDATA[ゲームの復号化に必要な <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> ファイルがインストールされていることを確認してください。]]></string> <string name="loader_error_video_core">ビデオコアの初期化中にエラーが発生しました</string> <string name="loader_error_video_core_description">これは通常、互換性のないGPUドライバーが原因で発生します。 カスタムGPUドライバーをインストールすると、問題が解決する可能性があります。</string> <string name="loader_error_invalid_format">ROMの読み込みに失敗しました</string> <string name="loader_error_file_not_found">ROMファイルが存在しません</string> <!-- Emulation Menu --> - <string name="emulation_exit">エミュレーションを終了</string> + <string name="emulation_exit">終了</string> <string name="emulation_done">完了</string> <string name="emulation_fps_counter">FPSカウンター</string> - <string name="emulation_toggle_controls">コントロールを切り替え</string> - <string name="emulation_dpad_slide">十字キーのスライド操作</string> - <string name="emulation_haptics">振動</string> - <string name="emulation_show_overlay">オーバーレイを表示</string> - <string name="emulation_toggle_all">すべて選択</string> - <string name="emulation_control_adjust">オーバーレイを調整</string> + <string name="emulation_toggle_controls">ボタンの表示設定</string> + <string name="emulation_rel_stick_center">スティックを固定しない</string> + <string name="emulation_dpad_slide">十字キーをスライド操作</string> + <string name="emulation_haptics">タッチ振動</string> + <string name="emulation_show_overlay">ボタンを表示</string> + <string name="emulation_toggle_all">すべて切替</string> + <string name="emulation_control_adjust">見た目を調整</string> <string name="emulation_control_scale">大きさ</string> <string name="emulation_control_opacity">不透明度</string> <string name="emulation_touch_overlay_reset">リセット</string> - <string name="emulation_touch_overlay_edit">オーバーレイを編集</string> - <string name="emulation_pause">エミュレーションを一時停止</string> - <string name="emulation_unpause">エミュレーションを再開</string> - <string name="emulation_input_overlay">オーバーレイオプション</string> + <string name="emulation_touch_overlay_edit">位置を編集</string> + <string name="emulation_pause">一時停止</string> + <string name="emulation_unpause">再開</string> + <string name="emulation_input_overlay">表示オプション</string> <string name="load_settings">設定をロード中…</string> @@ -220,10 +273,13 @@ <string name="system_archive_not_found">システムアーカイブが見つかりません</string> <string name="system_archive_not_found_message">%s が見つかりません。システムアーカイブをダンプしてください。\nエミュレーションを続行すると、クラッシュやバグが発生する可能性があります。</string> <string name="system_archive_general">システムアーカイブ</string> - <string name="save_load_error">セーブ/ロード エラー</string> + <string name="save_load_error">セーブ/ロードエラー</string> <string name="fatal_error">致命的なエラー</string> <string name="fatal_error_message">致命的なエラーが発生しました。詳細はログを確認してください。\nエミュレーションを続行するとクラッシュやバグが発生する可能性があります。</string> - <string name="performance_warning">この設定をオフにすると、エミュレーションのパフォーマンスが著しく低下します!最高の体験を得るためには、この設定を有効にしておくことをお勧めします。</string> + <string name="performance_warning">この設定をオフにすると、エミュレーションのパフォーマンスが著しく低下します!最高の体験を得るためには、この設定を有効にしておくことを推奨します。</string> + <string name="device_memory_inadequate">デバイス RAM: %1$s\n推奨: %2$s</string> + <string name="memory_formatted">%1$s %2$s</string> + <string name="no_game_present">起動できるゲームがありません!</string> <!-- Region Names --> <string name="region_japan">日本</string> @@ -234,7 +290,14 @@ <string name="region_korea">韓国</string> <string name="region_taiwan">台湾</string> - <!-- Language Names --> + <!-- Memory Sizes --> + <string name="memory_byte">Byte</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> @@ -242,7 +305,7 @@ <!-- Renderer Accuracy --> <string name="renderer_accuracy_normal">標準</string> - <string name="renderer_accuracy_high">高い</string> + <string name="renderer_accuracy_high">高</string> <string name="renderer_accuracy_extreme">最高 (低速)</string> <!-- Resolutions --> @@ -272,12 +335,17 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <!-- Screen Layouts --> + <string name="screen_layout_landscape">横長</string> + <string name="screen_layout_portrait">縦長</string> + <string name="screen_layout_auto">自動</string> + <!-- Aspect Ratios --> <string name="ratio_default">デフォルト (16:9)</string> <string name="ratio_force_four_three">強制 4:3</string> <string name="ratio_force_twenty_one_nine">強制 21:9</string> <string name="ratio_force_sixteen_ten">強制 16:10</string> - <string name="ratio_stretch">ウィンドウに合わせる</string> + <string name="ratio_stretch">画面に合わせる</string> <!-- CPU Accuracy --> <string name="cpu_accuracy_accurate">正確</string> @@ -289,7 +357,7 @@ <string name="gamepad_left_stick">Lスティック</string> <string name="gamepad_right_stick">Rスティック</string> <string name="gamepad_home">HOMEボタン</string> - <string name="gamepad_screenshot">スクリーンショット</string> + <string name="gamepad_screenshot">キャプチャーボタン</string> <!-- Disk shader cache --> <string name="preparing_shaders">シェーダーを準備しています</string> @@ -306,8 +374,22 @@ <string name="theme_mode_light">ライト</string> <string name="theme_mode_dark">ダーク</string> - <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">黒色の背景を使用</string> - <string name="use_black_backgrounds_description">ダークテーマの使用時は、黒色の背景を有効にしてください。</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> -</resources> + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">完全な黒を使用</string> + <string name="use_black_backgrounds_description">ダークテーマの背景色に黒が適用されます。</string> + + <!-- Picture-In-Picture --> + <string name="picture_in_picture">ピクチャーインピクチャー</string> + <string name="picture_in_picture_description">バックグラウンド時にウインドウを最小化する</string> + <string name="pause">中断</string> + <string name="play">プレイ</string> + <string name="mute">消音</string> + <string name="unmute">消音解除</string> + + <!-- Licenses screen strings --> + <string name="licenses">ライセンス</string> + <string name="license_fidelityfx_fsr_description">AMDの高品質アップスケーリング</string> + </resources> diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index 214f95706..1b9160a23 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> - <string name="app_disclaimer">이 소프트웨어는 닌텐도 스위치 게임 콘솔용 게임을 실행합니다. 게임 타이틀이나 keys는 포함되어 있지 않습니다.<br /><br />시작하기 전에 장치 저장소에서 <![CDATA[<b> prod.keys </b>]]> 파일을 찾아주세요.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">자세히 알아보기</a>]]></string> + <string name="app_disclaimer">이 소프트웨어는 Nintendo Switch 게임을 실행합니다. 게임 타이틀이나 키는 포함되어 있지 않습니다.<br /><br />시작하기 전에 장치 저장소에서 <![CDATA[<b> prod.keys </b>]]> 파일을 찾아주세요.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">자세히 알아보기</a>]]></string> <string name="emulation_notification_channel_name">에뮬레이션이 활성화됨</string> - <string name="emulation_notification_channel_description">에뮬레이션이 실행 중일 때 영구 알림을 표시합니다.</string> + <string name="emulation_notification_channel_description">에뮬레이션이 실행 중일 때 지속적으로 알림을 표시합니다.</string> <string name="emulation_notification_running">yuzu가 실행 중입니다.</string> <string name="notice_notification_channel_name">알림 및 오류</string> <string name="notice_notification_channel_description">문제가 발생하면 알림을 표시합니다.</string> @@ -11,26 +11,25 @@ <!-- Setup strings --> <string name="welcome">환영합니다!</string> - <string name="welcome_description"><b>yuzu</b> 를 설정하고 에뮬레이션으로 이동하는 방법을 알아보세요.</string> + <string name="welcome_description"><b>yuzu</b>를 설정하고 에뮬레이션을 시작하세요.</string> <string name="get_started">시작하기</string> - <string name="keys">Keys</string> - <string name="keys_description">아래 버튼을 사용하여 <b>prod.keys</b> 파일을 선택합니다.</string> - <string name="select_keys">keys 선택</string> + <string name="keys">키 설정</string> + <string name="keys_description">아래 버튼으로 <b>prod.keys</b> 파일을 선택합니다.</string> + <string name="select_keys">키 선택</string> <string name="games">게임</string> <string name="games_description">아래 버튼으로 <b>게임</b> 폴더를 선택합니다.</string> <string name="done">완료</string> - <string name="done_description">모든 준비가 완료되었습니다.\n게임을 즐기세요!</string> + <string name="done_description">모두 준비되었습니다.\n게임을 즐기세요!</string> <string name="text_continue">계속</string> <string name="next">다음</string> - <string name="back">뒤로</string> + <string name="back">이전</string> <string name="add_games">게임 추가</string> <string name="add_games_description">게임 폴더 선택</string> - <!-- Home strings --> <string name="home_games">게임</string> <string name="home_search">검색</string> <string name="home_settings">설정</string> - <string name="empty_gamelist">파일을 찾을 수 없거나 아직 게임 디렉토리를 선택하지 않았습니다.</string> + <string name="empty_gamelist">파일을 찾을 수 없거나 아직 게임 디렉터리를 선택하지 않았습니다.</string> <string name="search_and_filter_games">게임 검색 및 필터링</string> <string name="select_games_folder">게임 폴더 선택</string> <string name="select_games_folder_description">yuzu가 게임 목록을 채울 수 있도록 허용</string> @@ -38,140 +37,160 @@ <string name="add_games_warning_description">폴더를 선택하지 않으면 게임 목록에 게임이 표시되지 않습니다.</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> <string name="home_search_games">게임 검색</string> - <string name="games_dir_selected">게임 디렉터리 선택</string> + <string name="games_dir_selected">게임 디렉터리를 설정했습니다.</string> <string name="install_prod_keys">prod.keys 설치</string> - <string name="install_prod_keys_description">판매용 게임 암호 해독에 요구</string> - <string name="install_prod_keys_warning">keys 추가를 건너뛰겠습니까?</string> - <string name="install_prod_keys_warning_description">정품 게임을 에뮬레이트하려면 유효한 keys가 필요합니다. 계속하면 자체 제작 앱만 작동합니다.</string> + <string name="install_prod_keys_description">패키지 게임 암호 해독에 필요</string> + <string name="install_prod_keys_warning">키 추가를 건너뛰겠습니까?</string> + <string name="install_prod_keys_warning_description">패키지 게임을 에뮬레이트하려면 유효한 키 값이 필요합니다. 이 단계를 건너뛰면 홈브류 게임만 실행할 수 있습니다.</string> <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> <string name="notifications">알림</string> <string name="notifications_description">아래 버튼으로 알림 권한을 부여합니다.</string> - <string name="give_permission">권한 부여</string> - <string name="notification_warning">알림 권한 부여를 건너뛰겠습니까?</string> - <string name="notification_warning_description">yuzu는 중요한 정보를 알려드리지 않습니다.</string> + <string name="give_permission">알림 켜기</string> + <string name="notification_warning">알림을 끄겠습니까?</string> + <string name="notification_warning_description">yuzu가 중요한 정보를 알려드리지 않습니다.</string> <string name="permission_denied">권한 거부됨</string> - <string name="permission_denied_description">이 권한을 너무 많이 거부했으므로 이제 시스템 설정에서 수동으로 권한을 부여해야 합니다.</string> + <string name="permission_denied_description">권한 허용을 너무 많이 거부하여 시스템 설정에서 수동으로 권한을 부여해야 합니다.</string> <string name="about">정보</string> <string name="about_description">빌드 버전, 크레딧 등</string> <string name="warning_help">도움말</string> <string name="warning_skip">건너뛰기</string> <string name="warning_cancel">취소</string> - <string name="install_amiibo_keys">Amiibo keys 설치</string> - <string name="install_amiibo_keys_description">게임에서 아미보 사용 시 필요</string> - <string name="invalid_keys_file">잘못된 keys 파일 선택</string> - <string name="install_keys_success">keys가 성공적으로 설치됨</string> - <string name="reading_keys_failure">암호화 keys 읽기 오류</string> - <string name="invalid_keys_error">잘못된 암호화 keys</string> + <string name="install_amiibo_keys">amiibo 키 설치</string> + <string name="install_amiibo_keys_description">게임에서 amiibo 사용 시 필요</string> + <string name="invalid_keys_file">잘못된 키 파일이 선택됨</string> + <string name="install_keys_success">키 값을 설치했습니다.</string> + <string name="reading_keys_failure">암호화 키 읽기 오류</string> + <string name="install_prod_keys_failure_extension_description">키 파일의 확장자가 .keys인지 확인하고 다시 시도하세요.</string> + <string name="install_amiibo_keys_failure_extension_description">키 파일의 확장자가 .bin인지 확인하고 다시 시도하세요.</string> + <string name="invalid_keys_error">암호화 키가 올바르지 않음</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> - <string name="install_keys_failure_description">선택한 파일이 잘못되었거나 손상되었습니다. keys를 다시 덤프하세요.</string> + <string name="install_keys_failure_description">선택한 파일이 잘못되었거나 손상되었습니다. 키를 다시 덤프하세요.</string> <string name="install_gpu_driver">GPU 드라이버 설치</string> <string name="install_gpu_driver_description">잠재적으로 더 나은 성능 또는 정확성을 위해 대체 드라이버를 설치하세요.</string> <string name="advanced_settings">고급 설정</string> <string name="settings_description">에뮬레이터 설정 구성</string> - <string name="search_recently_played">최근 플레이한 게임</string> - <string name="search_recently_added">최근 추가한 게임</string> - <string name="search_retail">판매용</string> + <string name="search_recently_played">최근 플레이</string> + <string name="search_recently_added">최근 추가</string> + <string name="search_retail">패키지</string> <string name="search_homebrew">홈브류</string> <string name="open_user_folder">yuzu 폴더 열기</string> <string name="open_user_folder_description">yuzu의 내부 파일 관리</string> - <string name="theme_and_color_description">앱 모양 수정</string> + <string name="theme_and_color_description">앱 디자인 편집</string> <string name="no_file_manager">파일 관리자를 찾을 수 없음</string> - <string name="notification_no_directory_link">yuzu 디렉토리를 열 수 없음</string> + <string name="notification_no_directory_link">yuzu 디렉터리를 열 수 없음</string> <string name="notification_no_directory_link_description">파일 관리자의 사이드 패널에서 사용자 폴더를 수동으로 찾아주세요.</string> <string name="manage_save_data">저장 데이터 관리</string> - <string name="manage_save_data_description">데이터를 저장했습니다. 아래에서 옵션을 선택하세요.</string> + <string name="manage_save_data_description">저장 데이터를 발견했습니다. 아래에서 옵션을 선택하세요.</string> <string name="import_export_saves_description">저장 파일 가져오기 또는 내보내기</string> - <string name="save_file_imported_success">가져오기 성공</string> - <string name="save_file_invalid_zip_structure">저장 디렉터리 구조가 잘못됨</string> + <string name="save_file_imported_success">데이터를 불러왔습니다.</string> + <string name="save_file_invalid_zip_structure">올바르지 않은 저장 디렉터리 구조</string> <string name="save_file_invalid_zip_structure_description">첫 번째 하위 폴더 이름은 게임의 타이틀 ID여야 합니다.</string> <string name="import_saves">가져오기</string> <string name="export_saves">내보내기</string> - + <string name="install_firmware">펌웨어 설치</string> + <string name="install_firmware_description">펌웨어는 ZIP 파일이며 일부 게임을 부팅하는 데 필요합니다.</string> + <string name="firmware_installing">펌웨어 설치</string> + <string name="firmware_installed_success">펌웨어를 설치했습니다.</string> + <string name="firmware_installed_failure">펌웨어 설치 실패</string> + <string name="share_log">디버그 로그 공유</string> + <string name="share_log_description">yuzu의 로그 파일을 공유하여 문제 디버깅하기</string> + <string name="share_log_missing">로그 파일을 찾을 수 없습니다.</string> + <string name="install_game_content">게임 콘텐츠 설치</string> + <string name="install_game_content_description">게임 업데이트 또는 DLC 설치</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> <!-- About screen strings --> <string name="gaia_is_not_real">가이아는 진짜가 아님</string> - <string name="copied_to_clipboard">클립보드에 복사</string> - <string name="about_app_description">오픈 소스 스위치 에뮬레이터</string> + <string name="copied_to_clipboard">클립보드에 복사되었습니다.</string> + <string name="about_app_description">오픈 소스 Switch 에뮬레이터</string> <string name="contributors">기여자</string> <string name="contributors_description">yuzu 팀의 \u2764로 제작</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Android용 yuzu를 가능하게 하는 프로젝트</string> <string name="build">빌드</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> <!-- Early access upgrade strings --> - <string name="early_access">미리 체험하기</string> - <string name="get_early_access">미리 체험하기 신청</string> + <string name="early_access">앞서 해보기</string> + <string name="get_early_access">앞서 해보기 신청</string> <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> - <string name="get_early_access_description">최첨단 기능, 미리 체험하기 업데이트 등</string> - <string name="early_access_benefits">미리 체험하기 혜택</string> - <string name="cutting_edge_features">최첨단 기능</string> - <string name="early_access_updates">미리 체험하기 업데이트</string> + <string name="get_early_access_description">최신 기능, 업데이트 미리 체험 등</string> + <string name="early_access_benefits">앞서 해보기 혜택</string> + <string name="cutting_edge_features">최신 기능</string> + <string name="early_access_updates">업데이트 미리 체험</string> <string name="no_manual_installation">수동 설치 불필요</string> <string name="prioritized_support">우선 지원</string> - <string name="helping_game_preservation">게임 보존 도움주기</string> - <string name="our_eternal_gratitude">영원한 감사의 마음을 전합니다</string> + <string name="helping_game_preservation">게임 보존 지원</string> + <string name="our_eternal_gratitude">우리의 영원한 감사의 마음</string> <string name="are_you_interested">관심 있으세요?</string> <!-- General settings strings --> - <string name="frame_limit_enable">제한 속도 활성화</string> - <string name="frame_limit_enable_description">활성화하면 에뮬레이션 속도가 정상 속도의 지정된 비율로 제한됩니다.</string> + <string name="frame_limit_enable">속도 제한</string> + <string name="frame_limit_enable_description">에뮬레이션 속도를 정상 속도의 지정된 비율로 제한합니다.</string> <string name="frame_limit_slider">속도 제한 비율</string> - <string name="frame_limit_slider_description">에뮬레이션 속도를 제한할 비율을 지정합니다. 기본값인 100%로 설정하면 에뮬레이션이 정상 속도로 제한됩니다. 값이 높거나 낮으면 속도 제한이 증가하거나 감소합니다.</string> + <string name="frame_limit_slider_description">에뮬레이션 속도의 제한 비율을 지정합니다. 100%가 정상 속도입니다. 값이 높거나 낮으면 속도 제한이 증가하거나 감소합니다.</string> <string name="cpu_accuracy">CPU 정확도</string> - <!-- System settings strings --> - <string name="use_docked_mode">도킹 모드</string> - <string name="use_docked_mode_description">도킹 모드에서 에뮬레이션하면 성능이 저하되는 대신 해상도가 향상됩니다.</string> - <string name="emulated_region">에뮬레이트된 지역</string> - <string name="emulated_language">에뮬레이트된 언어</string> + <string name="use_docked_mode">독 모드</string> + <string name="use_docked_mode_description">해상도를 높이며 성능이 저하됩니다. 비활성화시 휴대 모드가 사용되며 해상도는 낮아지고 성능은 향상됩니다.</string> + <string name="emulated_region">에뮬레이트 지역</string> + <string name="emulated_language">에뮬레이트 언어</string> <string name="select_rtc_date">RTC 날짜 선택</string> <string name="select_rtc_time">RTC 시간 선택</string> - <string name="use_custom_rtc">커스텀 RTC 활성화</string> - <string name="use_custom_rtc_description">이 설정을 사용하면 현재 시스템 시간과 별도로 사용자 지정 실시간 시계를 설정할 수 있음</string> - <string name="set_custom_rtc">커스텀 RTC 설정</string> + <string name="use_custom_rtc">사용자 지정 RTC</string> + <string name="use_custom_rtc_description">현재 시스템 시간과 별도로 사용자 지정 실시간 시계를 설정할 수 있습니다.</string> + <string name="set_custom_rtc">사용자 지정 RTC 설정</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">정확도 수준</string> - <string name="renderer_resolution">해상도</string> + <string name="renderer_resolution">해상도 (휴대 모드/독 모드)</string> <string name="renderer_vsync">수직동기화 모드</string> <string name="renderer_aspect_ratio">화면비</string> - <string name="renderer_scaling_filter">창 적응 필터</string> - <string name="renderer_anti_aliasing">안티-에일리어싱 방법</string> - <string name="renderer_force_max_clock">최대 클럭 강제 설정 (아드레노만 해당)</string> + <string name="renderer_scaling_filter">윈도우 적응 필터</string> + <string name="renderer_anti_aliasing">안티에일리어싱 방법</string> + <string name="renderer_force_max_clock">최대 클럭 강제 설정 (아드레노 전용)</string> <string name="renderer_force_max_clock_description">GPU가 가능한 최대 클럭으로 실행되도록 강제합니다 (열 제약 조건은 여전히 적용됩니다).</string> <string name="renderer_asynchronous_shaders">비동기 셰이더 사용</string> - <string name="renderer_asynchronous_shaders_description">셰이더를 비동기식으로 컴파일하므로 끊김 현상이 줄어들지만 글리치가 발생할 수 있습니다.</string> - <string name="renderer_debug">그래픽 디버깅 활성화</string> - <string name="renderer_debug_description">이 옵션을 선택하면 그래픽 API가 느린 디버깅 모드로 전환됩니다.</string> - <string name="use_disk_shader_cache">디스크 셰이더 캐시 사용</string> - <string name="use_disk_shader_cache_description">생성된 셰이더를 디스크에 저장하고 불러오기하여 끊김 현상을 줄입니다.</string> - - <!-- Audio settings strings --> + <string name="renderer_asynchronous_shaders_description">셰이더를 비동기식으로 컴파일하여 끊김 현상을 줄이지만 글리치가 발생할 수 있습니다.</string> + <string name="renderer_reactive_flushing">반응형 플러싱 사용</string> + <string name="renderer_reactive_flushing_description">일부 게임에서 성능 저하를 감수하고 렌더링 정확도를 향상합니다.</string> + <string name="use_disk_shader_cache">디스크 셰이더 캐시</string> + <string name="use_disk_shader_cache_description">생성된 셰이더를 로컬에 저장하고 로드하여 끊김 현상을 줄입니다.</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">그래픽 디버깅</string> + <string name="renderer_debug_description">그래픽 API를 느린 디버깅 모드로 설정합니다.</string> <string name="audio_volume">볼륨</string> <string name="audio_volume_description">오디오 출력의 볼륨을 지정합니다.</string> <!-- Miscellaneous --> <string name="slider_default">기본값</string> - <string name="ini_saved">저장된 설정</string> - <string name="gameid_saved">%1$s를 위해 저장된 설정</string> - <string name="error_saving">%1$s.ini 저장 중 오류: %2$s</string> - <string name="loading">불러오기 중...</string> - <string name="reset_setting_confirmation">이 설정을 기본값으로 되돌리겠습니까?</string> + <string name="ini_saved">설정이 저장되었습니다.</string> + <string name="gameid_saved">%1$s 전용 설정이 저장되었습니다.</string> + <string name="error_saving">%1$s.ini 저장 중 오류 발생: %2$s</string> + <string name="loading">불러오는 중...</string> + <string name="reset_setting_confirmation">이 설정을 기본값으로 재설정하겠습니까?</string> <string name="reset_to_default">기본값으로 재설정</string> <string name="reset_all_settings">모든 설정을 초기화하겠습니까?</string> - <string name="reset_all_settings_description">모든 고급 설정이 기본 구성으로 재설정됩니다. 이 설정은 되돌릴 수 없습니다.</string> + <string name="reset_all_settings_description">모든 고급 설정이 기본 구성으로 재설정됩니다. 이 작업은 되돌릴 수 없습니다.</string> <string name="settings_reset">설정 초기화</string> <string name="close">닫기</string> - <string name="learn_more">자세히 알아보기</string> - + <string name="learn_more">자세히</string> + <string name="auto">자동</string> + <string name="submit">제출</string> + <string name="string_null">Null</string> + <string name="string_import">가져오기</string> + <string name="export">내보내기</string> <!-- GPU driver installation --> <string name="select_gpu_driver">GPU 드라이버 선택</string> - <string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string> + <string name="select_gpu_driver_title">현재 사용중인 GPU 드라이버를 변경하겠습니까?</string> <string name="select_gpu_driver_install">설치</string> <string name="select_gpu_driver_default">기본값</string> - <string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string> + <string name="select_gpu_driver_use_default">기본 GPU 드라이버를 사용합니다.</string> + <string name="select_gpu_driver_error">잘못된 드라이브가 선택되었습니다. 시스템 기본값을 사용합니다.</string> <string name="system_gpu_driver">시스템 GPU 드라이버</string> <string name="installing_driver">드라이버 설치 중...</string> @@ -182,51 +201,50 @@ <string name="preferences_graphics">그래픽</string> <string name="preferences_audio">오디오</string> <string name="preferences_theme">테마 및 색상</string> + <string name="preferences_debug">디버그</string> <!-- ROM loading errors --> - <string name="loader_error_encrypted">롬이 암호화되었음</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[가이드에 따라 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">게임 카트리지</a> 또는 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">설치된 타이틀</a>를 다시 덤프하세요.]]></string> - <string name="loader_error_encrypted_keys_description"><![CDATA[P게임을 해독할 수 있도록 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 파일이 설치되어 있는지 확인하세요.]]></string> + <string name="loader_error_encrypted">롬 파일이 암호화되어있음</string> + <string name="loader_error_encrypted_keys_description"><![CDATA[게임을 해독할 수 있도록 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 파일이 설치되어 있는지 확인하세요.]]></string> <string name="loader_error_video_core">비디오 코어를 초기화하는 동안 오류 발생</string> - <string name="loader_error_video_core_description">이 문제는 일반적으로 호환되지 않는 GPU 드라이버로 인해 발생합니다. 사용자 지정 GPU 드라이버를 설치하면 이 문제가 해결될 수 있습니다.</string> - <string name="loader_error_invalid_format">롬을 불러올 수 없음</string> + <string name="loader_error_video_core_description">일반적으로 이 문제는 호환되지 않는 GPU 드라이버로 인해 발생합니다. 사용자 지정 GPU 드라이버를 설치하면 이 문제가 해결될 수 있습니다.</string> + <string name="loader_error_invalid_format">롬 파일을 불러올 수 없음</string> <string name="loader_error_file_not_found">롬 파일이 존재하지 않음</string> <!-- Emulation Menu --> <string name="emulation_exit">에뮬레이션 종료</string> <string name="emulation_done">완료</string> - <string name="emulation_fps_counter">FPS 카운터</string> - <string name="emulation_toggle_controls">토글 제어</string> - <string name="emulation_rel_stick_center">상대 스틱 센터</string> - <string name="emulation_dpad_slide">십자패드 슬라이드</string> - <string name="emulation_haptics">햅틱</string> - <string name="emulation_show_overlay">오버레이 표시</string> - <string name="emulation_toggle_all">모두 토글</string> - <string name="emulation_control_adjust">오버레이 조정</string> - <string name="emulation_control_scale">스케일</string> + <string name="emulation_fps_counter">FPS 표시</string> + <string name="emulation_toggle_controls">컨트롤러 선택</string> + <string name="emulation_rel_stick_center">스틱의 중심 이동</string> + <string name="emulation_dpad_slide">십자키 슬라이드</string> + <string name="emulation_haptics">터치 햅틱</string> + <string name="emulation_show_overlay">컨트롤러 표시</string> + <string name="emulation_toggle_all">모두 선택</string> + <string name="emulation_control_adjust">컨트롤러 조정</string> + <string name="emulation_control_scale">크기</string> <string name="emulation_control_opacity">불투명도</string> - <string name="emulation_touch_overlay_reset">오버레이 재설정</string> - <string name="emulation_touch_overlay_edit">오버레이 편집</string> + <string name="emulation_touch_overlay_reset">컨트롤러 설정 초기화</string> + <string name="emulation_touch_overlay_edit">컨트롤러 위치 편집</string> <string name="emulation_pause">에뮬레이션 일시 중지</string> <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string> - <string name="emulation_input_overlay">오버레이 옵션</string> + <string name="emulation_input_overlay">화면 오버레이 설정</string> - <string name="load_settings">설정 불러오기 중...</string> + <string name="load_settings">설정 불러오는 중...</string> <!-- Software keyboard --> - <string name="software_keyboard">가상 키보드</string> + <string name="software_keyboard">소프트웨어 키보드</string> <!-- Errors and warnings --> - <string name="abort_button">정보</string> + <string name="abort_button">중단</string> <string name="continue_button">계속</string> <string name="system_archive_not_found">시스템 아카이브를 찾을 수 없음</string> <string name="system_archive_not_found_message">%s가 누락되었습니다. 시스템 아카이브를 덤프하세요.\n에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다.</string> <string name="system_archive_general">시스템 아카이브</string> <string name="save_load_error">저장하기/불러오기 오류</string> - <string name="fatal_error">치명적인 오류</string> - <string name="fatal_error_message">치명적인 오류가 발생했습니다. 자세한 내용은 로그를 확인하십시오.\n에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다.</string> + <string name="fatal_error">치명적 오류</string> + <string name="fatal_error_message">치명적 오류가 발생했습니다. 자세한 내용은 로그를 확인하십시오.\n에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다.</string> <string name="performance_warning">이 설정을 끄면 에뮬레이션 성능이 크게 저하됩니다! 최상의 환경을 위해 이 설정을 활성화된 상태로 두는 것이 좋습니다.</string> - <!-- Region Names --> <string name="region_japan">일본</string> <string name="region_usa">미국</string> @@ -234,12 +252,11 @@ <string name="region_australia">호주</string> <string name="region_china">중국</string> <string name="region_korea">대한민국</string> - <string name="region_taiwan">타이완</string> - - <!-- Language Names --> + <string name="region_taiwan">대만</string> + <string name="memory_gigabyte">영국 하계 표준시(GB)</string> <!-- Renderer APIs --> - <string name="renderer_vulkan">불칸</string> + <string name="renderer_vulkan">Vulcan</string> <string name="renderer_none">없음</string> <!-- Renderer Accuracy --> @@ -256,17 +273,17 @@ <string name="resolution_four">4X (2880p/4320p) (느림)</string> <!-- Renderer VSync --> - <string name="renderer_vsync_immediate">즉시 (끔)</string> + <string name="renderer_vsync_immediate">즉각 표시 (끄기)</string> <string name="renderer_vsync_mailbox">메일박스</string> - <string name="renderer_vsync_fifo">FIFO (켬)</string> - <string name="renderer_vsync_fifo_relaxed">FIFO 릴랙스</string> + <string name="renderer_vsync_fifo">FIFO (켜기)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> <!-- Scaling Filters --> - <string name="scaling_filter_nearest_neighbor">가장 가까운 이웃</string> - <string name="scaling_filter_bilinear">이중선형</string> - <string name="scaling_filter_bicubic">고등차수보간</string> + <string name="scaling_filter_nearest_neighbor">최근접 보간</string> + <string name="scaling_filter_bilinear">쌍선형 보간</string> + <string name="scaling_filter_bicubic">쌍입방 보간</string> <string name="scaling_filter_gaussian">가우시안</string> - <string name="scaling_filter_scale_force">스케일포스</string> + <string name="scaling_filter_scale_force">ScaleForce</string> <string name="scaling_filter_fsr">AMD FidelityFX™ 초고해상도</string> <!-- Anti-Aliasing --> @@ -274,27 +291,29 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <string name="screen_layout_auto">자동</string> + <!-- Aspect Ratios --> <string name="ratio_default">기본 (16:9)</string> <string name="ratio_force_four_three">강제 4:3</string> <string name="ratio_force_twenty_one_nine">강제 21:9</string> <string name="ratio_force_sixteen_ten">강제 16:10</string> - <string name="ratio_stretch">창에 맞게 늘림</string> + <string name="ratio_stretch">화면에 맞춤</string> <!-- CPU Accuracy --> <string name="cpu_accuracy_accurate">정확함</string> - <string name="cpu_accuracy_unsafe">안전하지 않음</string> - <string name="cpu_accuracy_paranoid">편집증 (느림)</string> + <string name="cpu_accuracy_unsafe">최적화 (안전하지 않음)</string> + <string name="cpu_accuracy_paranoid">최적화하지 않음 (느림)</string> <!-- Gamepad Buttons --> - <string name="gamepad_d_pad">십자패드</string> + <string name="gamepad_d_pad">십자키</string> <string name="gamepad_left_stick">L 스틱</string> <string name="gamepad_right_stick">R 스틱</string> <string name="gamepad_home">홈</string> <string name="gamepad_screenshot">스크린샷</string> <!-- Disk shader cache --> - <string name="preparing_shaders">셰이더 준비하기</string> + <string name="preparing_shaders">셰이더 준비하는 중</string> <string name="building_shaders">셰이더 빌드 중</string> <!-- Theme options --> @@ -303,13 +322,19 @@ <string name="theme_material_you">Material You</string> <!-- Theme Modes --> - <string name="change_theme_mode">테마 모드 변경</string> - <string name="theme_mode_follow_system">팔로우 시스템</string> - <string name="theme_mode_light">밝음</string> - <string name="theme_mode_dark">어두움</string> + <string name="change_theme_mode">다크 모드 설정</string> + <string name="theme_mode_follow_system">시스템 값 사용</string> + <string name="theme_mode_light">라이트 모드</string> + <string name="theme_mode_dark">다크 모드</string> <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">검은색 배경 사용</string> - <string name="use_black_backgrounds_description">어두운 테마를 사용할 때는 검은색 배경을 적용합니다.</string> + <string name="use_black_backgrounds">검정 배경</string> + <string name="use_black_backgrounds_description">어두운 테마를 사용할 때는 검정 배경을 적용합니다.</string> + + <string name="mute">음소거</string> + <string name="unmute">음소거 해제</string> -</resources> + <!-- Licenses screen strings --> + <string name="licenses">라이센스</string> + <string name="license_fidelityfx_fsr_description">AMD의 고품질 업스케일링</string> + </resources> diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml index 5443cef42..3162a9d41 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.<br /><br />Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string> <string name="emulation_notification_channel_name">Emulering er aktiv</string> @@ -25,7 +25,6 @@ <string name="back">Tilbake</string> <string name="add_games">Legg til spill</string> <string name="add_games_description">Velg din spillmappe</string> - <!-- Home strings --> <string name="home_games">Spill</string> <string name="home_search">Søk</string> @@ -37,7 +36,7 @@ <string name="add_games_warning">Hoppe over valg av spillmappe?</string> <string name="add_games_warning_description">Spill vises ikke i Spill-listen hvis en mappe ikke er valgt.</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> - <string name="home_search_games">Søk i spill</string> + <string name="home_search_games">Søk i spill|</string> <string name="games_dir_selected">Spillkatalogen er valgt</string> <string name="install_prod_keys">Installer prod.keys</string> <string name="install_prod_keys_description">Nødvendig for å dekryptere spill</string> @@ -61,6 +60,8 @@ <string name="invalid_keys_file">Ugyldig nøkkelfil valgt</string> <string name="install_keys_success">Nøkler vellykket installert</string> <string name="reading_keys_failure">Feil ved lesing av krypteringsnøkler</string> + <string name="install_prod_keys_failure_extension_description">Kontroller at nøkkelfilen har filtypen .keys, og prøv igjen.</string> + <string name="install_amiibo_keys_failure_extension_description">Kontroller at nøkkelfilen har filtypen .bin, og prøv igjen.</string> <string name="invalid_keys_error">Ugyldige krypteringsnøkler</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">Den valgte filen er feil eller ødelagt. Vennligst dump nøklene på nytt.</string> @@ -86,7 +87,17 @@ <string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string> <string name="import_saves">Importer</string> <string name="export_saves">Eksporter</string> - + <string name="install_firmware">Installer fastvare</string> + <string name="install_firmware_description">Fastvaren må være i et ZIP-arkiv og er nødvendig for å starte noen spill.</string> + <string name="firmware_installing">Installering av fastvare</string> + <string name="firmware_installed_success">Fastvaren er vellykket installert</string> + <string name="firmware_installed_failure">Installasjon av fastvare mislyktes</string> + <string name="share_log">Del feilsøkingslogger</string> + <string name="share_log_description">Del yuzus loggfil for å feilsøke problemer</string> + <string name="share_log_missing">Ingen loggfil funnet</string> + <string name="install_game_content">Installer spillinnhold</string> + <string name="install_game_content_description">Installer spilloppdateringer eller DLC</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia er ikke ekte</string> <string name="copied_to_clipboard">Kopiert til utklippstavlen</string> @@ -94,6 +105,7 @@ <string name="contributors">Bidragsytere</string> <string name="contributors_description">Laget med \u2764 fra yuzu-teamet</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Prosjekter som gjør yuzu for Android mulig</string> <string name="build">Bygg</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> @@ -114,41 +126,43 @@ <string name="are_you_interested">Er du interessert?</string> <!-- General settings strings --> - <string name="frame_limit_enable">Aktiver hastighetsbegrensning</string> - <string name="frame_limit_enable_description">Når aktivert, begrenses emuleringshastigheten til en angitt prosentandel av normal hastighet.</string> + <string name="frame_limit_enable">Begrense hastigheten</string> + <string name="frame_limit_enable_description">Begrenser emuleringshastigheten til en spesifisert prosentandel av normal hastighet.</string> <string name="frame_limit_slider">Hastighetsbegrensning i prosent</string> - <string name="frame_limit_slider_description">Angir prosentandelen som skal begrense emuleringshastigheten. Med standardverdien 100 % vil emuleringen være begrenset til normal hastighet. Høyere eller lavere verdier vil øke eller redusere hastighetsbegrensningen.</string> + <string name="frame_limit_slider_description">Angir prosentandelen som skal begrense emuleringshastigheten. 100 % er normal hastighet. Høyere eller lavere verdier vil øke eller redusere hastighetsgrensen.</string> <string name="cpu_accuracy">CPU-nøyaktighet</string> - <!-- System settings strings --> <string name="use_docked_mode">Dokket modus</string> - <string name="use_docked_mode_description">Emulerer i dokket modus, noe som øker oppløsningen på bekostning av ytelsen.</string> + <string name="use_docked_mode_description">Øker oppløsningen, men reduserer ytelsen. Håndholdt modus brukes når den er deaktivert, noe som reduserer oppløsningen og øker ytelsen.</string> <string name="emulated_region">Emulert region</string> <string name="emulated_language">Emulert språk</string> <string name="select_rtc_date">Velg RTC-dato</string> <string name="select_rtc_time">Velg RTC-tid</string> - <string name="use_custom_rtc">Aktiver egendefinert RTC</string> - <string name="use_custom_rtc_description">Med denne innstillingen kan du stille inn en egendefinert sanntidsklokke som er atskilt fra gjeldende systemtid.</string> - <string name="set_custom_rtc">Angi egendefinert RTC</string> + <string name="use_custom_rtc">Tilpasset Sannhetstidsklokke</string> + <string name="use_custom_rtc_description">Gjør det mulig å stille inn en egendefinert sanntidsklokke separat fra den gjeldende systemtiden.</string> + <string name="set_custom_rtc">Angi tilpasset RTC</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">Nøyaktighetsnivå</string> - <string name="renderer_resolution">Oppløsning</string> + <string name="renderer_resolution">Oppløsning (håndholdt/dokket)</string> <string name="renderer_vsync">VSync-modus</string> <string name="renderer_aspect_ratio">Størrelsesforhold</string> <string name="renderer_scaling_filter">Filter for vindustilpasning</string> - <string name="renderer_anti_aliasing">Anti-Aliasing-metode</string> + <string name="renderer_anti_aliasing">Anti-aliasing-metode</string> <string name="renderer_force_max_clock">Tving fram maksimal klokkefrekvens (kun Adreno)</string> <string name="renderer_force_max_clock_description">Tvinger GPU-en til å kjøre med maksimal klokkefrekvens (termiske begrensninger vil fortsatt gjelde).</string> <string name="renderer_asynchronous_shaders">Bruk asynkrone shaders</string> - <string name="renderer_asynchronous_shaders_description">Kompilerer shaders asynkront, noe som reduserer hakkingen, men kan føre til feil.</string> - <string name="renderer_debug">Aktiver feilsøking av grafikk</string> - <string name="renderer_debug_description">Når dette er merket av, går grafikk-API-et inn i en langsommere feilsøkingsmodus.</string> - <string name="use_disk_shader_cache">Bruk disk shader-cache</string> - <string name="use_disk_shader_cache_description">Reduser hakking ved å lagre og laste inn genererte shaders på disken.</string> - - <!-- Audio settings strings --> + <string name="renderer_asynchronous_shaders_description">Kompilerer shaders asynkront, noe som reduserer hakking, men kan føre til feil.</string> + <string name="renderer_reactive_flushing">Bruk reaktiv spyling</string> + <string name="renderer_reactive_flushing_description">Forbedrer gjengivelsesnøyaktigheten i enkelte spill på bekostning av ytelsen.</string> + <string name="use_disk_shader_cache">Disk shader-hurtigbuffer</string> + <string name="use_disk_shader_cache_description">Reduserer hakking ved å lagre og laste inn genererte shaders lokalt.</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">Feilsøking av grafikk</string> + <string name="renderer_debug_description">Setter grafikk-API-et til en langsom feilsøkingsmodus.</string> <string name="audio_volume">Volum</string> <string name="audio_volume_description">Angir volumet på lydutgangen.</string> @@ -164,14 +178,19 @@ <string name="reset_all_settings_description">Alle avanserte innstillinger tilbakestilles til standardkonfigurasjonen. Dette kan ikke angres.</string> <string name="settings_reset">Tilbakestilling av innstillinger</string> <string name="close">Lukk</string> - <string name="learn_more">Lær Mer</string> - + <string name="learn_more">Lær mer</string> + <string name="auto">Auto</string> + <string name="submit">Send inn</string> + <string name="string_null">Null</string> + <string name="string_import">Importer</string> + <string name="export">Eksporter</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Velg GPU-driver</string> <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string> <string name="select_gpu_driver_install">Installer</string> <string name="select_gpu_driver_default">Standard</string> <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string> + <string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string> <string name="system_gpu_driver">Systemets GPU-driver</string> <string name="installing_driver">Installerer driver...</string> @@ -182,10 +201,10 @@ <string name="preferences_graphics">Grafikk</string> <string name="preferences_audio">Lyd</string> <string name="preferences_theme">Tema og farge</string> + <string name="preferences_debug">Feilsøk</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">ROM-en din er kryptert</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[Følg veiledningene for å redumpe dine <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">spillkassetter</a> eller <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">installerte titler</a>.]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[Vennligst sørg for at <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> filen er installert slik at spillene kan dekrypteres.]]></string> <string name="loader_error_video_core">Det oppstod en feil ved initialisering av videokjernen</string> <string name="loader_error_video_core_description">Dette skyldes vanligvis en inkompatibel GPU-driver. Installering av en tilpasset GPU-driver kan løse problemet.</string> @@ -196,25 +215,25 @@ <string name="emulation_exit">Avslutt emulering</string> <string name="emulation_done">Ferdig</string> <string name="emulation_fps_counter">FPS-teller</string> - <string name="emulation_toggle_controls">Veksle kontroller</string> - <string name="emulation_rel_stick_center">Relativt senter for stikken</string> - <string name="emulation_dpad_slide">DPad-skyveplate</string> - <string name="emulation_haptics">Haptikk</string> + <string name="emulation_toggle_controls">Veksle mellom kontrollene</string> + <string name="emulation_rel_stick_center">Relativt pinnesenter</string> + <string name="emulation_dpad_slide">D-pad-skyving</string> + <string name="emulation_haptics">Berøringshaptikk</string> <string name="emulation_show_overlay">Vis overlegg</string> - <string name="emulation_toggle_all">Slå av alt</string> + <string name="emulation_toggle_all">Veksle mellom alle</string> <string name="emulation_control_adjust">Juster overlegg</string> <string name="emulation_control_scale">Skaler</string> <string name="emulation_control_opacity">Gjennomsiktighet</string> <string name="emulation_touch_overlay_reset">Tilbakestill overlegg</string> <string name="emulation_touch_overlay_edit">Rediger overlegg</string> - <string name="emulation_pause">Pause Emulering</string> - <string name="emulation_unpause">Opphev pausing av emulering</string> - <string name="emulation_input_overlay">Alternativer for overlegg</string> + <string name="emulation_pause">Pause emulering</string> + <string name="emulation_unpause">Ta emuleringen ut av pause</string> + <string name="emulation_input_overlay">Overlay-alternativer</string> <string name="load_settings">Laster inn innstillinger...</string> <!-- Software keyboard --> - <string name="software_keyboard">Programvare Tastatur</string> + <string name="software_keyboard">Programvaretastatur</string> <!-- Errors and warnings --> <string name="abort_button">Avbryt</string> @@ -226,7 +245,6 @@ <string name="fatal_error">Fatal Feil</string> <string name="fatal_error_message">Det oppstod en fatal feil. Sjekk loggen for mer informasjon.\nFortsatt emulering kan føre til krasj og feil.</string> <string name="performance_warning">Hvis du slår av denne innstillingen, reduseres emuleringsytelsen betydelig! Vi anbefaler at du lar denne innstillingen være aktivert for å få den beste opplevelsen.</string> - <!-- Region Names --> <string name="region_japan">Japan</string> <string name="region_usa">USA</string> @@ -236,8 +254,7 @@ <string name="region_korea">Korea</string> <string name="region_taiwan">Taiwan</string> - <!-- Language Names --> - + <string name="memory_gigabyte">GB</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> <string name="renderer_none">Ingen</string> @@ -274,12 +291,14 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <string name="screen_layout_auto">Auto</string> + <!-- Aspect Ratios --> <string name="ratio_default">Standard (16:9)</string> <string name="ratio_force_four_three">Tving 4:3</string> <string name="ratio_force_twenty_one_nine">Tving 21:9</string> <string name="ratio_force_sixteen_ten">Tving 16:10</string> - <string name="ratio_stretch">Strekk til Vindu</string> + <string name="ratio_stretch">Strekk til vindu</string> <!-- CPU Accuracy --> <string name="cpu_accuracy_accurate">Nøyaktig</string> @@ -287,9 +306,9 @@ <string name="cpu_accuracy_paranoid">Paranoid (Langsom)</string> <!-- Gamepad Buttons --> - <string name="gamepad_d_pad">D-Pad</string> - <string name="gamepad_left_stick">Venstre Pinne</string> - <string name="gamepad_right_stick">Høyre Pinne</string> + <string name="gamepad_d_pad">D-pad</string> + <string name="gamepad_left_stick">Venstre spak</string> + <string name="gamepad_right_stick">Høyre spak</string> <string name="gamepad_home">Hjem</string> <string name="gamepad_screenshot">Skjermbilde</string> @@ -298,7 +317,7 @@ <string name="building_shaders">Bygging av shaders</string> <!-- Theme options --> - <string name="change_app_theme">Endre appens tema</string> + <string name="change_app_theme">Endre app-tema</string> <string name="theme_default">Standard</string> <string name="theme_material_you">Material You</string> @@ -309,7 +328,13 @@ <string name="theme_mode_dark">Mørk</string> <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">Bruk svart bakgrunn</string> + <string name="use_black_backgrounds">Svart bakgrunn</string> <string name="use_black_backgrounds_description">Bruk svart bakgrunn når du bruker det mørke temaet.</string> -</resources> + <string name="mute">Lydløs</string> + <string name="unmute">Slå på lyden</string> + + <!-- Licenses screen strings --> + <string name="licenses">Lisenser</string> + <string name="license_fidelityfx_fsr_description">Oppskalering av høy kvalitet fra AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index 899e233d0..f4d9920c2 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.<br /><br />Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string> <string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string> @@ -25,7 +25,6 @@ <string name="back">Wstecz</string> <string name="add_games">Dodaj gry</string> <string name="add_games_description">Wybierz folder zawierający Twoje gry</string> - <!-- Home strings --> <string name="home_games">Gry</string> <string name="home_search">Szukaj</string> @@ -61,6 +60,8 @@ <string name="invalid_keys_file">Wybrano niepoprawne klucze</string> <string name="install_keys_success">Klucze zainstalowane pomyślnie</string> <string name="reading_keys_failure">Błąd podczas odczytu kluczy</string> + <string name="install_prod_keys_failure_extension_description">Upewnij się że twoje klucze mają rozszerzenie .keys i spróbuj ponownie.</string> + <string name="install_amiibo_keys_failure_extension_description">Upewnij się że twoje klucze mają rozszerzenie .bin i spróbuj ponownie.</string> <string name="invalid_keys_error">Niepoprawne klucze</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">Wybrany plik jest niepoprawny lub uszkodzony. Zrzuć ponownie swoje klucze.</string> @@ -86,7 +87,17 @@ <string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string> <string name="import_saves">Importuj</string> <string name="export_saves">Eksportuj</string> - + <string name="install_firmware">Zainstaluj firmware</string> + <string name="install_firmware_description">Firmware musi być w postaci archiwum ZIP, niektóre gry wymagają go do uruchomienia/prawidłowego działania</string> + <string name="firmware_installing">Instaluję firmware</string> + <string name="firmware_installed_success">Zainstalowano pomyślnie</string> + <string name="firmware_installed_failure">Błąd podczas instalacji firmware</string> + <string name="share_log">Udostępnij logi debugowania</string> + <string name="share_log_description">Podziel się logami yuzu, pomoże to twórcom w poprawie działania emulatora</string> + <string name="share_log_missing">Nie znaleziono plików logów</string> + <string name="install_game_content">Zainstaluj zawartość gry</string> + <string name="install_game_content_description">Zainstaluj aktualizację gry lub dodatek DLC</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia isn\'t real</string> <string name="copied_to_clipboard">Skopiowano do schowka</string> @@ -94,6 +105,7 @@ <string name="contributors">Współtwórcy</string> <string name="contributors_description">Stworzone z \u2764 przez zespół yuzu</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Projekty dzięki którym yuzu mógł zostać stworzony</string> <string name="build">Wersja</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> @@ -114,27 +126,25 @@ <string name="are_you_interested">Jesteś zainteresowany?</string> <!-- General settings strings --> - <string name="frame_limit_enable">Włącz limit szybkości emulacji</string> + <string name="frame_limit_enable">Limit szybkość</string> <string name="frame_limit_enable_description">Włącz, aby ustawić procentowy limit szybkości emulacji</string> <string name="frame_limit_slider">Procentowy limit szybkości emulacji</string> <string name="frame_limit_slider_description">Określa limit szybkości emulacji gier. Domyślna wartość 100% oznacza normalną szybkość z jaką działa gra. Wartości niższe lub wyższe zmniejszą lub zwiększą limit szybkości.</string> <string name="cpu_accuracy">Dokładność procesora CPU</string> - <!-- System settings strings --> <string name="use_docked_mode">Tryb zadokowany</string> - <string name="use_docked_mode_description">Emulacja w trybie stacji dokującej, zwiększa rozdzielczość kosztem wydajności.</string> + <string name="use_docked_mode_description">Zwiększa rozdzielczość kosztem wydajności. Kiedy wyłączone, używany jest tryb Handheld, który obniża rozdzielczość i dzięki temu zwiększa wydajność.</string> <string name="emulated_region">Region emulacji</string> <string name="emulated_language">Język emulacji</string> <string name="select_rtc_date">Ustaw datę RTC</string> <string name="select_rtc_time">Ustaw czas RTC</string> - <string name="use_custom_rtc">Włącz niestandardowy zegar RTC</string> + <string name="use_custom_rtc">Niestandardowy RTC</string> <string name="use_custom_rtc_description">Ta opcja pozwala na wybranie własnych ustawień czasu używanych w czasie emulacji, innych niż czas systemu Android.</string> <string name="set_custom_rtc">Ustaw niestandardowy czas RTC</string> <!-- Graphics settings strings --> - <string name="renderer_api">Interfejs graficzny</string> <string name="renderer_accuracy">Poziom precyzji emulacji</string> - <string name="renderer_resolution">Rozdzielczość</string> + <string name="renderer_resolution">Rozdzielczość (Handheld/Zadokowany)</string> <string name="renderer_vsync">Synchronizacja pionowa VSync</string> <string name="renderer_aspect_ratio">Proporcje ekranu</string> <string name="renderer_scaling_filter">Filtr adaptacji rozdzielczości</string> @@ -143,12 +153,16 @@ <string name="renderer_force_max_clock_description">Wymusza uruchomienie maksymalnego taktowania układu graficznego (zabezpieczenia termiczne będą dalej aktywne).</string> <string name="renderer_asynchronous_shaders">Wyłącz synchronizację shaderów</string> <string name="renderer_asynchronous_shaders_description">Kompiluj oświetlenie bez synchronizacji, poprawi wydajność ale może powodować błędy.</string> - <string name="renderer_debug">Włącz debugowanie grafiki</string> - <string name="renderer_debug_description">Kiedy włączone, interfejs graficzny korzysta z wolnego trybu debugowania błędów.</string> - <string name="use_disk_shader_cache">Użyj pamięci podręcznej shaderów na dysku</string> + <string name="renderer_reactive_flushing">Użyj spłukiwania reaktywnego - reactive flushing</string> + <string name="renderer_reactive_flushing_description">Poprawia jakość renderowania w kilku grach, kosztem wydajności.</string> + <string name="use_disk_shader_cache">Pamięć podręczna shaderów</string> <string name="use_disk_shader_cache_description">Zmniejsza przycięcia przez przechowywanie gotowych wygenerowanych plików oświetlenia w pamięci urządzenia.</string> - <!-- Audio settings strings --> + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="renderer_api">Interfejs graficzny</string> + <string name="renderer_debug">Debugowanie grafiki</string> + <string name="renderer_debug_description">Kiedy włączone, interfejs graficzny korzysta z wolnego trybu debugowania błędów.</string> <string name="audio_volume">Głośność</string> <string name="audio_volume_description">Ustala poziom głośności wyjścia dźwięku.</string> @@ -161,17 +175,21 @@ <string name="reset_setting_confirmation">Przywrócić wartość tego ustawienia do wartości domyślnej?</string> <string name="reset_to_default">Przywróć ustawienia domyślne</string> <string name="reset_all_settings">Przywrócić WSZYSTKIE ustawienia?</string> - <string name="reset_all_settings_description">Wszystkie zaawansowane opcje zostaną przywrócone do wartości domyślnych. Czynności nie będzie można cofnąć.</string> + <string name="reset_all_settings_description">Wszystkie zaawansowane opcje zostaną przywrócone do wartości domyślnych. Czynności nie będzie można cofnąć</string> <string name="settings_reset">Reset ustawień</string> <string name="close">Zamknij</string> <string name="learn_more">Dowiedz się więcej</string> - + <string name="auto">Automatyczny</string> + <string name="submit">Zatwierdź</string> + <string name="string_import">Importuj</string> + <string name="export">Eksportuj</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Wybierz sterownik GPU </string> <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string> <string name="select_gpu_driver_install">Zainstaluj</string> <string name="select_gpu_driver_default">Domyślne</string> <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string> + <string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string> <string name="system_gpu_driver">Systemowy sterownik GPU</string> <string name="installing_driver">Instalowanie sterownika...</string> @@ -182,10 +200,10 @@ <string name="preferences_graphics">Grafika</string> <string name="preferences_audio">Dźwięk</string> <string name="preferences_theme">Motyw i kolor</string> + <string name="preferences_debug">Debug</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">Twój ROM jest zakodowany</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[Użyj przewodnika aby wykonać zrzuty <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">kardridży</a> lub <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">zainstalowanych gier</a>.]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[Upewnij się że plik kluczy <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> jest zainstalowany aby gry mogły zostać odczytane.]]></string> <string name="loader_error_video_core">Błąd inicjacji podsystemu graficznego</string> <string name="loader_error_video_core_description">Zazwyczaj spowodowane niekompatybilnym sterownikiem GPU, instalacja niestandardowego sterownika może rozwiązać ten problem.</string> @@ -198,23 +216,23 @@ <string name="emulation_fps_counter">Licznik FPS</string> <string name="emulation_toggle_controls">Wybierz przyciski</string> <string name="emulation_rel_stick_center">Wycentruj gałki</string> - <string name="emulation_dpad_slide">Ruchomy DPad</string> + <string name="emulation_dpad_slide">Ruchomy D-pad</string> <string name="emulation_haptics">Wibracje haptyczne</string> <string name="emulation_show_overlay">Pokaż przyciski</string> - <string name="emulation_toggle_all">Zaznacz wszystkie</string> + <string name="emulation_toggle_all">Włącz wszystkie</string> <string name="emulation_control_adjust">Dostosuj nakładkę</string> <string name="emulation_control_scale">Skala</string> <string name="emulation_control_opacity">Przeźroczystość</string> - <string name="emulation_touch_overlay_reset">Resetuj</string> + <string name="emulation_touch_overlay_reset">Resetuj nakładkę</string> <string name="emulation_touch_overlay_edit">Edytuj nakładkę</string> <string name="emulation_pause">Wstrzymaj emulację</string> <string name="emulation_unpause">Wznów emulację</string> <string name="emulation_input_overlay">Opcje nakładki</string> - <string name="load_settings">Wczytywanie ustawień...</string> + <string name="load_settings">Wczytuję ustawienia...</string> <!-- Software keyboard --> - <string name="software_keyboard">Klawiatura systemowa</string> + <string name="software_keyboard">Klawiatura programowa</string> <!-- Errors and warnings --> <string name="abort_button">Przerwij</string> @@ -226,7 +244,6 @@ <string name="fatal_error">Błąd krytyczny</string> <string name="fatal_error_message">Wystąpił błąd krytyczny. Szczegóły znajdziesz w pliku log.\nKontynuowanie może spowodować błędy lub przerwanie emulacji. </string> <string name="performance_warning">Wyłączenie tej opcji znacząco ograniczy wydajność! Dla najlepszego doświadczenia, zaleca się zostawienie tej opcji włączonej.</string> - <!-- Region Names --> <string name="region_japan">Japonia</string> <string name="region_usa">USA</string> @@ -236,8 +253,7 @@ <string name="region_korea">Korea</string> <string name="region_taiwan">Tajwan</string> - <!-- Language Names --> - + <string name="memory_gigabyte">GB</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> <string name="renderer_none">Żadny</string> @@ -274,12 +290,14 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <string name="screen_layout_auto">Automatyczny</string> + <!-- Aspect Ratios --> <string name="ratio_default">Domyślne (16:9)</string> <string name="ratio_force_four_three">Wymuś 4:3</string> <string name="ratio_force_twenty_one_nine">Wymuś 21:9</string> <string name="ratio_force_sixteen_ten">Wymuś 16:10</string> - <string name="ratio_stretch">Rozciągnij do Okna</string> + <string name="ratio_stretch">Rozciągnij do okna</string> <!-- CPU Accuracy --> <string name="cpu_accuracy_accurate">Dokładny</string> @@ -287,7 +305,7 @@ <string name="cpu_accuracy_paranoid">Paranoid (Wolny)</string> <!-- Gamepad Buttons --> - <string name="gamepad_d_pad">D-Pad</string> + <string name="gamepad_d_pad">D-pad</string> <string name="gamepad_left_stick">Lewa gałka</string> <string name="gamepad_right_stick">Prawa gałka</string> <string name="gamepad_home">Home</string> @@ -298,18 +316,21 @@ <string name="building_shaders">Budowanie shaderów</string> <!-- Theme options --> - <string name="change_app_theme">Zmień motyw aplikacji</string> + <string name="change_app_theme">Ustaw motyw aplikacji</string> <string name="theme_default">Domyślny</string> <string name="theme_material_you">Material You</string> <!-- Theme Modes --> - <string name="change_theme_mode">Zmiana trybu motywu</string> + <string name="change_theme_mode">Zmień tryb motywu</string> <string name="theme_mode_follow_system">Podążaj za systemowym</string> <string name="theme_mode_light">Jasny</string> <string name="theme_mode_dark">Ciemny</string> <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">Używaj czarnego tła</string> + <string name="use_black_backgrounds">Czarne tła</string> <string name="use_black_backgrounds_description">Kiedy używany ciemny motyw, tła zostają zastąpione czernią.</string> -</resources> + <!-- Licenses screen strings --> + <string name="licenses">Licencje</string> + <string name="license_fidelityfx_fsr_description">Rozciąganie wysokiej jakości od AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index caa095364..8888fc750 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -1,30 +1,31 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> - <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.<br /><br /><![CDATA[2Learn more2]]></string> + <string name="app_disclaimer">Este software executa jogos do console Nintendo Switch. Não estão inclusos nem jogos ou chaves. <br /><br />Antes de começar, por favor localize o arquivo <![CDATA[1 prod.keys 1]]> no armazenamento de seu dispositivo.<br /><br /><![CDATA[2Saiba mais2]]></string> <string name="emulation_notification_channel_name">Emulação está Ativa</string> - <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação está a correr.</string> + <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação estiver em andamento.</string> <string name="emulation_notification_running">Yuzu está em execução </string> <string name="notice_notification_channel_name">Notificações e erros</string> - <string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string> - <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string> + <string name="notice_notification_channel_description">Mostra notificações quando algo dá errado.</string> + <string name="notification_permission_not_granted">Acesso às notificações não concedido!</string> <!-- Setup strings --> - <string name="welcome">Bemvindo! </string> - <string name="welcome_description">Aprende como configurar <b>yuzu</b> e arranca a emulação.</string> - <string name="get_started">Começa</string> - <string name="keys">Chaves</string> - <string name="keys_description">Seleciona o teu ficheiro <b>prod.keys</b> com o botão abaixo.</string> - <string name="select_keys">Seleciona as Chaves</string> + <string name="welcome">Bem-vindo! </string> + <string name="welcome_description">Aprenda como configurar o <b>yuzu</b> e mergulhe na emulação.</string> + <string name="get_started">Primeiros passos</string> + <string name="keys">Keys</string> + <string name="keys_description">Selecione seu arquivo <b>prod.keys</b> com o botão abaixo.</string> + <string name="select_keys">Selecione as Keys</string> <string name="games">Jogos</string> - <string name="games_description">Seleciona a tua pasta <b>Games</b> com o botão abaixo.</string> + <string name="games_description">Seleciona sua pasta <b>Jogos</b> com o botão abaixo.</string> <string name="done">Feito</string> - <string name="done_description">Tudo pronto.\nDisfruta dos teus jogos!</string> + <string name="done_description">Tudo pronto.\nAproveite seus jogos!</string> <string name="text_continue">Continuar</string> <string name="next">Próximo</string> <string name="back">Voltar</string> - <string name="add_games">Adiciona Jogos</string> - <string name="add_games_description">Seleciona a tua pasta de Jogos</string> + <string name="add_games">Adicionar Jogos</string> + <string name="add_games_description">Selecione sua pasta de Jogos</string> + <string name="step_complete">Completo!</string> <!-- Home strings --> <string name="home_games">Jogos</string> @@ -37,7 +38,8 @@ <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string> <string name="add_games_warning_description">Os jogos não serão exibidos na lista de jogos se uma pasta não estiver selecionada.</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> - <string name="home_search_games">Procurar Jogos</string> + <string name="home_search_games">Procurar jogos</string> + <string name="search_settings">Procurar nas definições</string> <string name="games_dir_selected">Pasta de Jogos selecionada</string> <string name="install_prod_keys">Instala prod.keys</string> <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string> @@ -61,15 +63,18 @@ <string name="invalid_keys_file">Ficheiro de chaves inválido</string> <string name="install_keys_success">Chaves instaladas com sucesso</string> <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string> + <string name="install_prod_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .keys e tente novamente.</string> + <string name="install_amiibo_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .bin e tente novamente.</string> <string name="invalid_keys_error">Chaves de encriptação inválidas</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string> <string name="install_gpu_driver">Instala driver para GPU</string> <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string> <string name="advanced_settings">Definições avançadas</string> + <string name="advanced_settings_game">Definições avançadas: %1$s</string> <string name="settings_description">Configura definições do emulador</string> - <string name="search_recently_played">Jogos recentes</string> - <string name="search_recently_added">Adicionados recentemente</string> + <string name="search_recently_played">Jogado recentemente</string> + <string name="search_recently_added">Adicionado recentemente</string> <string name="search_retail">Jogos comerciais</string> <string name="search_homebrew">Homebrew</string> <string name="open_user_folder">Abre a pasta Yuzu</string> @@ -86,6 +91,33 @@ <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> <string name="import_saves">Importar</string> <string name="export_saves">Exportar</string> + <string name="install_firmware">Instalar firmware</string> + <string name="install_firmware_description">O firmware deve estar em um arquivo ZIP e é necessário para iniciar alguns jogos.</string> + <string name="firmware_installing">Instalando firmware</string> + <string name="firmware_installed_success">Firmware instalado com sucesso.</string> + <string name="firmware_installed_failure">Falha na instalação do firmware</string> + <string name="firmware_installed_failure_description">Cofirma que os ficheiros firmware nca estão no root do finheiro zip e tenta de novo.</string> + <string name="share_log">Compartilhe registros de debug.</string> + <string name="share_log_description">Compartilhe o arquivo de registro do yuzu para obter ajuda com problemas</string> + <string name="share_log_missing">Arquivo de registro não encontrado</string> + <string name="install_game_content">Instalar conteúdo de jogos</string> + <string name="install_game_content_description">Instalar atualizações de jogos ou DLC</string> + <string name="installing_game_content">A instalar conteúdo...</string> + <string name="install_game_content_failure">Erro ao instalar ficheiro(s) para NAND</string> + <string name="install_game_content_failure_description">Por favor confitma que o conteúdo(s) é válido e que as prod.keys estão instaladas.</string> + <string name="install_game_content_failure_base">A instalação de jogos base não é permitida para evitar possíveis conflitos.</string> + <string name="install_game_content_failure_file_extension">Sò conteúdos NSP e XCI são suportados. Por favor verifica que o conteúdo(s) do jogo são válidos.</string> + <string name="install_game_content_failed_count">%1$d erro(s) de instalação</string> + <string name="install_game_content_success">Conteúdo(s) de jogo instalados com sucesso</string> + <string name="install_game_content_success_install">%1$d instalado com sucesso</string> + <string name="install_game_content_success_overwrite">%1$d substituída com êxito</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">Drivers personalizados não suportados</string> + <string name="custom_driver_not_supported_description">Carrea«gamento de drivers personalizados não é suportado pr este dispositivo. \nCheck verifica esta opção de futuro para confirmar se o suporte foi adicionado!</string> + <string name="manage_yuzu_data">Administrar dados yuzu</string> + <string name="manage_yuzu_data_description">Importa/exporta firmware, chaves, dados do usuário e mais!</string> + <string name="share_save_file">Partilha ficheiro duardado</string> + <string name="export_save_failed">Erro ao exportar dados guardados</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia não é real</string> @@ -94,7 +126,18 @@ <string name="contributors">Contribuidores</string> <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Projetos que tornam o yuzu para Android possível</string> <string name="build">Versão</string> + <string name="user_data">Dado de utilizados</string> + <string name="user_data_description">Importar/exportar todos dados da aplicação data.\n\n Ao importar dados do utilizados, todos os dados existentes do utilizados serão excluídos!</string> + <string name="exporting_user_data">A exportar dados de utilizados...</string> + <string name="importing_user_data">A importar dados de utilizador...</string> + <string name="import_user_data">Importar dados de utilizados...</string> + <string name="invalid_yuzu_backup">Backup yuzu inválido</string> + <string name="user_data_export_success">Dados de utilizados exportados com sucesso</string> + <string name="user_data_import_success">Dados de utilizador importado com sucesso</string> + <string name="user_data_export_cancelled">Exportação cancelada</string> + <string name="user_data_import_failed_description">Verifiqua se as pastas de dados do utilizados estão na raiz da pasta zip e contêm um arquivo de configuração em config/config.ini e tenta novamente.</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> @@ -114,41 +157,53 @@ <string name="are_you_interested">Estás interessado?</string> <!-- General settings strings --> - <string name="frame_limit_enable">Ativar limite de velocidade</string> - <string name="frame_limit_enable_description">Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal.</string> + <string name="frame_limit_enable">Limite de velocidade</string> + <string name="frame_limit_enable_description">Limita a velocidade da emulação a uma porcentagem específica da velocidade normal.</string> <string name="frame_limit_slider">Percentagem do limite de velocidade</string> - <string name="frame_limit_slider_description">Especifica o limite da percentagem da velocidade da emulação. Com a velocidade por defeito a 100% a emulação será limitada à velocidade normal. Valores maiores ou menores aumentarão ou diminuirão o limite de velocidade.</string> + <string name="frame_limit_slider_description">Especifica a porcentagem para limitar a velocidade de emulação. 100% é o normal. Valores mais altos ou mais baixos irão aumentar ou diminuir o limite de velocidade.</string> <string name="cpu_accuracy">Precisão do CPU</string> + <string name="value_with_units">%1$s%2$s</string> <!-- System settings strings --> - <string name="use_docked_mode">Modo ancorado</string> - <string name="use_docked_mode_description">Emula em modo ancorado, que aumenta a resolução ás custas da performance.</string> + <string name="use_docked_mode">Modo Ancorado</string> + <string name="use_docked_mode_description">Aumenta a resolução, diminuindo o desempenho. O Modo Portátil é utilizado quando estiver desabilitado, diminuindo a resolução e melhorando o desempenho.</string> <string name="emulated_region">Região da emulação</string> <string name="emulated_language">Idioma da emulação</string> - <string name="select_rtc_date">Seleciona a data RTC</string> - <string name="select_rtc_time">Seleciona a hora RTC</string> - <string name="use_custom_rtc">Ativa RTC personalizado</string> - <string name="use_custom_rtc_description">Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema</string> - <string name="set_custom_rtc">Define RTC personalizado</string> + <string name="select_rtc_date">Selecione a data do sistema</string> + <string name="select_rtc_time">Selecione a hora do sistema</string> + <string name="use_custom_rtc">Data e hora personalizada</string> + <string name="use_custom_rtc_description">Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo.</string> + <string name="set_custom_rtc">Defina um relógio em tempo real personalizado</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">Nível de precisão</string> - <string name="renderer_resolution">Resolução</string> + <string name="renderer_resolution">Resolução (Portátil/Ancorado)</string> <string name="renderer_vsync">Modo VSync</string> - <string name="renderer_aspect_ratio">Proporção do ecrã</string> + <string name="renderer_screen_layout">Oriantação</string> + <string name="renderer_aspect_ratio">Proporção da tela</string> <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string> - <string name="renderer_anti_aliasing">Método de Anti-Aliasing </string> + <string name="renderer_anti_aliasing">Método de Anti-Serrilhado</string> <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string> <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string> <string name="renderer_asynchronous_shaders">Usa shaders assíncronos </string> - <string name="renderer_asynchronous_shaders_description">Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas.</string> + <string name="renderer_asynchronous_shaders_description">Compila os shaders de forma assíncrona, reduzindo travamentos, mas pode apresentar problemas.</string> + <string name="renderer_reactive_flushing">Usar flushing reativo</string> + <string name="renderer_reactive_flushing_description">Melhora a precisão da renderização em alguns jogos ao custo de desempenho.</string> + <string name="use_disk_shader_cache">Cache de shaders em disco</string> + <string name="use_disk_shader_cache_description">Reduz travamentos ao armazenar e carregar localmente os shaders.</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">Depuração da CPU</string> + <string name="cpu_debug_mode_description">Coloca a CPU em um modo de depuração lento.</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> <string name="renderer_debug">Ativar depuração de gráficos</string> <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string> - <string name="use_disk_shader_cache">Usar cache de shaders em disco</string> - <string name="use_disk_shader_cache_description">Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento.</string> + <string name="fastmem">Fastmem</string> <!-- Audio settings strings --> + <string name="audio_output_engine">Motor de saída</string> <string name="audio_volume">Volume</string> <string name="audio_volume_description">Especifica o volume de saída.</string> @@ -157,14 +212,24 @@ <string name="ini_saved">Definições guardadas</string> <string name="gameid_saved">Definições guardadas para %1$s</string> <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string> + <string name="unimplemented_menu">Menu não implementado</string> <string name="loading">A carregar...</string> + <string name="shutting_down">A desligar...</string> <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string> <string name="reset_to_default">Reverter para padrão</string> <string name="reset_all_settings">Redefinir todas as definições?</string> - <string name="reset_all_settings_description">Todas as definições avançadas serão redefinidas para as definições padrão. Isto não pode ser revertido.</string> + <string name="reset_all_settings_description">Todas as configurações avançadas retornarão ao padrão. Isto não pode ser desfeito.</string> <string name="settings_reset">Redefinir definições</string> <string name="close">Fechar</string> <string name="learn_more">Saiba mais</string> + <string name="auto">Automático</string> + <string name="submit">Enviar</string> + <string name="string_null">Nenhum (desativado)</string> + <string name="string_import">Importar</string> + <string name="export">Exportar</string> + <string name="export_failed">Exportação falhada</string> + <string name="import_failed">IMportação falhada</string> + <string name="cancelling">A cancelar</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Seleciona a driver para o GPU</string> @@ -172,6 +237,7 @@ <string name="select_gpu_driver_install">Instalar</string> <string name="select_gpu_driver_default">Padrão</string> <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> + <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string> <string name="system_gpu_driver">Driver do GPU padrão</string> <string name="installing_driver">A instalar o Driver...</string> @@ -182,10 +248,11 @@ <string name="preferences_graphics">Gráficos</string> <string name="preferences_audio">Áudio</string> <string name="preferences_theme">Cor e tema.</string> + <string name="preferences_debug">Depuração</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">A tua ROM está encriptada</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor segue os guias para fazer redump das tuas<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">Cartidges de Jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">Jogos Instalados</a>.]]></string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga os guias para despejar novamente o seu <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartucho de jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">títulos instalados</a>.]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor confirma que o teu ficheiro <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado para que os jogos possam ser desencriptados.]]></string> <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vídeo.</string> <string name="loader_error_video_core_description">Isto é normalmente causado por um driver de GPU incompatível. Instalar um driver GPU pode resolver este problema.</string> @@ -193,25 +260,25 @@ <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string> <!-- Emulation Menu --> - <string name="emulation_exit">Sair da emulação</string> + <string name="emulation_exit">Parar emulação</string> <string name="emulation_done">Feito</string> <string name="emulation_fps_counter">Contador de FPS</string> - <string name="emulation_toggle_controls">Alterar Controlos</string> - <string name="emulation_rel_stick_center">Centro do Analógico Relativo</string> - <string name="emulation_dpad_slide">Deslizar do DPad</string> - <string name="emulation_haptics">Hápticos </string> - <string name="emulation_show_overlay">Mostrar sobreposição </string> - <string name="emulation_toggle_all">Alterar todos</string> - <string name="emulation_control_adjust">Ajustar a sobreposição </string> + <string name="emulation_toggle_controls">Alterar controles</string> + <string name="emulation_rel_stick_center">Centro Relativo de Analógico</string> + <string name="emulation_dpad_slide">Deslizamento dos Botões Direcionais</string> + <string name="emulation_haptics">Vibração ao tocar</string> + <string name="emulation_show_overlay">Mostrar overlay</string> + <string name="emulation_toggle_all">Marcar/Desmarcar tudo</string> + <string name="emulation_control_adjust">Ajustar overlay</string> <string name="emulation_control_scale">Escala</string> <string name="emulation_control_opacity">Opacidade</string> - <string name="emulation_touch_overlay_reset">Redefinir Sobreposição </string> - <string name="emulation_touch_overlay_edit">Editar sobreposição </string> - <string name="emulation_pause">Pausa emulação</string> + <string name="emulation_touch_overlay_reset">Restaurar overlay padrão</string> + <string name="emulation_touch_overlay_edit">Editar overlay</string> + <string name="emulation_pause">Pausar emulação</string> <string name="emulation_unpause">Retomar emulação</string> - <string name="emulation_input_overlay">Opções de sobreposição </string> + <string name="emulation_input_overlay">Opções de overlay</string> - <string name="load_settings">Configurações a carregar...</string> + <string name="load_settings">Carregando configurações...</string> <!-- Software keyboard --> <string name="software_keyboard">Teclado de software</string> @@ -226,6 +293,9 @@ <string name="fatal_error">Erro fatal</string> <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string> <string name="performance_warning">Desligar esta configuração irá reduzir a performance da emulação significantemente! Para a melhor experiência é recomendado que deixes esta configuração ativada.</string> + <string name="device_memory_inadequate">RAM do dispositivo: %1$s\nRecommended: %2$s</string> + <string name="memory_formatted">%1$s %2$s</string> + <string name="no_game_present">Nenhum jogo inicializável presente!</string> <!-- Region Names --> <string name="region_japan">Japão</string> @@ -236,7 +306,14 @@ <string name="region_korea">Coréia</string> <string name="region_taiwan">Taiwan</string> - <!-- Language Names --> + <!-- Memory Sizes --> + <string name="memory_byte">Byte</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulcano</string> @@ -274,12 +351,17 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <!-- Screen Layouts --> + <string name="screen_layout_landscape">Landscape</string> + <string name="screen_layout_portrait">Portrait</string> + <string name="screen_layout_auto">Automático</string> + <!-- Aspect Ratios --> <string name="ratio_default">Padrão (16:9)</string> <string name="ratio_force_four_three">Forçar 4:3</string> <string name="ratio_force_twenty_one_nine">Forçar 21:9</string> <string name="ratio_force_sixteen_ten">Forçar 16:10</string> - <string name="ratio_stretch">Esticar para a janela</string> + <string name="ratio_stretch">Esticar à janela</string> <!-- CPU Accuracy --> <string name="cpu_accuracy_accurate">Preciso</string> @@ -287,7 +369,7 @@ <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string> <!-- Gamepad Buttons --> - <string name="gamepad_d_pad">D-pad</string> + <string name="gamepad_d_pad">Botões Direcionais</string> <string name="gamepad_left_stick">Analógico esquerdo</string> <string name="gamepad_right_stick">Analógico direito</string> <string name="gamepad_home">Botão Home</string> @@ -298,18 +380,32 @@ <string name="building_shaders">A criar shaders</string> <!-- Theme options --> - <string name="change_app_theme">Muda o Tema da App</string> + <string name="change_app_theme">Mudar o tema do aplicativo</string> <string name="theme_default">Padrão</string> <string name="theme_material_you">Material You</string> <!-- Theme Modes --> - <string name="change_theme_mode">Altera o Modo do Tema</string> + <string name="change_theme_mode">Alterar o tema</string> <string name="theme_mode_follow_system">Igual ao Sistema</string> <string name="theme_mode_light">Claro</string> <string name="theme_mode_dark">Escuro</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">Usa Fundos Negros</string> + <string name="use_black_backgrounds">Plano de fundo preto</string> <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string> -</resources> + <!-- Picture-In-Picture --> + <string name="picture_in_picture">Picture in Picture</string> + <string name="picture_in_picture_description">Minimizar a janela quando colocada em segundo plano</string> + <string name="pause">Pausa</string> + <string name="play">Correr</string> + <string name="mute">Mudo</string> + <string name="unmute">Unmute</string> + + <!-- Licenses screen strings --> + <string name="licenses">Licenças</string> + <string name="license_fidelityfx_fsr_description">Upscaling de alta qualidade da AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index 0a1a47fbb..6afea9b03 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.<br /><br /><![CDATA[2Learn more2]]></string> <string name="emulation_notification_channel_name">Emulação está Ativa</string> @@ -25,6 +25,7 @@ <string name="back">Voltar</string> <string name="add_games">Adiciona Jogos</string> <string name="add_games_description">Seleciona a tua pasta de Jogos</string> + <string name="step_complete">Completo!</string> <!-- Home strings --> <string name="home_games">Jogos</string> @@ -37,7 +38,8 @@ <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string> <string name="add_games_warning_description">Os jogos não serão exibidos na lista de jogos se uma pasta não estiver selecionada.</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> - <string name="home_search_games">Procurar Jogos</string> + <string name="home_search_games">Procurar jogos</string> + <string name="search_settings">Procurar nas definições</string> <string name="games_dir_selected">Pasta de Jogos selecionada</string> <string name="install_prod_keys">Instala prod.keys</string> <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string> @@ -61,15 +63,18 @@ <string name="invalid_keys_file">Ficheiro de chaves inválido</string> <string name="install_keys_success">Chaves instaladas com sucesso</string> <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string> + <string name="install_prod_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .keys e tente novamente.</string> + <string name="install_amiibo_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .bin e tente novamente.</string> <string name="invalid_keys_error">Chaves de encriptação inválidas</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string> <string name="install_gpu_driver">Instala driver para GPU</string> <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string> <string name="advanced_settings">Configurações avançadas</string> + <string name="advanced_settings_game">Definições avançadas: %1$s</string> <string name="settings_description">Configura configurações do emulador</string> - <string name="search_recently_played">Jogos recentes</string> - <string name="search_recently_added">Adicionados recentemente</string> + <string name="search_recently_played">Jogado recentemente</string> + <string name="search_recently_added">Adicionado recentemente</string> <string name="search_retail">Jogos comerciais</string> <string name="search_homebrew">Homebrew</string> <string name="open_user_folder">Abre a pasta Yuzu</string> @@ -86,6 +91,33 @@ <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> <string name="import_saves">Importar</string> <string name="export_saves">Exportar</string> + <string name="install_firmware">Instalar firmware</string> + <string name="install_firmware_description">O firmware deve estar em um arquivo ZIP e é necessário para iniciar alguns jogos.</string> + <string name="firmware_installing">Instalando firmware</string> + <string name="firmware_installed_success">Firmware instalado com sucesso.</string> + <string name="firmware_installed_failure">Falha na instalação do firmware</string> + <string name="firmware_installed_failure_description">Cofirma que os ficheiros firmware nca estão no root do finheiro zip e tenta de novo.</string> + <string name="share_log">Compartilhe registros de debug.</string> + <string name="share_log_description">Compartilhe o arquivo de registro do yuzu para obter ajuda com problemas</string> + <string name="share_log_missing">Arquivo de registro não encontrado</string> + <string name="install_game_content">Instalar conteúdo adicional</string> + <string name="install_game_content_description">Instale atualizações de jogos ou DLC</string> + <string name="installing_game_content">A instalar conteúdo...</string> + <string name="install_game_content_failure">Erro ao instalar ficheiro(s) para NAND</string> + <string name="install_game_content_failure_description">Por favor confitma que o conteúdo(s) é válido e que as prod.keys estão instaladas.</string> + <string name="install_game_content_failure_base">A instalação de jogos base não é permitida para evitar possíveis conflitos.</string> + <string name="install_game_content_failure_file_extension">Sò conteúdos NSP e XCI são suportados. Por favor verifica que o conteúdo(s) do jogo são válidos.</string> + <string name="install_game_content_failed_count">%1$d erro(s) de instalação</string> + <string name="install_game_content_success">Conteúdo(s) de jogo instalados com sucesso</string> + <string name="install_game_content_success_install">%1$d instalado com sucesso</string> + <string name="install_game_content_success_overwrite">%1$d substituída com êxito</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">Drivers personalizados não suportados</string> + <string name="custom_driver_not_supported_description">Carrea«gamento de drivers personalizados não é suportado pr este dispositivo. \nCheck verifica esta opção de futuro para confirmar se o suporte foi adicionado!</string> + <string name="manage_yuzu_data">Administrar dados yuzu</string> + <string name="manage_yuzu_data_description">Importa/exporta firmware, chaves, dados do usuário e mais!</string> + <string name="share_save_file">Partilha ficheiro duardado</string> + <string name="export_save_failed">Erro ao exportar dados guardados</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia não é real</string> @@ -94,7 +126,18 @@ <string name="contributors">Contribuidores</string> <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Projetos que tornam o yuzu para Android possível</string> <string name="build">Versão</string> + <string name="user_data">Dado de utilizados</string> + <string name="user_data_description">Importar/exportar todos dados da aplicação data.\n\n Ao importar dados do utilizados, todos os dados existentes do utilizados serão excluídos!</string> + <string name="exporting_user_data">A exportar dados de utilizados...</string> + <string name="importing_user_data">A importar dados de utilizador...</string> + <string name="import_user_data">Importar dados de utilizados...</string> + <string name="invalid_yuzu_backup">Backup yuzu inválido</string> + <string name="user_data_export_success">Dados de utilizados exportados com sucesso</string> + <string name="user_data_import_success">Dados de utilizador importado com sucesso</string> + <string name="user_data_export_cancelled">Exportação cancelada</string> + <string name="user_data_import_failed_description">Verifiqua se as pastas de dados do utilizados estão na raiz da pasta zip e contêm um arquivo de configuração em config/config.ini e tenta novamente.</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> @@ -114,41 +157,53 @@ <string name="are_you_interested">Estás interessado?</string> <!-- General settings strings --> - <string name="frame_limit_enable">Ativar limite de velocidade</string> - <string name="frame_limit_enable_description">Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal.</string> + <string name="frame_limit_enable">Limite de velocidade</string> + <string name="frame_limit_enable_description">Limita a velocidade da emulação a uma porcentagem específica da velocidade normal.</string> <string name="frame_limit_slider">Percentagem do limite de velocidade</string> - <string name="frame_limit_slider_description">Especifica o limite da percentagem da velocidade da emulação. Com a velocidade por defeito a 100% a emulação será limitada à velocidade normal. Valores maiores ou menores aumentarão ou diminuirão o limite de velocidade.</string> + <string name="frame_limit_slider_description">Especifica a porcentagem para limitar a velocidade de emulação. 100% é o normal. Valores mais altos ou mais baixos irão aumentar ou diminuir o limite de velocidade.</string> <string name="cpu_accuracy">Precisão do CPU</string> + <string name="value_with_units">%1$s%2$s</string> <!-- System settings strings --> - <string name="use_docked_mode">Modo ancorado</string> - <string name="use_docked_mode_description">Emula em modo ancorado, que aumenta a resolução ás custas da performance.</string> + <string name="use_docked_mode">Modo Ancorado</string> + <string name="use_docked_mode_description">Aumenta a resolução, diminuindo o desempenho. O Modo Portátil é utilizado quando estiver desabilitado, diminuindo a resolução e melhorando o desempenho.</string> <string name="emulated_region">Região da emulação</string> <string name="emulated_language">Idioma da emulação</string> - <string name="select_rtc_date">Seleciona a data RTC</string> - <string name="select_rtc_time">Seleciona a hora RTC</string> - <string name="use_custom_rtc">Ativa RTC personalizado</string> - <string name="use_custom_rtc_description">Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema</string> - <string name="set_custom_rtc">Define RTC personalizado</string> + <string name="select_rtc_date">Selecione a data do sistema</string> + <string name="select_rtc_time">Selecione a hora do sistema</string> + <string name="use_custom_rtc">RTC personalizado</string> + <string name="use_custom_rtc_description">Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo.</string> + <string name="set_custom_rtc">Defina um relógio em tempo real personalizado</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">Nível de precisão</string> - <string name="renderer_resolution">Resolução</string> + <string name="renderer_resolution">Resolução (Portátil/Ancorado)</string> <string name="renderer_vsync">Modo VSync</string> - <string name="renderer_aspect_ratio">Proporção do ecrã</string> + <string name="renderer_screen_layout">Oriantação</string> + <string name="renderer_aspect_ratio">Proporção da tela</string> <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string> - <string name="renderer_anti_aliasing">Método de Anti-Aliasing </string> + <string name="renderer_anti_aliasing">Método de Anti-Serrilhado</string> <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string> <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string> <string name="renderer_asynchronous_shaders">Usa shaders assíncronos </string> - <string name="renderer_asynchronous_shaders_description">Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas.</string> + <string name="renderer_asynchronous_shaders_description">Compila os shaders de forma assíncrona, reduzindo travamentos, mas pode apresentar problemas.</string> + <string name="renderer_reactive_flushing">Usar flushing reativo</string> + <string name="renderer_reactive_flushing_description">Melhora a precisão da renderização em alguns jogos ao custo de desempenho.</string> + <string name="use_disk_shader_cache">Cache de shaders em disco</string> + <string name="use_disk_shader_cache_description">Reduz travamentos ao armazenar e carregar localmente os shaders.</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">Depuração da CPU</string> + <string name="cpu_debug_mode_description">Coloca a CPU em um modo de depuração lento.</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> <string name="renderer_debug">Ativar depuração de gráficos</string> <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string> - <string name="use_disk_shader_cache">Usar cache do disk shader</string> - <string name="use_disk_shader_cache_description">Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento.</string> + <string name="fastmem">Fastmem</string> <!-- Audio settings strings --> + <string name="audio_output_engine">Motor de saída</string> <string name="audio_volume">Volume</string> <string name="audio_volume_description">Especifica o volume de saída.</string> @@ -157,14 +212,24 @@ <string name="ini_saved">Configurações guardadas</string> <string name="gameid_saved">Configurações guardadas para %1$s</string> <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string> + <string name="unimplemented_menu">Menu não implementado</string> <string name="loading">A carregar...</string> + <string name="shutting_down">A desligar...</string> <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string> <string name="reset_to_default">Reverter para padrão</string> <string name="reset_all_settings">Redefinir todas as configurações?</string> - <string name="reset_all_settings_description">Todas as configurações avançadas serão redefinidas para as definições padrão. Isto não pode ser revertido.</string> + <string name="reset_all_settings_description">Todas as configurações avançadas retornarão ao padrão. Isto não pode ser desfeito.</string> <string name="settings_reset">Redefinir configurações </string> <string name="close">Fechar</string> - <string name="learn_more">Saber Mais</string> + <string name="learn_more">Saber mais</string> + <string name="auto">Automático</string> + <string name="submit">Enviar</string> + <string name="string_null">Nenhum (desativado)</string> + <string name="string_import">Importar</string> + <string name="export">Exportar</string> + <string name="export_failed">Exportação falhada</string> + <string name="import_failed">IMportação falhada</string> + <string name="cancelling">A cancelar</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Seleciona a driver para o GPU</string> @@ -172,6 +237,7 @@ <string name="select_gpu_driver_install">Instalar</string> <string name="select_gpu_driver_default">Padrão</string> <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> + <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string> <string name="system_gpu_driver">Driver do GPU padrão</string> <string name="installing_driver">A instalar o Driver...</string> @@ -182,10 +248,11 @@ <string name="preferences_graphics">Gráficos</string> <string name="preferences_audio">Audio</string> <string name="preferences_theme">Cor e tema.</string> + <string name="preferences_debug">Depurar</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">A tua ROM está encriptada</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor segue os guias para fazer redump das tuas<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">Cartidges de Jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">Jogos Instalados</a>.]]></string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga os guias para despejar novamente o seu <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartucho de jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">títulos instalados</a>.]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[Por favor confirma que o teu ficheiro <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> está instalado para que os jogos possam ser desencriptados.]]></string> <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vídeo.</string> <string name="loader_error_video_core_description">Isto é normalmente causado por um driver de GPU incompatível. Instalar um driver GPU pode resolver este problema.</string> @@ -193,28 +260,28 @@ <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string> <!-- Emulation Menu --> - <string name="emulation_exit">Sair da emulação</string> + <string name="emulation_exit">Parar emulação</string> <string name="emulation_done">Feito</string> <string name="emulation_fps_counter">Contador de FPS</string> - <string name="emulation_toggle_controls">Alterar Controlos</string> - <string name="emulation_rel_stick_center">Centro do Analógico Relativo</string> - <string name="emulation_dpad_slide">Deslizar do DPad</string> - <string name="emulation_haptics">Hápticos </string> - <string name="emulation_show_overlay">Mostrar sobreposição </string> - <string name="emulation_toggle_all">Alterar todos</string> - <string name="emulation_control_adjust">Ajustar a sobreposição </string> + <string name="emulation_toggle_controls">Alterar controles</string> + <string name="emulation_rel_stick_center">Centro Relativo de Analógico</string> + <string name="emulation_dpad_slide">Deslizamento dos Botões Direcionais</string> + <string name="emulation_haptics">Vibração ao tocar</string> + <string name="emulation_show_overlay">Mostrar overlay</string> + <string name="emulation_toggle_all">Marcar/Desmarcar tudo</string> + <string name="emulation_control_adjust">Ajustar overlay</string> <string name="emulation_control_scale">Escala</string> <string name="emulation_control_opacity">Opacidade</string> - <string name="emulation_touch_overlay_reset">Redefinir Sobreposição </string> - <string name="emulation_touch_overlay_edit">Editar sobreposição </string> - <string name="emulation_pause">Pausa emulação</string> - <string name="emulation_unpause">Retomar emulação</string> - <string name="emulation_input_overlay">Opções de sobreposição </string> + <string name="emulation_touch_overlay_reset">Restaurar overlay padrão</string> + <string name="emulation_touch_overlay_edit">Editar overlay</string> + <string name="emulation_pause">Pausar emulação</string> + <string name="emulation_unpause">Despausar emulação</string> + <string name="emulation_input_overlay">Opções de overlay</string> - <string name="load_settings">Configurações a carregar...</string> + <string name="load_settings">Carregando configurações...</string> <!-- Software keyboard --> - <string name="software_keyboard">Teclado de Software</string> + <string name="software_keyboard">Teclado de software</string> <!-- Errors and warnings --> <string name="abort_button">Abortar</string> @@ -226,6 +293,9 @@ <string name="fatal_error">Erro fatal</string> <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string> <string name="performance_warning">Desligar esta configuração irá reduzir a performance da emulação significantemente! Para a melhor experiência é recomendado que deixes esta configuração ativada.</string> + <string name="device_memory_inadequate">RAM do dispositivo: %1$s\nRecommended: %2$s</string> + <string name="memory_formatted">%1$s %2$s</string> + <string name="no_game_present">Nenhum jogo inicializável presente!</string> <!-- Region Names --> <string name="region_japan">Japão</string> @@ -236,7 +306,14 @@ <string name="region_korea">Coreia</string> <string name="region_taiwan">Taiwan</string> - <!-- Language Names --> + <!-- Memory Sizes --> + <string name="memory_byte">Byte</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulcano</string> @@ -274,12 +351,17 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <!-- Screen Layouts --> + <string name="screen_layout_landscape">Landscape</string> + <string name="screen_layout_portrait">Portrait</string> + <string name="screen_layout_auto">Automático</string> + <!-- Aspect Ratios --> <string name="ratio_default">Padrão (16:9)</string> <string name="ratio_force_four_three">Forçar 4:3</string> <string name="ratio_force_twenty_one_nine">Forçar 21:9</string> <string name="ratio_force_sixteen_ten">Forçar 16:10</string> - <string name="ratio_stretch">Esticar à Janela</string> + <string name="ratio_stretch">Esticar à janela</string> <!-- CPU Accuracy --> <string name="cpu_accuracy_accurate">Preciso</string> @@ -287,9 +369,9 @@ <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string> <!-- Gamepad Buttons --> - <string name="gamepad_d_pad">D-Pad</string> - <string name="gamepad_left_stick">Analógico Esquerdo</string> - <string name="gamepad_right_stick">Analógico Direito</string> + <string name="gamepad_d_pad">Botões Direcionais</string> + <string name="gamepad_left_stick">Analógico esquerdo</string> + <string name="gamepad_right_stick">Analógico direito</string> <string name="gamepad_home">Home</string> <string name="gamepad_screenshot">Captura de ecrã</string> @@ -298,18 +380,32 @@ <string name="building_shaders">A criar shaders</string> <!-- Theme options --> - <string name="change_app_theme">Muda o Tema da App</string> + <string name="change_app_theme">Mudar o tema do aplicativo</string> <string name="theme_default">Padrão</string> <string name="theme_material_you">Material You</string> <!-- Theme Modes --> - <string name="change_theme_mode">Altera o Modo do Tema</string> + <string name="change_theme_mode">Alterar o tema</string> <string name="theme_mode_follow_system">Igual ao Sistema</string> <string name="theme_mode_light">Claro</string> <string name="theme_mode_dark">Escuro</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">Usa Fundos Escuros</string> + <string name="use_black_backgrounds">Plano de fundo preto</string> <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string> -</resources> + <!-- Picture-In-Picture --> + <string name="picture_in_picture">Picture in Picture</string> + <string name="picture_in_picture_description">Minimizar a janela quando colocada em segundo plano</string> + <string name="pause">Pausa</string> + <string name="play">Correr</string> + <string name="mute">Mute</string> + <string name="unmute">Unmute</string> + + <!-- Licenses screen strings --> + <string name="licenses">Licenças</string> + <string name="license_fidelityfx_fsr_description">Upscaling de alta qualidade da AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 0bef035d6..c614257a8 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <string name="app_disclaimer">Это программное обеспечение позволяет запускать игры для игровой консоли Nintendo Switch. Мы не предоставляем сами игры или ключи.<br /><br />Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище устройства..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string> <string name="emulation_notification_channel_name">Эмуляция активна</string> @@ -7,7 +7,7 @@ <string name="emulation_notification_running">yuzu запущен</string> <string name="notice_notification_channel_name">Уведомления и ошибки</string> <string name="notice_notification_channel_description">Показывать уведомления, когда что-то пошло не так</string> - <string name="notification_permission_not_granted">Вы не предоставили разрешение уведомлений!</string> + <string name="notification_permission_not_granted">Вы не предоставили разрешение на уведомления!</string> <!-- Setup strings --> <string name="welcome">Добро пожаловать!</string> @@ -25,6 +25,7 @@ <string name="back">Назад</string> <string name="add_games">Добавить игры</string> <string name="add_games_description">Выберите папку с играми</string> + <string name="step_complete">Выполнено!</string> <!-- Home strings --> <string name="home_games">Игры</string> @@ -38,6 +39,7 @@ <string name="add_games_warning_description">Игры не будут отображаться в списке Игры, если папка не выбрана.</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> <string name="home_search_games">Найти игры</string> + <string name="search_settings">Настройки поиска</string> <string name="games_dir_selected">Выбрана папка с играми</string> <string name="install_prod_keys">Установить prod.keys</string> <string name="install_prod_keys_description">Требуется для расшифровки розничных игр</string> @@ -61,14 +63,17 @@ <string name="invalid_keys_file">Выбран неверный файл ключей</string> <string name="install_keys_success">Ключи успешно установлены</string> <string name="reading_keys_failure">Ошибка при чтении ключей шифрования</string> + <string name="install_prod_keys_failure_extension_description">Убедитесь, что файл ключей имеет расширение .keys, и повторите попытку.</string> + <string name="install_amiibo_keys_failure_extension_description">Убедитесь, что файл ключей имеет расширение .bin, и повторите попытку.</string> <string name="invalid_keys_error">Неверные ключи шифрования</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">Выбранный файл неверен или поврежден. Пожалуйста, пере-дампите ваши ключи.</string> <string name="install_gpu_driver">Установить драйвер ГП</string> <string name="install_gpu_driver_description">Установите альтернативные драйверы для потенциально лучшей производительности и/или точности</string> <string name="advanced_settings">Расширенные настройки</string> + <string name="advanced_settings_game">Расширенные настройки: %1$s</string> <string name="settings_description">Настройка параметров эмулятора</string> - <string name="search_recently_played">Недавно сыграно</string> + <string name="search_recently_played">Недавно сыгранные</string> <string name="search_recently_added">Недавно добавлено</string> <string name="search_retail">Розничные</string> <string name="search_homebrew">Homebrew</string> @@ -86,6 +91,34 @@ <string name="save_file_invalid_zip_structure_description">Название первой вложенной папки должно быть идентификатором игры.</string> <string name="import_saves">Импорт</string> <string name="export_saves">Экспорт</string> + <string name="install_firmware">Установить прошивку</string> + <string name="install_firmware_description">Прошивка должна находиться в ZIP-архиве и необходима для загрузки некоторых игр</string> + <string name="firmware_installing">Установка прошивки</string> + <string name="firmware_installed_success">Прошивка успешно установлена</string> + <string name="firmware_installed_failure">Не удалось установить прошивку</string> + <string name="firmware_installed_failure_description">Убедитесь что файлы прошивки nca находятся в корне zip-архива и повторите попытку.</string> + <string name="share_log">Поделиться журналом отладки</string> + <string name="share_log_description">Поделиться журналом отладки yuzu для устранения проблем</string> + <string name="share_log_missing">Файл журнала не найден</string> + <string name="install_game_content">Установить игровой контент</string> + <string name="install_game_content_description">Установить обновления игры или дополнений</string> + <string name="installing_game_content">Установка контента...</string> + <string name="install_game_content_failure">Ошибка установки файл(ов) в NAND.</string> + <string name="install_game_content_failure_description">Убедитесь что содержимое допустимо и что файл prod.keys установлен.</string> + <string name="install_game_content_failure_base">Установка базовых игр запрещена во избежание возможных конфликтов.</string> + <string name="install_game_content_failure_file_extension">Поддерживается только контент NSP и XCI. Пожалуйста убедитесь что игровой контент действителен.</string> + <string name="install_game_content_failed_count">%1$d ошибка установки</string> + <string name="install_game_content_success">Игровой контент успешно установлен</string> + <string name="install_game_content_success_install">%1$d Успешно установлено</string> + <string name="install_game_content_success_overwrite">%1$d Успешно перезаписано</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">Пользовательские драйверы не поддерживаются</string> + <string name="custom_driver_not_supported_description">Загрузка пользовательского драйвера в настоящее время не поддерживается для этого устройства.\nПроверьте этот параметр еще раз в будущем чтобы узнать была ли добавлена поддержка! + </string> + <string name="manage_yuzu_data">Управление данными yuzu</string> + <string name="manage_yuzu_data_description">Импортируйте/экспортируйте прошивку, ключи, пользовательские данные и многое другое!</string> + <string name="share_save_file">Поделиться файлом сохранения</string> + <string name="export_save_failed">Не удалось экспортировать сохранение</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia не существует</string> @@ -94,7 +127,18 @@ <string name="contributors">Контрибьюторы</string> <string name="contributors_description">Сделано с \u2764 от команды yuzu</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Проекты, которые сделали yuzu для Android возможным</string> <string name="build">Сборка</string> + <string name="user_data">Данные пользователя</string> + <string name="user_data_description">Импортируйте/экспортируйте все данные приложения.\n\nПри импорте пользовательских данных все существующие пользовательские данные будут удалены!</string> + <string name="exporting_user_data">Экспорт пользовательских данных…</string> + <string name="importing_user_data">Импорт пользовательских данных…</string> + <string name="import_user_data">Импортировать пользовательские данные</string> + <string name="invalid_yuzu_backup">Неверная резервная копия yuzu</string> + <string name="user_data_export_success">Пользовательские данные успешно экспортированы</string> + <string name="user_data_import_success">Пользовательские данные успешно импортированы</string> + <string name="user_data_export_cancelled">Экспорт отменен</string> + <string name="user_data_import_failed_description">Убедитесь что папки пользовательских данных находятся в корне zip-папки и содержат файл конфигурации config/config.ini и повторите попытку.</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> @@ -114,41 +158,51 @@ <string name="are_you_interested">Вы заинтересованы?</string> <!-- General settings strings --> - <string name="frame_limit_enable">Включить ограничение скорости</string> - <string name="frame_limit_enable_description">Если эта функция включена, скорость эмуляции будет ограничена указанным процентом от нормальной скорости.</string> + <string name="frame_limit_enable">Ограничить скорость</string> + <string name="frame_limit_enable_description">Ограничивает скорость эмуляции указанным процентом от нормальной скорости.</string> <string name="frame_limit_slider">Ограничение процента cкорости</string> - <string name="frame_limit_slider_description">Указывает процент для ограничения скорости эмуляции. При значении по умолчанию 100% эмуляция будет ограничена нормальной скоростью. Значения выше или ниже будут увеличивать или уменьшать ограничение скорости.</string> + <string name="frame_limit_slider_description">Указывает процент ограничения скорости эмуляции. 100% - это нормальная скорость. Значения больше или меньше увеличивают или уменьшают ограничение скорости.</string> <string name="cpu_accuracy">Точность ЦП</string> + <string name="value_with_units">%1$s%2$s</string> <!-- System settings strings --> <string name="use_docked_mode">Режим док-станции</string> - <string name="use_docked_mode_description">Эмуляция режима док-станции, что увеличивает разрешение за счет снижения производительности.</string> - <string name="emulated_region">Эмулируемый регион</string> - <string name="emulated_language">Эмулируемый язык</string> + <string name="use_docked_mode_description">Увеличивает разрешение, снижая производительность. Портативный режим используется при отключении, снижая разрешение и повышая производительность.</string> + <string name="emulated_region">Регион консоли</string> + <string name="emulated_language">Язык консоли</string> <string name="select_rtc_date">Выберите дату RTC</string> <string name="select_rtc_time">Выберите время RTC</string> - <string name="use_custom_rtc">Включить пользовательский RTC</string> - <string name="use_custom_rtc_description">Этот параметр позволяет установить пользовательские часы реального времени отдельно от текущего системного времени</string> + <string name="use_custom_rtc">Пользовательский RTC</string> + <string name="use_custom_rtc_description">Позволяет установить пользовательские часы реального времени отдельно от текущего системного времени.</string> <string name="set_custom_rtc">Установить пользовательский RTC</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">Уровень точности</string> - <string name="renderer_resolution">Разрешение</string> + <string name="renderer_resolution">Разрешение (портативное/в док-станции)</string> <string name="renderer_vsync">Режим верт. синхронизации</string> + <string name="renderer_screen_layout">Ориентация</string> <string name="renderer_aspect_ratio">Соотношение сторон</string> <string name="renderer_scaling_filter">Фильтр адаптации окна</string> <string name="renderer_anti_aliasing">Метод сглаживания</string> <string name="renderer_force_max_clock">Принудительно заставить максимальную тактовую частоту (только для Adreno)</string> <string name="renderer_force_max_clock_description">Заставляет ГП работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string> <string name="renderer_asynchronous_shaders">Использовать асинхронные шейдеры</string> - <string name="renderer_asynchronous_shaders_description">Компилирует шейдеры асинхронно, что уменьшает зависания, но может взамен предоставить визуальные баги.</string> - <string name="renderer_debug">Включить отладку графики</string> - <string name="renderer_debug_description">Если включено, графический API переходит в более медленный режим отладки</string> - <string name="use_disk_shader_cache">Использовать кэш шейдеров на диске</string> - <string name="use_disk_shader_cache_description">Уменьшение зависаний за счет хранения и загрузки сгенерированных шейдеров на хранилище.</string> + <string name="renderer_asynchronous_shaders_description">Компиляция шейдеров происходит асинхронно, что уменьшает зависания, но может привести к появлению багов.</string> + <string name="renderer_reactive_flushing">Реактивная очистка</string> + <string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string> + <string name="use_disk_shader_cache">Кэш шейдеров на диске</string> + <string name="use_disk_shader_cache_description">Уменьшение зависаний за счет хранения и загрузки сгенерированных шейдеров.</string> + + <!-- Debug settings strings --> + <string name="cpu">ЦП</string> + <string name="cpu_debug_mode">Отладка ЦП</string> + <string name="cpu_debug_mode_description">Переводит ЦП в режим медленной отладки.</string> + <string name="gpu">графический процессор</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">Отладка графики</string> + <string name="renderer_debug_description">Переводит графический API в режим медленной отладки.</string> + <string name="fastmem">Fastmem</string> - <!-- Audio settings strings --> <string name="audio_volume">Громкость</string> <string name="audio_volume_description">Задает громкость аудиовыхода.</string> @@ -157,7 +211,9 @@ <string name="ini_saved">Сохраненные настройки</string> <string name="gameid_saved">Настройки сохранены для %1$s</string> <string name="error_saving">Ошибка сохранения %1$s.ini: %2$s</string> + <string name="unimplemented_menu">Нереализованное меню</string> <string name="loading">Загрузка...</string> + <string name="shutting_down">Выключение…</string> <string name="reset_setting_confirmation">Хотите ли вы вернуть этот параметр к значению по умолчанию?</string> <string name="reset_to_default">Сброс к настройкам по умолчанию</string> <string name="reset_all_settings">Сбросить все настройки?</string> @@ -165,6 +221,14 @@ <string name="settings_reset">Настройки сброшены</string> <string name="close">Закрыть</string> <string name="learn_more">Узнать больше</string> + <string name="auto">Авто</string> + <string name="submit">Отправить</string> + <string name="string_null">Null</string> + <string name="string_import">Импорт</string> + <string name="export">Экспорт</string> + <string name="export_failed">Ошибка экспорта</string> + <string name="import_failed">Ошибка импортирования</string> + <string name="cancelling">Отменяю</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Выбрать драйвер ГП</string> @@ -172,6 +236,7 @@ <string name="select_gpu_driver_install">Установить</string> <string name="select_gpu_driver_default">По умолчанию</string> <string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string> + <string name="select_gpu_driver_error">Выбран неверный драйвер, используется стандартный системный!</string> <string name="system_gpu_driver">Системный драйвер ГП</string> <string name="installing_driver">Установка драйвера...</string> @@ -182,10 +247,11 @@ <string name="preferences_graphics">Графика</string> <string name="preferences_audio">Аудио</string> <string name="preferences_theme">Тема и цвет</string> + <string name="preferences_debug">Отладка</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">Ваш ROM зашифрованный</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[Пожалуйста, следуйте инструкциям, чтобы пере-дампить ваши <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">игровые картриджи</a> или <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">установленные игры</a>.]]></string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Следуйте инструкциям, чтобы пере-дампить игровые картриджи <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\"> или <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\"> установленные игры</a>.]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[Пожалуйста, убедитесь, что ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> установлен, чтобы игры можно было расшифровать.]]></string> <string name="loader_error_video_core">Произошла ошибка при инициализации видеоядра.</string> <string name="loader_error_video_core_description">Обычно это вызвано несовместимым драйвером ГП. Установка пользовательского драйвера ГП может решить эту проблему.</string> @@ -199,17 +265,17 @@ <string name="emulation_toggle_controls">Переключение управления</string> <string name="emulation_rel_stick_center">Относительный центр стика</string> <string name="emulation_dpad_slide">Слайд крестовиной</string> - <string name="emulation_haptics">Тактильная обратная связь</string> + <string name="emulation_haptics">Обратная связь от нажатий</string> <string name="emulation_show_overlay">Показать оверлей</string> <string name="emulation_toggle_all">Переключить всё</string> - <string name="emulation_control_adjust">Настроить оверлей</string> + <string name="emulation_control_adjust">Регулировка оверлея</string> <string name="emulation_control_scale">Масштаб</string> <string name="emulation_control_opacity">Непрозрачность</string> <string name="emulation_touch_overlay_reset">Сбросить оверлей</string> - <string name="emulation_touch_overlay_edit">Изменить оверлей</string> + <string name="emulation_touch_overlay_edit">Редактировать оверлей</string> <string name="emulation_pause">Пауза эмуляции</string> - <string name="emulation_unpause">Возобновление эмуляции</string> - <string name="emulation_input_overlay">Настройки оверлея</string> + <string name="emulation_unpause">Возобновить эмуляцию</string> + <string name="emulation_input_overlay">Настройка оверлея</string> <string name="load_settings">Загрузка настроек...</string> @@ -226,6 +292,9 @@ <string name="fatal_error">Фатальная ошибка</string> <string name="fatal_error_message">Произошла фатальная ошибка. Проверьте журнал для получения подробной информации.\nПродолжение эмуляции может привести к сбоям и ошибкам.</string> <string name="performance_warning">Отключение этой настройки значительно снизит производительность эмуляции! Для достижения наилучших результатов рекомендуется оставить эту настройку включенной.</string> + <string name="device_memory_inadequate">Оперативная память устройства: %1$s\nРекомендовано: %2$s</string> + <string name="memory_formatted">%1$s%2$s</string> + <string name="no_game_present">Загрузочной игры нету!</string> <!-- Region Names --> <string name="region_japan">Япония</string> @@ -236,7 +305,14 @@ <string name="region_korea">Корея</string> <string name="region_taiwan">Тайвань</string> - <!-- Language Names --> + <!-- Memory Sizes --> + <string name="memory_byte">Байт</string> + <string name="memory_kilobyte">КБ</string> + <string name="memory_megabyte">МБ</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">ТБ</string> + <string name="memory_petabyte">ПБ</string> + <string name="memory_exabyte">ЕВ</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> @@ -274,6 +350,11 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <!-- Screen Layouts --> + <string name="screen_layout_landscape">Пейзаж</string> + <string name="screen_layout_portrait">Портрет</string> + <string name="screen_layout_auto">Авто</string> + <!-- Aspect Ratios --> <string name="ratio_default">Стандартное (16:9)</string> <string name="ratio_force_four_three">Заставить 4:3</string> @@ -288,8 +369,8 @@ <!-- Gamepad Buttons --> <string name="gamepad_d_pad">Крестовина</string> - <string name="gamepad_left_stick">Левый мини-джойстик</string> - <string name="gamepad_right_stick">Правый мини-джойстик</string> + <string name="gamepad_left_stick">Левый стик</string> + <string name="gamepad_right_stick">Правый стик</string> <string name="gamepad_home">Home</string> <string name="gamepad_screenshot">Скриншот</string> @@ -298,18 +379,32 @@ <string name="building_shaders">Постройка шейдеров</string> <!-- Theme options --> - <string name="change_app_theme">Изменить тему приложения</string> + <string name="change_app_theme">Сменить тему</string> <string name="theme_default">По умолчанию</string> <string name="theme_material_you">Material You</string> <!-- Theme Modes --> - <string name="change_theme_mode">Изменить режим темы</string> + <string name="change_theme_mode">Сменить режим темы</string> <string name="theme_mode_follow_system">Системная</string> <string name="theme_mode_light">Светлая</string> <string name="theme_mode_dark">Темная</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">Использовать черный фон</string> + <string name="use_black_backgrounds">Чёрный фон</string> <string name="use_black_backgrounds_description">При использовании темной темы применяйте черный фон.</string> -</resources> + <!-- Picture-In-Picture --> + <string name="picture_in_picture">Картинка в картинке</string> + <string name="picture_in_picture_description">Свернуть окно при размещении в фоновом режиме</string> + <string name="pause">Пауза</string> + <string name="play">Играть</string> + <string name="mute">Выключить звук</string> + <string name="unmute">Включить звук</string> + + <!-- Licenses screen strings --> + <string name="licenses">Лицензии</string> + <string name="license_fidelityfx_fsr_description">Высококачественное масштабирование от AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index 5b789ee98..34809dbb8 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <string name="app_disclaimer">Це програмне забезпечення дозволяє запускати ігри для ігрової консолі Nintendo Switch. Ми не надаємо самі ігри або ключі.<br /><br />Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у сховищі пристрою.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Дізнатися більше</a>]]></string> <string name="emulation_notification_channel_name">Емуляція активна</string> @@ -25,7 +25,6 @@ <string name="back">Назад</string> <string name="add_games">Додати ігри</string> <string name="add_games_description">Виберіть папку з іграми</string> - <!-- Home strings --> <string name="home_games">Ігри</string> <string name="home_search">Пошук</string> @@ -61,6 +60,7 @@ <string name="invalid_keys_file">Вибрано неправильний файл ключів</string> <string name="install_keys_success">Ключі успішно встановлено</string> <string name="reading_keys_failure">Помилка під час зчитування ключів шифрування</string> + <string name="install_prod_keys_failure_extension_description">Переконайтеся, що файл ключів має розширення .keys, і повторіть спробу.</string> <string name="invalid_keys_error">Невірні ключі шифрування</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">Обраний файл невірний або пошкоджений. Будь ласка, пере-дампіть ваші ключі.</string> @@ -68,8 +68,6 @@ <string name="install_gpu_driver_description">Встановіть альтернативні драйвери для потенційно кращої продуктивності та/або точності</string> <string name="advanced_settings">Розширені налаштування</string> <string name="settings_description">Налаштування параметрів емулятора</string> - <string name="search_recently_played">Нещодавно зіграно</string> - <string name="search_recently_added">Нещодавно додано</string> <string name="search_retail">Роздрібні</string> <string name="search_homebrew">Homebrew</string> <string name="open_user_folder">Відкрити папку yuzu</string> @@ -86,7 +84,6 @@ <string name="save_file_invalid_zip_structure_description">Назва першої вкладеної папки має бути ідентифікатором гри.</string> <string name="import_saves">Імпорт</string> <string name="export_saves">Експорт</string> - <!-- About screen strings --> <string name="gaia_is_not_real">Gaia не існує</string> <string name="copied_to_clipboard">Скопійовано в буфер обміну</string> @@ -113,42 +110,20 @@ <string name="our_eternal_gratitude">Наша нескінченна вдячність</string> <string name="are_you_interested">Ви зацікавлені?</string> - <!-- General settings strings --> - <string name="frame_limit_enable">Увімкнути обмеження швидкості</string> - <string name="frame_limit_enable_description">Якщо цю функцію ввімкнено, швидкість емуляції буде обмежена зазначеним відсотком від нормальної швидкості.</string> <string name="frame_limit_slider">Обмеження відсотка швидкості</string> - <string name="frame_limit_slider_description">Вказує відсоток для обмеження швидкості емуляції. При значенні за замовчуванням 100% емуляція буде обмежена нормальною швидкістю. Значення вище або нижче збільшуватимуть або зменшуватимуть обмеження швидкості.</string> <string name="cpu_accuracy">Точність ЦП</string> - - <!-- System settings strings --> - <string name="use_docked_mode">Режим док-станції</string> - <string name="use_docked_mode_description">Емуляція режиму док-станції, що збільшує роздільну здатність за рахунок зниження продуктивності.</string> <string name="emulated_region">Емульований регіон</string> <string name="emulated_language">Емульована мова</string> - <string name="select_rtc_date">Оберіть дату RTC</string> - <string name="select_rtc_time">Оберіть час RTC</string> - <string name="use_custom_rtc">Увімкнути користувацький RTC</string> - <string name="use_custom_rtc_description">Цей параметр дає змогу встановити користувацький годинник реального часу окремо від поточного системного часу</string> - <string name="set_custom_rtc">Встановити користувацький RTC</string> - + <string name="use_custom_rtc">Користувацький RTC</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">Рівень точності</string> - <string name="renderer_resolution">Роздільна здатність</string> <string name="renderer_vsync">Режим верт. синхронізації</string> - <string name="renderer_aspect_ratio">Співвідношення сторін</string> - <string name="renderer_scaling_filter">Фільтр адаптації вікна</string> - <string name="renderer_anti_aliasing">Метод згладжування</string> <string name="renderer_force_max_clock">Примусово змусити максимальну тактову частоту (тільки для Adreno)</string> <string name="renderer_force_max_clock_description">Змушує ГП працювати на максимально можливих тактових частотах (теплові обмеження все одно будуть застосовуватися).</string> <string name="renderer_asynchronous_shaders">Використовувати асинхронні шейдери</string> - <string name="renderer_asynchronous_shaders_description">Компілює шейдери асинхронно, що зменшує зависання, але може натомість надати візуальні баги.</string> - <string name="renderer_debug">Увімкнути налагодження графіки</string> - <string name="renderer_debug_description">Якщо увімкнено, графічний API переходить у повільніший режим налагодження</string> - <string name="use_disk_shader_cache">Використовувати кеш шейдерів на диску</string> - <string name="use_disk_shader_cache_description">Зменшення зависань завдяки зберіганню та завантаженню згенерованих шейдерів на сховище.</string> - - <!-- Audio settings strings --> + <!-- Debug settings strings --> + <string name="cpu">ЦП</string> + <string name="renderer_api">API</string> <string name="audio_volume">Гучність</string> <string name="audio_volume_description">Вказує гучність аудіовиходу.</string> @@ -161,17 +136,20 @@ <string name="reset_setting_confirmation">Чи хочете ви повернути цей параметр до значення за замовчуванням?</string> <string name="reset_to_default">Скидання до налаштувань за замовчуванням</string> <string name="reset_all_settings">Скинути всі налаштування</string> - <string name="reset_all_settings_description">Усі додаткові налаштування буде скинуто до налаштування за замовчуванням. Це неможливо скасувати.</string> <string name="settings_reset">Налаштування скинуто</string> <string name="close">Закрити</string> <string name="learn_more">Дізнатися більше</string> - + <string name="auto">Авто</string> + <string name="string_null">Null</string> + <string name="string_import">Імпорт</string> + <string name="export">Експорт</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Вибрати драйвер ГП</string> <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string> <string name="select_gpu_driver_install">Встановити</string> <string name="select_gpu_driver_default">За замовчуванням</string> <string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string> + <string name="select_gpu_driver_error">Обрано неправильний драйвер, використовується стандартний системний!</string> <string name="system_gpu_driver">Системний драйвер ГП</string> <string name="installing_driver">Встановлення драйвера...</string> @@ -182,40 +160,19 @@ <string name="preferences_graphics">Графіка</string> <string name="preferences_audio">Аудіо</string> <string name="preferences_theme">Тема і колір</string> + <string name="preferences_debug">Налагодження</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">Ваш ROM зашифрований</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[Будь ласка, дотримуйтесь інструкцій, щоб пере-дампити ваші <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">ігрові картриджі</a> або <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">встановлені ігри</a>.]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[Будь ласка, переконайтеся, що ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> встановлено, щоб ігри можна було розшифрувати.]]></string> <string name="loader_error_video_core">Сталася помилка під час ініціалізації відеоядра.</string> <string name="loader_error_video_core_description">Зазвичай це спричинено несумісним драйвером ГП. Встановлення користувацького драйвера ГП може вирішити цю проблему.</string> <string name="loader_error_invalid_format">Не вдалося запустити ROM</string> <string name="loader_error_file_not_found">Файл ROM не існує</string> - <!-- Emulation Menu --> - <string name="emulation_exit">Вихід з емуляції</string> <string name="emulation_done">Готово</string> - <string name="emulation_fps_counter">Лічильник FPS</string> - <string name="emulation_toggle_controls">Перемикання керування</string> - <string name="emulation_rel_stick_center">Відносний центр стіка</string> - <string name="emulation_dpad_slide">Слайд хрестовиною</string> - <string name="emulation_haptics">Тактильний зворотний зв\'язок</string> - <string name="emulation_show_overlay">Показати оверлей</string> - <string name="emulation_toggle_all">Перемкнути все</string> - <string name="emulation_control_adjust">Налаштувати оверлей</string> <string name="emulation_control_scale">Масштаб</string> <string name="emulation_control_opacity">Непрозорість</string> - <string name="emulation_touch_overlay_reset">Скинути оверлей</string> - <string name="emulation_touch_overlay_edit">Змінити оверлей</string> - <string name="emulation_pause">Пауза емуляції</string> - <string name="emulation_unpause">Відновлення емуляції</string> - <string name="emulation_input_overlay">Налаштування оверлея</string> - - <string name="load_settings">Завантаження налаштувань...</string> - - <!-- Software keyboard --> - <string name="software_keyboard">Віртуальна клавіатура</string> - <!-- Errors and warnings --> <string name="abort_button">Перервати</string> <string name="continue_button">Продовжити</string> @@ -226,7 +183,6 @@ <string name="fatal_error">Фатальна помилка</string> <string name="fatal_error_message">Сталася фатальна помилка. Перевірте журнал для отримання докладної інформації.\nПродовження емуляції може призвести до збоїв і помилок.</string> <string name="performance_warning">Вимкнення цього налаштування значно знизить продуктивність емуляції! Для досягнення найкращих результатів рекомендується залишити це налаштування увімкненим.</string> - <!-- Region Names --> <string name="region_japan">Японія</string> <string name="region_usa">США</string> @@ -236,8 +192,7 @@ <string name="region_korea">Корея</string> <string name="region_taiwan">Тайвань</string> - <!-- Language Names --> - + <string name="memory_gigabyte">GB</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> <string name="renderer_none">Вимкнено</string> @@ -274,22 +229,18 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <string name="screen_layout_auto">Авто</string> + <!-- Aspect Ratios --> <string name="ratio_default">За замовчуванням (16:9)</string> <string name="ratio_force_four_three">Змусити 4:3</string> <string name="ratio_force_twenty_one_nine">Змусити 21:9</string> <string name="ratio_force_sixteen_ten">Змусити 16:10</string> - <string name="ratio_stretch">Розтягнути до вікна</string> - <!-- CPU Accuracy --> <string name="cpu_accuracy_accurate">Точно</string> <string name="cpu_accuracy_unsafe">Небезпечно</string> <string name="cpu_accuracy_paranoid">Параноїк (повільно)</string> - <!-- Gamepad Buttons --> - <string name="gamepad_d_pad">Кнопки напрямків</string> - <string name="gamepad_left_stick">Лівий міні-джойстик</string> - <string name="gamepad_right_stick">Правий міні-джойстик</string> <string name="gamepad_home">Home</string> <string name="gamepad_screenshot">Знімок екрану</string> @@ -297,19 +248,16 @@ <string name="preparing_shaders">Підготовка шейдерів</string> <string name="building_shaders">Побудова шейдерів</string> - <!-- Theme options --> - <string name="change_app_theme">Змінити тему застосунку</string> <string name="theme_default">За замовчуванням</string> <string name="theme_material_you">Material You</string> - <!-- Theme Modes --> - <string name="change_theme_mode">Змінити режим теми</string> <string name="theme_mode_follow_system">Системна</string> <string name="theme_mode_light">Світла</string> <string name="theme_mode_dark">Темна</string> - <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">Використовувати чорне тло</string> <string name="use_black_backgrounds_description">У разі використання темної теми застосовуйте чорне тло.</string> -</resources> + <string name="mute">Вимкнути звук</string> + <string name="unmute">Увімкнути звук</string> + + </resources> diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml new file mode 100644 index 000000000..f977db3a2 --- /dev/null +++ b/src/android/app/src/main/res/values-vi/strings.xml @@ -0,0 +1,340 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + + <string name="app_disclaimer">Phần mềm này sẽ chạy các game cho máy chơi game Nintendo Switch. Không có title games hoặc keys được bao gồm.<br /><br />Trước khi bạn bắt đầu, hãy tìm tập tin <![CDATA[<b> prod.keys </b>]]> trên bộ nhớ thiết bị của bạn.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tìm hiểu thêm</a>]]></string> + <string name="emulation_notification_channel_name">Giả lập đang chạy</string> + <string name="emulation_notification_channel_description">Hiển thị thông báo liên tục khi giả lập đang chạy.</string> + <string name="emulation_notification_running">yuzu đang chạy</string> + <string name="notice_notification_channel_name">Thông báo và lỗi</string> + <string name="notice_notification_channel_description">Hiển thị thông báo khi có sự cố xảy ra.</string> + <string name="notification_permission_not_granted">Ứng dụng không được cấp quyền thông báo!</string> + + <!-- Setup strings --> + <string name="welcome">Chào mừng!</string> + <string name="welcome_description">Tìm hiểu cách cài đặt <b>yuzu</b> và bắt đầu giả lập.</string> + <string name="get_started">Bắt đầu</string> + <string name="keys">Keys</string> + <string name="keys_description">Chọn tệp <b>prod.keys</b> của bạn bằng nút bên dưới.</string> + <string name="select_keys">Chọn Keys</string> + <string name="games">Game</string> + <string name="games_description">Chọn thư mục <b>Game</b> của bạn bằng nút bên dưới.</string> + <string name="done">Hoàn thành</string> + <string name="done_description">Tất cả đã hoàn tất.\nHãy tận hưởng các game của bạn!</string> + <string name="text_continue">Tiếp tục</string> + <string name="next">Tiếp theo</string> + <string name="back">Trở lại</string> + <string name="add_games">Thêm Game</string> + <string name="add_games_description">Chọn thư mục game của bạn</string> + <!-- Home strings --> + <string name="home_games">Game</string> + <string name="home_search">Tìm kiếm</string> + <string name="home_settings">Cài đặt</string> + <string name="empty_gamelist">Không tìm thấy tập tin hoặc chưa có thư mục game nào được chọn.</string> + <string name="search_and_filter_games">Tìm và lọc game</string> + <string name="select_games_folder">Chọn thư mục game</string> + <string name="select_games_folder_description">Cho phép yuzu thêm vào danh sách game</string> + <string name="add_games_warning">Bỏ qua việc lựa chọn thư mục game?</string> + <string name="add_games_warning_description">Game sẽ không hiển thị trong danh sách nếu một thư mục không được chọn.</string> + <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> + <string name="home_search_games">Tìm kiếm game</string> + <string name="games_dir_selected">Thư mục game đã được chọn</string> + <string name="install_prod_keys">Cài đặt prod.keys</string> + <string name="install_prod_keys_description">Yêu cầu để giải mã các game bán lẻ</string> + <string name="install_prod_keys_warning">Bỏ qua việc thêm keys?</string> + <string name="install_prod_keys_warning_description">Cần có keys hợp lệ để giả lập các game bán lẻ. Chỉ có các ứng dụng homebrew có thể vận hành nếu bạn tiếp tục.</string> + <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> + <string name="notifications">Thông báo</string> + <string name="notifications_description">Cấp quyền thông báo bằng nút bên dưới.</string> + <string name="give_permission">Cấp quyền</string> + <string name="notification_warning">Bỏ qua việc cấp quyền thông báo?</string> + <string name="notification_warning_description">yuzu sẽ không thể gửi những thông báo quan trọng đến bạn.</string> + <string name="permission_denied">Đã từ chối cấp quyền</string> + <string name="permission_denied_description">Bạn từ chối cấp quyền này quá nhiều lần và giờ bạn phải cấp quyền thủ công trong cài đặt máy.</string> + <string name="about">Thông tin</string> + <string name="about_description">Phiên bản, đóng góp và những thứ khác</string> + <string name="warning_help">Trợ giúp</string> + <string name="warning_skip">Bỏ qua</string> + <string name="warning_cancel">Hủy bỏ</string> + <string name="install_amiibo_keys">Cài đặt keys Amiibo</string> + <string name="install_amiibo_keys_description">Cần thiết để dùng Amiibo trong game</string> + <string name="invalid_keys_file">Tệp keys không hợp lệ đã được chọn</string> + <string name="install_keys_success">Cài đặt keys thành công</string> + <string name="reading_keys_failure">Lỗi đọc keys mã hóa</string> + <string name="install_prod_keys_failure_extension_description">Xác minh rằng tệp keys của bạn có đuôi .keys và thử lại.</string> + <string name="install_amiibo_keys_failure_extension_description">Xác minh rằng tệp keys của bạn có đuôi .bin và thử lại.</string> + <string name="invalid_keys_error">Keys mã hoá không hợp lệ</string> + <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> + <string name="install_keys_failure_description">Tệp đã chọn sai hoặc hỏng. Vui lòng trích xuất lại keys của bạn.</string> + <string name="install_gpu_driver">Cài đặt driver GPU</string> + <string name="install_gpu_driver_description">Cài đặt driver thay thế để có thể có hiệu suất tốt và chính xác hơn</string> + <string name="advanced_settings">Cài đặt nâng cao</string> + <string name="settings_description">Cấu hình cài đặt giả lập</string> + <string name="search_recently_played">Đã chơi gần đây</string> + <string name="search_recently_added">Đã thêm gần đây</string> + <string name="search_retail">Bán lẻ</string> + <string name="search_homebrew">Homebrew</string> + <string name="open_user_folder">Mở thư mục yuzu</string> + <string name="open_user_folder_description">Quản lý tệp nội bộ của yuzu</string> + <string name="theme_and_color_description">Thay đổi giao diện ứng dụng</string> + <string name="no_file_manager">Không tìm thấy trình quản lý tập tin</string> + <string name="notification_no_directory_link">Không thể mở thư mục yuzu</string> + <string name="notification_no_directory_link_description">Vui lòng xác định thư mục người dùng với bảng điều khiển bên của trình quản lý tệp thủ công.</string> + <string name="manage_save_data">Quản lý dữ liệu save</string> + <string name="manage_save_data_description">Đã tìm thấy dữ liệu save. Vui lòng chọn một tuỳ chọn bên dưới.</string> + <string name="import_export_saves_description">Nhập hoặc xuất tệp save</string> + <string name="save_file_imported_success">Nhập thành công</string> + <string name="save_file_invalid_zip_structure">Cấu trúc thư mục save không hợp lệ</string> + <string name="save_file_invalid_zip_structure_description">Tên thư mục con đầu tiên phải là ID title của game.</string> + <string name="import_saves">Nhập</string> + <string name="export_saves">Xuất</string> + <string name="install_firmware">Cài đặt firmware</string> + <string name="install_firmware_description">Firmware phải được đặt trong một tập tin nén ZIP và cần thiết để khởi chạy một số game</string> + <string name="firmware_installing">Đang cài đặt firmware</string> + <string name="firmware_installed_success">Cài đặt firmware thành công</string> + <string name="firmware_installed_failure">Cài đặt firmware thất bại</string> + <string name="share_log">Chia sẻ nhật ký gỡ lỗi</string> + <string name="share_log_description">Chia sẻ tập tin nhật ký của yuzu để gỡ lỗi vấn đề</string> + <string name="share_log_missing">Không tìm thấy tập tin nhật ký</string> + <string name="install_game_content">Cài đặt nội dung game</string> + <string name="install_game_content_description">Cài đặt cập nhật game hoặc DLC</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <!-- About screen strings --> + <string name="gaia_is_not_real">Gaia không có thật</string> + <string name="copied_to_clipboard">Đã sao chép vào bộ nhớ tạm</string> + <string name="about_app_description">Một giả lập Switch mã nguồn mở</string> + <string name="contributors">Người đóng góp</string> + <string name="contributors_description">Được làm với \u2764 từ nhóm yuzu</string> + <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Các dự án làm cho yuzu trên Android trở thành điều có thể</string> + <string name="build">Dựng</string> + <string name="support_link">https://discord.gg/u77vRWY</string> + <string name="website_link">https://yuzu-emu.org/</string> + <string name="github_link">https://github.com/yuzu-emu</string> + + <!-- Early access upgrade strings --> + <string name="early_access">Early Access</string> + <string name="get_early_access">Tải Early Access</string> + <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> + <string name="get_early_access_description">Các tính năng tiên tiến, truy cập sớm các bản cập nhật và nhiều hơn nữa</string> + <string name="early_access_benefits">Lợi ích của Early Access</string> + <string name="cutting_edge_features">Tính năng tiên tiến</string> + <string name="early_access_updates">Truy cập sớm các bản cập nhật</string> + <string name="no_manual_installation">Không có cài đặt thủ công</string> + <string name="prioritized_support">Ưu tiên hỗ trợ</string> + <string name="helping_game_preservation">Hỗ trợ bảo tồn game</string> + <string name="our_eternal_gratitude">Sự biết ơn vô hạn của chúng tôi</string> + <string name="are_you_interested">Bạn có thấy hứng thú không?</string> + + <!-- General settings strings --> + <string name="frame_limit_enable">Giới hạn tốc độ</string> + <string name="frame_limit_enable_description">Giới hạn tốc độ giả lập ở một phần trăm cụ thể của tốc độ bình thường.</string> + <string name="frame_limit_slider">Giới hạn phần trăm tốc độ</string> + <string name="frame_limit_slider_description">Xác định phần trăm để giới hạn tốc độ giả lập. 100% là tốc độ bình thường. Giá trị cao hơn hoặc thấp hơn sẽ tăng hoặc giảm giới hạn tốc độ.</string> + <string name="cpu_accuracy">Độ chính xác CPU</string> + <!-- System settings strings --> + <string name="use_docked_mode">Chế độ docked</string> + <string name="use_docked_mode_description">Tăng độ phân giải, giảm hiệu suất. Chế độ handheld được sử dụng khi tắt, giảm độ phân giải và tăng hiệu suất.</string> + <string name="emulated_region">Khu vực giả lập</string> + <string name="emulated_language">Ngôn ngữ giả lập</string> + <string name="select_rtc_date">Chọn ngày RTC</string> + <string name="select_rtc_time">Chọn giờ RTC</string> + <string name="use_custom_rtc">RTC tuỳ chỉnh</string> + <string name="use_custom_rtc_description">Cho phép bạn thiết lập một đồng hồ thời gian thực tùy chỉnh riêng biệt so với thời gian hệ thống hiện tại.</string> + <string name="set_custom_rtc">Thiết lập RTC tùy chỉnh</string> + + <!-- Graphics settings strings --> + <string name="renderer_accuracy">Mức độ chính xác</string> + <string name="renderer_resolution">Độ phân giải (Handheld/Docked)</string> + <string name="renderer_vsync">Chế độ VSync</string> + <string name="renderer_aspect_ratio">Tỉ lệ khung hình</string> + <string name="renderer_scaling_filter">Bộ lọc điều chỉnh cửa sổ</string> + <string name="renderer_anti_aliasing">Phương pháp khử răng cưa</string> + <string name="renderer_force_max_clock">Buộc chạy ở xung nhịp tối đa (chỉ cho Adreno)</string> + <string name="renderer_force_max_clock_description">Buộc GPU hoạt động ở xung nhịp tối đa có thể (ràng buộc nhiệt độ vẫn sẽ được áp dụng).</string> + <string name="renderer_asynchronous_shaders">Dùng các shader bất đồng bộ</string> + <string name="renderer_asynchronous_shaders_description">Biên dịch các shader bất đồng bộ, giảm tình trạng giật lag nhưng có thể gây ra các lỗi.</string> + <string name="renderer_reactive_flushing">Dùng xả tương ứng</string> + <string name="renderer_reactive_flushing_description">Cải thiện độ chính xác kết xuất trong một số game nhưng đồng thời giảm hiệu suất.</string> + <string name="use_disk_shader_cache">Lưu bộ nhớ đệm shader trên ổ cứng</string> + <string name="use_disk_shader_cache_description">Giảm tình trạng giật lag bằng cách lưu trữ và tải các shader được tạo ra nội bộ.</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">Gỡ lỗi đồ hoạ</string> + <string name="renderer_debug_description">Đặt API đồ họa vào chế độ gỡ lỗi chậm.</string> + <string name="audio_volume">Âm lượng</string> + <string name="audio_volume_description">Xác định âm lượng của đầu ra âm thanh.</string> + + <!-- Miscellaneous --> + <string name="slider_default">Mặc định</string> + <string name="ini_saved">Cài đặt đã lưu</string> + <string name="gameid_saved">Cài đặt đã lưu cho %1$s</string> + <string name="error_saving">Lỗi khi lưu %1$s.ini: %2$s</string> + <string name="loading">Đang tải...</string> + <string name="reset_setting_confirmation">Bạn có muốn đặt lại cài đặt này về giá trị mặc định không?</string> + <string name="reset_to_default">Đặt lại về mặc định</string> + <string name="reset_all_settings">Bạn có muốn đặt lại tất cả các cài đặt về giá trị mặc định không?</string> + <string name="reset_all_settings_description">Tất cả các cài đặt nâng cao sẽ được đặt lại về cấu hình mặc định. Điều này không thể hoàn tác.</string> + <string name="settings_reset">Cài đặt đã được đặt lại</string> + <string name="close">Đóng</string> + <string name="learn_more">Tìm hiểu thêm</string> + <string name="auto">Tự động</string> + <string name="submit">Gửi</string> + <string name="string_null">Null</string> + <string name="string_import">Nhập</string> + <string name="export">Xuất</string> + <!-- GPU driver installation --> + <string name="select_gpu_driver">Chọn driver GPU</string> + <string name="select_gpu_driver_title">Bạn có muốn thay thế driver GPU hiện tại không?</string> + <string name="select_gpu_driver_install">Cài đặt</string> + <string name="select_gpu_driver_default">Mặc định</string> + <string name="select_gpu_driver_use_default">Dùng driver GPU mặc định</string> + <string name="select_gpu_driver_error">Driver không hợp lệ đã được chọn, dùng mặc định hệ thống!</string> + <string name="system_gpu_driver">Driver GPU hệ thống</string> + <string name="installing_driver">Đang cài đặt driver...</string> + + <!-- Preferences Screen --> + <string name="preferences_settings">Cài đặt</string> + <string name="preferences_general">Chung</string> + <string name="preferences_system">Hệ thống</string> + <string name="preferences_graphics">Đồ hoạ</string> + <string name="preferences_audio">Âm thanh</string> + <string name="preferences_theme">Chủ đề và màu sắc</string> + <string name="preferences_debug">Gỡ lỗi</string> + + <!-- ROM loading errors --> + <string name="loader_error_encrypted">ROM của bạn đã bị mã hoá</string> + <string name="loader_error_encrypted_keys_description"><![CDATA[Vui lòng đảm bảo tệp <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> đã được cài đặt để các game có thể được giải mã.]]></string> + <string name="loader_error_video_core">Đã xảy ra lỗi khi khởi tạo lõi video</string> + <string name="loader_error_video_core_description">Việc này thường do driver GPU không tương thích. Cài đặt một driver GPU tùy chỉnh có thể giải quyết vấn đề này.</string> + <string name="loader_error_invalid_format">Không thể nạp ROM</string> + <string name="loader_error_file_not_found">Tệp ROM không tồn tại</string> + + <!-- Emulation Menu --> + <string name="emulation_exit">Thoát giả lập</string> + <string name="emulation_done">Hoàn thành</string> + <string name="emulation_fps_counter">Bộ đếm FPS</string> + <string name="emulation_toggle_controls">Chuyển đổi điều khiển</string> + <string name="emulation_rel_stick_center">Trung tâm nút cần xoay tương đối</string> + <string name="emulation_dpad_slide">Trượt D-pad</string> + <string name="emulation_haptics">Chạm haptics</string> + <string name="emulation_show_overlay">Hiện lớp phủ</string> + <string name="emulation_toggle_all">Chuyển đổi tất cả</string> + <string name="emulation_control_adjust">Điều chỉnh lớp phủ</string> + <string name="emulation_control_scale">Tỉ lệ thu phóng</string> + <string name="emulation_control_opacity">Độ mờ</string> + <string name="emulation_touch_overlay_reset">Đặt lại lớp phủ</string> + <string name="emulation_touch_overlay_edit">Chỉnh sửa lớp phủ</string> + <string name="emulation_pause">Tạm đừng giả lập</string> + <string name="emulation_unpause">Tiếp tục giả lập</string> + <string name="emulation_input_overlay">Tuỳ chọn lớp phủ</string> + + <string name="load_settings">Đang tải cài đặt...</string> + + <!-- Software keyboard --> + <string name="software_keyboard">Bàn phím mềm</string> + + <!-- Errors and warnings --> + <string name="abort_button">Hủy bỏ</string> + <string name="continue_button">Tiếp tục</string> + <string name="system_archive_not_found">Không tìm thấy bản lưu trữ của hệ thống</string> + <string name="system_archive_not_found_message">%s bị thiếu. Vui lòng trích xuất các bản lưu trữ hệ thống của bạn.\nNếu chạy tiếp giả lập có thể bị crash và lỗi.</string> + <string name="system_archive_general">Một bản lưu trữ của hệ thống</string> + <string name="save_load_error">Lỗi Lưu/Tải</string> + <string name="fatal_error">Lỗi nghiêm trọng</string> + <string name="fatal_error_message">Đã xảy ra lỗi nghiêm trọng. Kiểm tra nhật ký để biết thêm chi tiết.\nNếu chạy tiếp giả lập có thể bị crash và lỗi.</string> + <string name="performance_warning">Tắt cài đặt này sẽ làm giảm đáng kể hiệu suất giả lập! Để có trải nghiệm tốt nhất, bạn nên bật cài đặt này.</string> + <!-- Region Names --> + <string name="region_japan">Nhật Bản</string> + <string name="region_usa">Hoa Kỳ</string> + <string name="region_europe">Châu Âu</string> + <string name="region_australia">Úc</string> + <string name="region_china">Trung Quốc</string> + <string name="region_korea">Hàn Quốc</string> + <string name="region_taiwan">Đài Loan</string> + + <string name="memory_gigabyte">GB</string> + <!-- Renderer APIs --> + <string name="renderer_vulkan">Vulkan</string> + <string name="renderer_none">Không có</string> + + <!-- Renderer Accuracy --> + <string name="renderer_accuracy_normal">Bình thường</string> + <string name="renderer_accuracy_high">Cao</string> + <string name="renderer_accuracy_extreme">Cực đại (Chậm)</string> + + <!-- Resolutions --> + <string name="resolution_half">0.5X (360p/540p)</string> + <string name="resolution_three_quarter">0.75X (540p/810p)</string> + <string name="resolution_one">1X (720p/1080p)</string> + <string name="resolution_two">2X (1440p/2160p) (Chậm)</string> + <string name="resolution_three">3X (2160p/3240p) (Chậm)</string> + <string name="resolution_four">4X (2880p/4320p) (Chậm)</string> + + <!-- Renderer VSync --> + <string name="renderer_vsync_immediate">Immediate (Tắt)</string> + <string name="renderer_vsync_mailbox">Mailbox</string> + <string name="renderer_vsync_fifo">FIFO (Bật)</string> + <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> + + <!-- Scaling Filters --> + <string name="scaling_filter_nearest_neighbor">Nearest Neighbor</string> + <string name="scaling_filter_bilinear">Bilinear</string> + <string name="scaling_filter_bicubic">Bicubic</string> + <string name="scaling_filter_gaussian">Gaussian</string> + <string name="scaling_filter_scale_force">ScaleForce</string> + <string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string> + + <!-- Anti-Aliasing --> + <string name="anti_aliasing_none">Không có</string> + <string name="anti_aliasing_fxaa">FXAA</string> + <string name="anti_aliasing_smaa">SMAA</string> + + <string name="screen_layout_auto">Tự động</string> + + <!-- Aspect Ratios --> + <string name="ratio_default">Mặc định (16:9)</string> + <string name="ratio_force_four_three">Dùng 4:3</string> + <string name="ratio_force_twenty_one_nine">Dùng 21:9</string> + <string name="ratio_force_sixteen_ten">Dùng 16:10</string> + <string name="ratio_stretch">Mở rộng đến cửa sổ</string> + + <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">Chính xác</string> + <string name="cpu_accuracy_unsafe">Không an toàn</string> + <string name="cpu_accuracy_paranoid">Paranoid (Chậm)</string> + + <!-- Gamepad Buttons --> + <string name="gamepad_d_pad">D-pad</string> + <string name="gamepad_left_stick">Cần trái</string> + <string name="gamepad_right_stick">Cần phải</string> + <string name="gamepad_home">Home</string> + <string name="gamepad_screenshot">Ảnh chụp màn hình</string> + + <!-- Disk shader cache --> + <string name="preparing_shaders">Đang chuẩn bị shader</string> + <string name="building_shaders">Đang đựng shader</string> + + <!-- Theme options --> + <string name="change_app_theme">Thay đổi chủ đề ứng dụng</string> + <string name="theme_default">Mặc định</string> + <string name="theme_material_you">Material You</string> + + <!-- Theme Modes --> + <string name="change_theme_mode">Thay đổi chủ đề</string> + <string name="theme_mode_follow_system">Theo hệ thống</string> + <string name="theme_mode_light">Sáng</string> + <string name="theme_mode_dark">Tối</string> + + <!-- Black backgrounds theme --> + <string name="use_black_backgrounds">Nền đen</string> + <string name="use_black_backgrounds_description">Khi sử dụng chủ đề tối, hãy áp dụng nền đen.</string> + + <string name="mute">Tắt tiếng</string> + <string name="unmute">Bật tiếng</string> + + <!-- Licenses screen strings --> + <string name="licenses">Giấy phép</string> + <string name="license_fidelityfx_fsr_description">Upscaling chất lượng cao từ AMD</string> + </resources> diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index c0e885751..13455564f 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <string name="app_disclaimer">此软件可以运行 Nintendo Switch 游戏,但不包含任何游戏和密钥文件。<br /><br />在开始前,请找到放置于设备存储中的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string> <string name="emulation_notification_channel_name">正在进行模拟</string> @@ -17,7 +17,7 @@ <string name="keys_description">使用下方的按钮来选择你的 <b>prod.keys</b> 文件。</string> <string name="select_keys">选择密钥文件</string> <string name="games">游戏</string> - <string name="games_description">使用下方的按钮选择你的 <b>游戏</b> 文件夹。</string> + <string name="games_description">使用下方的按钮选择你的<b>游戏</b>文件夹。</string> <string name="done">完成</string> <string name="done_description">你完成了全部设置。\n玩的开心!</string> <string name="text_continue">继续</string> @@ -25,6 +25,7 @@ <string name="back">上一步</string> <string name="add_games">添加游戏</string> <string name="add_games_description">选择你的游戏文件夹</string> + <string name="step_complete">完成!</string> <!-- Home strings --> <string name="home_games">游戏</string> @@ -38,6 +39,7 @@ <string name="add_games_warning_description">如果未选择游戏文件夹,游戏将不会显示在游戏列表中。</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> <string name="home_search_games">搜索游戏</string> + <string name="search_settings">搜索设置</string> <string name="games_dir_selected">已选择游戏文件夹</string> <string name="install_prod_keys">安装 prod.keys 文件</string> <string name="install_prod_keys_description">需要密钥文件来解密游戏</string> @@ -61,12 +63,15 @@ <string name="invalid_keys_file">选择的密钥文件无效</string> <string name="install_keys_success">密钥文件已成功安装</string> <string name="reading_keys_failure">读取加密密钥时出错</string> + <string name="install_prod_keys_failure_extension_description">请确保您的密钥文件扩展名为 .keys 并重试。</string> + <string name="install_amiibo_keys_failure_extension_description">请确保您的密钥文件扩展名为 .bin 并重试。</string> <string name="invalid_keys_error">无效的加密密钥</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">选择的密钥文件不正确或已损坏。请重新转储密钥文件。</string> <string name="install_gpu_driver">安装 GPU 驱动</string> <string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string> <string name="advanced_settings">高级选项</string> + <string name="advanced_settings_game">高级选项: %1$s</string> <string name="settings_description">更改模拟器设置</string> <string name="search_recently_played">最近游玩</string> <string name="search_recently_added">最近添加</string> @@ -86,6 +91,33 @@ <string name="save_file_invalid_zip_structure_description">第一个子文件夹名称必须为当前游戏的 ID。</string> <string name="import_saves">导入</string> <string name="export_saves">导出</string> + <string name="install_firmware">安装固件</string> + <string name="install_firmware_description">固件文件必须为 zip 格式,启动某些游戏时必需</string> + <string name="firmware_installing">正在安装固件</string> + <string name="firmware_installed_success">固件已成功安装</string> + <string name="firmware_installed_failure">固件安装失败</string> + <string name="firmware_installed_failure_description">请确保固件 nca 文件位于 zip 压缩包的根目录,然后重试。</string> + <string name="share_log">分享调试日志</string> + <string name="share_log_description">分享 yuzu 日志文件以便调试</string> + <string name="share_log_missing">未找到日志文件</string> + <string name="install_game_content">安装游戏附加内容</string> + <string name="install_game_content_description">安装游戏更新及 DLC</string> + <string name="installing_game_content">安装中...</string> + <string name="install_game_content_failure">向 NAND 安装文件时失败</string> + <string name="install_game_content_failure_description">请确保附加内容的有效性,并且 prod.keys 密钥文件已安装。</string> + <string name="install_game_content_failure_base">为避免产生冲突,此功能不能用于安装游戏本体。</string> + <string name="install_game_content_failure_file_extension">只有 NSP 或 XCI 格式的附加内容可以安装。请确保您的游戏附加内容是有效的。</string> + <string name="install_game_content_failed_count">%1$d 安装出错</string> + <string name="install_game_content_success">游戏附加内容已成功安装</string> + <string name="install_game_content_success_install">%1$d 安装成功</string> + <string name="install_game_content_success_overwrite">%1$d 覆盖安装成功</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">不支持自定义驱动</string> + <string name="custom_driver_not_supported_description">此设备不支持自定义驱动。\n请之后再访问此项,查看是否已为此设备添加支持。</string> + <string name="manage_yuzu_data">管理 yuzu 数据</string> + <string name="manage_yuzu_data_description">导入/导出固件、密钥、用户数据及其他。</string> + <string name="share_save_file">分享存档文件</string> + <string name="export_save_failed">导出存档文件失败</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia 不真实</string> @@ -94,14 +126,25 @@ <string name="contributors">贡献者</string> <string name="contributors_description">使用来自 yuzu 团队的 \u2764 制作</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">Android 版 yuzu 离不开这些项目的支持</string> <string name="build">构建版本</string> + <string name="user_data">用户数据</string> + <string name="user_data_description">导入/导出应用程序所有数据。\n\n导入用户数据时,将删除当前所有的用户数据!</string> + <string name="exporting_user_data">正在导出用户数据...</string> + <string name="importing_user_data">正在导入用户数据...</string> + <string name="import_user_data">导入用户数据</string> + <string name="invalid_yuzu_backup">无效的 yuzu 备份</string> + <string name="user_data_export_success">导出用户数据成功</string> + <string name="user_data_import_success">导入用户数据成功</string> + <string name="user_data_export_cancelled">已取消导出数据</string> + <string name="user_data_import_failed_description">请确保用户数据文件夹位于 zip 压缩包的根目录,并在 config/config.ini 路径中包含配置文件,然后重试。</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> <!-- Early access upgrade strings --> <string name="early_access">抢先体验</string> - <string name="get_early_access">取得抢先体验</string> + <string name="get_early_access">获取抢先体验!</string> <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> <string name="get_early_access_description">最新的功能、抢先更新、以及更多</string> <string name="early_access_benefits">抢先体验的权益</string> @@ -109,33 +152,34 @@ <string name="early_access_updates">抢先更新</string> <string name="no_manual_installation">无需手动安装</string> <string name="prioritized_support">优先支持</string> - <string name="helping_game_preservation">帮助保留游戏</string> + <string name="helping_game_preservation">帮助保留游玩历史</string> <string name="our_eternal_gratitude">我们真诚的感激</string> <string name="are_you_interested">您对此感兴趣吗?</string> <!-- General settings strings --> - <string name="frame_limit_enable">启用运行速度限制</string> - <string name="frame_limit_enable_description">启用后,模拟速度将限制在正常运行速度的指定百分比。</string> + <string name="frame_limit_enable">运行速度限制</string> + <string name="frame_limit_enable_description">将运行速度限制为正常速度的指定百分比。</string> <string name="frame_limit_slider">限制速度百分比</string> - <string name="frame_limit_slider_description">指定限制模拟速度的百分比。预设为 100%,此时模拟速度将被限制为标准速度。更高或更低的值将增加或降低速度限制上限。</string> + <string name="frame_limit_slider_description">指定限制运行速度的百分比。100% 为正常速度。更高或更低的值将增加或降低速度限制上限。</string> <string name="cpu_accuracy">CPU 精度</string> + <string name="value_with_units">%1$s%2$s</string> <!-- System settings strings --> <string name="use_docked_mode">主机模式</string> - <string name="use_docked_mode_description">以主机模式进行模拟,牺牲性能并提高画面分辨率。</string> + <string name="use_docked_mode_description">提高分辨率,但降低性能。禁用此项时使用掌机模式,降低分辨率并提高性能。</string> <string name="emulated_region">模拟区域</string> <string name="emulated_language">模拟语言</string> <string name="select_rtc_date">选择日期</string> <string name="select_rtc_time">选择时间</string> - <string name="use_custom_rtc">启用自定义系统时钟</string> - <string name="use_custom_rtc_description">此选项允许您设置与目前系统时间相独立的自定义系统时钟</string> - <string name="set_custom_rtc">设置自定义系统时钟</string> + <string name="use_custom_rtc">自定义系统时间</string> + <string name="use_custom_rtc_description">此选项允许您设置与目前系统时间相独立的自定义系统时钟。</string> + <string name="set_custom_rtc">设置自定义系统时间</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">精度等级</string> - <string name="renderer_resolution">分辨率</string> + <string name="renderer_resolution">分辨率 (掌机模式/主机模式)</string> <string name="renderer_vsync">垂直同步模式</string> + <string name="renderer_screen_layout">屏幕方向</string> <string name="renderer_aspect_ratio">屏幕纵横比</string> <string name="renderer_scaling_filter">窗口滤镜</string> <string name="renderer_anti_aliasing">抗锯齿方式</string> @@ -143,12 +187,23 @@ <string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (仍被温控限制)。</string> <string name="renderer_asynchronous_shaders">使用异步着色器</string> <string name="renderer_asynchronous_shaders_description">异步编译着色器,减少卡顿,但可能引入故障。</string> - <string name="renderer_debug">启用图形调试</string> - <string name="renderer_debug_description">启用时,图形 API 将进入较慢的调试模式。</string> - <string name="use_disk_shader_cache">使用磁盘着色器缓存</string> - <string name="use_disk_shader_cache_description">将生成的着色器缓存于磁盘中并进行读取以减少卡顿。</string> + <string name="renderer_reactive_flushing">启用反应性刷新</string> + <string name="renderer_reactive_flushing_description">牺牲性能,提高某些游戏的渲染精度。</string> + <string name="use_disk_shader_cache">磁盘着色器缓存</string> + <string name="use_disk_shader_cache_description">将生成的着色器缓存于磁盘中并进行读取,以减少卡顿。</string> + + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">CPU 调试</string> + <string name="cpu_debug_mode_description">将 CPU 设置为较慢的调试模式。</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">图形调试</string> + <string name="renderer_debug_description">将图形 API 设置为较慢的调试模式。</string> + <string name="fastmem">Fastmem</string> <!-- Audio settings strings --> + <string name="audio_output_engine">输出引擎</string> <string name="audio_volume">音量</string> <string name="audio_volume_description">指定输出的音量。</string> @@ -157,7 +212,9 @@ <string name="ini_saved">已保存设置</string> <string name="gameid_saved">已保存 %1$s 的设置</string> <string name="error_saving">保存 %1$s.ini 时出错: %2$s</string> + <string name="unimplemented_menu">未生效菜单</string> <string name="loading">加载中…</string> + <string name="shutting_down">正在关闭…</string> <string name="reset_setting_confirmation">您要将此设定重设为默认值吗?</string> <string name="reset_to_default">恢复默认</string> <string name="reset_all_settings">重置所有设置项?</string> @@ -165,6 +222,14 @@ <string name="settings_reset">重设设置项</string> <string name="close">关闭</string> <string name="learn_more">了解更多</string> + <string name="auto">自动</string> + <string name="submit">提交</string> + <string name="string_null">无</string> + <string name="string_import">导入</string> + <string name="export">导出</string> + <string name="export_failed">导出失败</string> + <string name="import_failed">导入失败</string> + <string name="cancelling">取消中</string> <!-- GPU driver installation --> <string name="select_gpu_driver">选择 GPU 驱动程序</string> @@ -172,6 +237,7 @@ <string name="select_gpu_driver_install">安装</string> <string name="select_gpu_driver_default">系统默认</string> <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string> + <string name="select_gpu_driver_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string> <string name="system_gpu_driver">系统 GPU 驱动程序</string> <string name="installing_driver">正在安装驱动程序…</string> @@ -182,10 +248,11 @@ <string name="preferences_graphics">图形</string> <string name="preferences_audio">声音</string> <string name="preferences_theme">主题和色彩</string> + <string name="preferences_debug">调试</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">您的 ROM 已加密</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[请参考指南重新转储你的<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">游戏卡带</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">已安装的游戏</a>。]]></string> + <string name="loader_error_encrypted_roms_description"><![CDATA[请按照指南重新转储您的<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">游戏卡带</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">已安装的游戏</a>。]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[请确保 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 文件已安装,使得游戏可以被解密。]]></string> <string name="loader_error_video_core">初始化视频核心时发生错误</string> <string name="loader_error_video_core_description">这通常由不兼容的 GPU 驱动程序造成,安装自定义 GPU 驱动程序可能解决此问题。</string> @@ -226,6 +293,9 @@ <string name="fatal_error">致命错误</string> <string name="fatal_error_message">发生致命错误,请查阅日志获取详细信息。\n继续模拟可能会造成崩溃和错误。</string> <string name="performance_warning">关闭此项会显著降低模拟性能!建议您将此项保持为启用状态。</string> + <string name="device_memory_inadequate">设备 RAM: %1$s\n推荐 RAM: %2$s</string> + <string name="memory_formatted">%1$s%2$s</string> + <string name="no_game_present">当前没有可启动的游戏!</string> <!-- Region Names --> <string name="region_japan">日本</string> @@ -236,7 +306,14 @@ <string name="region_korea">韩国</string> <string name="region_taiwan">中国台湾</string> - <!-- Language Names --> + <!-- Memory Sizes --> + <string name="memory_byte">Byte</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">GB</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> @@ -274,6 +351,11 @@ <string name="anti_aliasing_fxaa">快速近似抗锯齿</string> <string name="anti_aliasing_smaa">子像素形态学抗锯齿</string> + <!-- Screen Layouts --> + <string name="screen_layout_landscape">横向大屏</string> + <string name="screen_layout_portrait">纵向屏幕</string> + <string name="screen_layout_auto">自动</string> + <!-- Aspect Ratios --> <string name="ratio_default">默认 (16:9)</string> <string name="ratio_force_four_three">强制 4:3</string> @@ -303,13 +385,27 @@ <string name="theme_material_you">Material You</string> <!-- Theme Modes --> - <string name="change_theme_mode">主题模式</string> + <string name="change_theme_mode">更改主题模式</string> <string name="theme_mode_follow_system">跟随系统</string> <string name="theme_mode_light">浅色</string> <string name="theme_mode_dark">深色</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + <!-- Black backgrounds theme --> <string name="use_black_backgrounds">使用黑色背景</string> <string name="use_black_backgrounds_description">使用深色主题时,套用黑色背景。</string> -</resources> + <!-- Picture-In-Picture --> + <string name="picture_in_picture">画中画</string> + <string name="picture_in_picture_description">模拟器位于后台时最小化窗口</string> + <string name="pause">暂停</string> + <string name="play">开始</string> + <string name="mute">静音</string> + <string name="unmute">取消静音</string> + + <!-- Licenses screen strings --> + <string name="licenses">许可证</string> + <string name="license_fidelityfx_fsr_description">来自 AMD 的高品质画质升级</string> + </resources> diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index 4a21bf893..b8f468c68 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <string name="app_disclaimer">此軟體可以執行 Nintendo Switch 主機遊戲,但不包含任何遊戲和金鑰。<br /><br />在您開始前,請找到放置於您的裝置儲存空間的 <![CDATA[<b> prod.keys </b>]]> 檔案。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入瞭解</a>]]></string> <string name="emulation_notification_channel_name">模擬進行中</string> @@ -25,6 +25,7 @@ <string name="back">上一步</string> <string name="add_games">新增遊戲</string> <string name="add_games_description">選取您的遊戲資料夾</string> + <string name="step_complete">完成!</string> <!-- Home strings --> <string name="home_games">遊戲</string> @@ -33,11 +34,12 @@ <string name="empty_gamelist">找不到檔案,或者尚未選取遊戲目錄。</string> <string name="search_and_filter_games">搜尋並篩選遊戲</string> <string name="select_games_folder">選取遊戲資料夾</string> - <string name="select_games_folder_description">一律允許 yuzu 填入遊戲清單</string> + <string name="select_games_folder_description">允許 yuzu 填入遊戲清單</string> <string name="add_games_warning">跳過選取遊戲資料夾?</string> <string name="add_games_warning_description">如果資料夾未選取,遊戲將不會顯示在遊戲清單。</string> <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> <string name="home_search_games">搜尋遊戲</string> + <string name="search_settings">搜索设置</string> <string name="games_dir_selected">遊戲目錄已選取</string> <string name="install_prod_keys">安裝 prod.keys</string> <string name="install_prod_keys_description">需要解密零售遊戲</string> @@ -60,13 +62,16 @@ <string name="install_amiibo_keys_description">需要在遊戲中使用 Amiibo</string> <string name="invalid_keys_file">無效的金鑰檔案已選取</string> <string name="install_keys_success">金鑰已成功安裝</string> - <string name="reading_keys_failure">讀取加密金鑰時出現錯誤</string> + <string name="reading_keys_failure">讀取加密金鑰時發生錯誤</string> + <string name="install_prod_keys_failure_extension_description">驗證您的金鑰檔案是否具有 .keys 副檔名並再試一次。</string> + <string name="install_amiibo_keys_failure_extension_description">驗證您的金鑰檔案是否具有 .bin 副檔名並再試一次。</string> <string name="invalid_keys_error">無效的加密金鑰</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">選取的檔案不正確或已損毀,請重新傾印您的金鑰。</string> <string name="install_gpu_driver">安裝 GPU 驅動程式</string> <string name="install_gpu_driver_description">安裝替代驅動程式以取得潛在的更佳效能或準確度</string> <string name="advanced_settings">進階設定</string> + <string name="advanced_settings_game">高级选项: %1$s</string> <string name="settings_description">進行模擬器設定</string> <string name="search_recently_played">最近遊玩</string> <string name="search_recently_added">最近新增</string> @@ -86,6 +91,33 @@ <string name="save_file_invalid_zip_structure_description">首個子資料夾名稱必須為遊戲標題 ID。</string> <string name="import_saves">匯入</string> <string name="export_saves">匯出</string> + <string name="install_firmware">安裝韌體</string> + <string name="install_firmware_description">韌體必須為 ZIP 封存檔,將會用於部分遊戲的啟動</string> + <string name="firmware_installing">正在安裝韌體</string> + <string name="firmware_installed_success">韌體已成功安裝</string> + <string name="firmware_installed_failure">韌體安裝失敗</string> + <string name="firmware_installed_failure_description">请确保固件 nca 文件位于 zip 压缩包的根目录,然后重试。</string> + <string name="share_log">分享偵錯記錄</string> + <string name="share_log_description">分享 yuzu 的記錄檔以便對相關問題進行偵錯</string> + <string name="share_log_missing">找不到記錄檔</string> + <string name="install_game_content">安裝遊戲內容</string> + <string name="install_game_content_description">安裝遊戲更新或 DLC</string> + <string name="installing_game_content">安装中...</string> + <string name="install_game_content_failure">向 NAND 安装文件时失败</string> + <string name="install_game_content_failure_description">请确保附加内容的有效性,并且 prod.keys 密钥文件已安装。</string> + <string name="install_game_content_failure_base">为避免产生冲突,此功能不能用于安装游戏本体。</string> + <string name="install_game_content_failure_file_extension">只有 NSP 或 XCI 格式的附加内容可以安装。请确保您的游戏附加内容是有效的。</string> + <string name="install_game_content_failed_count">%1$d 安装出错</string> + <string name="install_game_content_success">游戏附加内容已成功安装</string> + <string name="install_game_content_success_install">%1$d 安装成功</string> + <string name="install_game_content_success_overwrite">%1$d 覆盖安装成功</string> + <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">不支持自定义驱动</string> + <string name="custom_driver_not_supported_description">此设备不支持自定义驱动。\n请之后再访问此项,查看是否已为此设备添加支持。</string> + <string name="manage_yuzu_data">管理 yuzu 数据</string> + <string name="manage_yuzu_data_description">导入/导出固件、密钥、用户数据及其他。</string> + <string name="share_save_file">分享存档文件</string> + <string name="export_save_failed">导出存档文件失败</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia 不真實</string> @@ -94,7 +126,18 @@ <string name="contributors">參與者</string> <string name="contributors_description">使用來自 yuzu 團隊的 \u2764 製作</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> + <string name="licenses_description">這些專案使 yuzu Android 版成為可能</string> <string name="build">組建</string> + <string name="user_data">用户数据</string> + <string name="user_data_description">导入/导出应用程序所有数据。\n\n导入用户数据时,将删除当前所有的用户数据!</string> + <string name="exporting_user_data">正在导出用户数据...</string> + <string name="importing_user_data">正在导入用户数据...</string> + <string name="import_user_data">导入用户数据</string> + <string name="invalid_yuzu_backup">无效的 yuzu 备份</string> + <string name="user_data_export_success">导出用户数据成功</string> + <string name="user_data_import_success">导入用户数据成功</string> + <string name="user_data_export_cancelled">已取消导出数据</string> + <string name="user_data_import_failed_description">请确保用户数据文件夹位于 zip 压缩包的根目录,并在 config/config.ini 路径中包含配置文件,然后重试。</string> <string name="support_link">https://discord.gg/u77vRWY</string> <string name="website_link">https://yuzu-emu.org/</string> <string name="github_link">https://github.com/yuzu-emu</string> @@ -114,28 +157,29 @@ <string name="are_you_interested">您仍感興趣嗎?</string> <!-- General settings strings --> - <string name="frame_limit_enable">啟用限制速度</string> - <string name="frame_limit_enable_description">若啟用,模擬速度將會限制在標準速度的指定百分比。</string> + <string name="frame_limit_enable">限制速度</string> + <string name="frame_limit_enable_description">將模擬速度限制在標準速度的指定百分比。</string> <string name="frame_limit_slider">限制速度百分比</string> - <string name="frame_limit_slider_description">指定限制模擬速度的百分比。預設為 100%,模擬速度將被限制為標準速度。更高或更低的值將會增加或減少速度限制。</string> + <string name="frame_limit_slider_description">指定限制模擬速度的百分比。100% 為標準速度,更高或更低的值將會增加或減少速度限制。</string> <string name="cpu_accuracy">CPU 準確度</string> + <string name="value_with_units">%1$s%2$s</string> <!-- System settings strings --> <string name="use_docked_mode">底座模式</string> - <string name="use_docked_mode_description">以底座模式模擬,以犧牲效能的代價提高解析度。</string> + <string name="use_docked_mode_description">提高解析度,降低效能。停用後將會使用手提模式,會降低解析度並提高效能。</string> <string name="emulated_region">模擬區域</string> <string name="emulated_language">模擬語言</string> <string name="select_rtc_date">選取 RTC 日期</string> <string name="select_rtc_time">選取 RTC 時間</string> - <string name="use_custom_rtc">啟用自訂 RTC</string> - <string name="use_custom_rtc_description">此設定允許您設定與您的目前系統時間相互獨立的自訂即時時鐘</string> + <string name="use_custom_rtc">自訂 RTC</string> + <string name="use_custom_rtc_description">允許您設定與您的目前系統時間相互獨立的自訂即時時鐘。</string> <string name="set_custom_rtc">設定自訂 RTC</string> <!-- Graphics settings strings --> - <string name="renderer_api">API</string> <string name="renderer_accuracy">準確度層級</string> - <string name="renderer_resolution">解析度</string> + <string name="renderer_resolution">解析度 (手提/底座)</string> <string name="renderer_vsync">VSync 模式</string> + <string name="renderer_screen_layout">屏幕方向</string> <string name="renderer_aspect_ratio">長寬比</string> <string name="renderer_scaling_filter">視窗適應過濾器</string> <string name="renderer_anti_aliasing">消除鋸齒方法</string> @@ -143,12 +187,23 @@ <string name="renderer_force_max_clock_description">強制 GPU 以最大可能時脈執行 (熱溫限制仍被套用)。</string> <string name="renderer_asynchronous_shaders">使用非同步著色器</string> <string name="renderer_asynchronous_shaders_description">非同步編譯著色器,將會減少間斷,但可能會引入故障。</string> - <string name="renderer_debug">啟用圖形偵錯</string> - <string name="renderer_debug_description">核取時,圖形 API 將會進入慢速偵錯模式。</string> - <string name="use_disk_shader_cache">使用磁碟著色器快取</string> + <string name="renderer_reactive_flushing">使用重新啟用排清</string> + <string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度。</string> + <string name="use_disk_shader_cache">磁碟著色器快取</string> <string name="use_disk_shader_cache_description">透過將產生的著色器儲存並載入至磁碟,減少中斷。</string> + <!-- Debug settings strings --> + <string name="cpu">CPU</string> + <string name="cpu_debug_mode">CPU 调试</string> + <string name="cpu_debug_mode_description">将 CPU 设置为较慢的调试模式。</string> + <string name="gpu">GPU</string> + <string name="renderer_api">API</string> + <string name="renderer_debug">圖形偵錯</string> + <string name="renderer_debug_description">將圖形 API 設為慢速偵錯模式。</string> + <string name="fastmem">Fastmem</string> + <!-- Audio settings strings --> + <string name="audio_output_engine">输出引擎</string> <string name="audio_volume">音量</string> <string name="audio_volume_description">指定音訊輸出音量。</string> @@ -157,7 +212,9 @@ <string name="ini_saved">已儲存設定</string> <string name="gameid_saved">已儲存 %1$s 設定</string> <string name="error_saving">儲存 %1$s 時發生錯誤 ini: %2$s</string> + <string name="unimplemented_menu">未生效菜单</string> <string name="loading">正在載入…</string> + <string name="shutting_down">正在关闭…</string> <string name="reset_setting_confirmation">要將此設定重設回預設值嗎?</string> <string name="reset_to_default">重設為預設值</string> <string name="reset_all_settings">重設所有設定?</string> @@ -165,6 +222,14 @@ <string name="settings_reset">設定已重設</string> <string name="close">關閉</string> <string name="learn_more">深入瞭解</string> + <string name="auto">自動</string> + <string name="submit">提交</string> + <string name="string_null">無</string> + <string name="string_import">匯入</string> + <string name="export">匯出</string> + <string name="export_failed">导出失败</string> + <string name="import_failed">导入失败</string> + <string name="cancelling">取消中</string> <!-- GPU driver installation --> <string name="select_gpu_driver">選取 GPU 驅動程式</string> @@ -172,6 +237,7 @@ <string name="select_gpu_driver_install">安裝</string> <string name="select_gpu_driver_default">預設</string> <string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string> + <string name="select_gpu_driver_error">選取的驅動程式無效,將使用系統預設驅動程式!</string> <string name="system_gpu_driver">系統 GPU 驅動程式</string> <string name="installing_driver">正在安裝驅動程式…</string> @@ -182,10 +248,11 @@ <string name="preferences_graphics">圖形</string> <string name="preferences_audio">音訊</string> <string name="preferences_theme">主題和色彩</string> + <string name="preferences_debug">偵錯</string> <!-- ROM loading errors --> <string name="loader_error_encrypted">您的 ROM 已加密</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[請依循指南重新傾印您的<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games\">遊戲卡匣</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop\">安裝標題</a>。]]></string> + <string name="loader_error_encrypted_roms_description"><![CDATA[请按照指南重新转储您的<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">游戏卡带</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">已安装的游戏</a>。]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[請確保您的 <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 檔案已安裝,讓遊戲可以解密。]]></string> <string name="loader_error_video_core">初始化視訊核心時發生錯誤</string> <string name="loader_error_video_core_description">這經常由不相容的 GPU 驅動程式造成,安裝自訂 GPU 驅動程式可能會解決此問題。</string> @@ -219,13 +286,16 @@ <!-- Errors and warnings --> <string name="abort_button">中止</string> <string name="continue_button">繼續</string> - <string name="system_archive_not_found">找不到系統檔案</string> + <string name="system_archive_not_found">找不到系統封存</string> <string name="system_archive_not_found_message">%s 遺失,請傾印您的系統封存。\n繼續模擬可能會造成當機和錯誤。</string> <string name="system_archive_general">系統封存</string> <string name="save_load_error">儲存/載入發生錯誤</string> <string name="fatal_error">嚴重錯誤</string> <string name="fatal_error_message">發生嚴重錯誤,檢查記錄以取得詳細資訊。\n繼續模擬可能會造成當機和錯誤。</string> <string name="performance_warning">關閉此設定會顯著降低模擬效能!如需最佳體驗,建議您將此設定保持為啟用狀態。</string> + <string name="device_memory_inadequate">设备 RAM: %1$s\n推荐 RAM: %2$s</string> + <string name="memory_formatted">%1$s%2$s</string> + <string name="no_game_present">当前没有可启动的游戏!</string> <!-- Region Names --> <string name="region_japan">日本</string> @@ -236,7 +306,14 @@ <string name="region_korea">南韓</string> <string name="region_taiwan">台灣</string> - <!-- Language Names --> + <!-- Memory Sizes --> + <string name="memory_byte">Byte</string> + <string name="memory_kilobyte">KB</string> + <string name="memory_megabyte">MB</string> + <string name="memory_gigabyte">英國</string> + <string name="memory_terabyte">TB</string> + <string name="memory_petabyte">PB</string> + <string name="memory_exabyte">EB</string> <!-- Renderer APIs --> <string name="renderer_vulkan">Vulkan</string> @@ -274,14 +351,20 @@ <string name="anti_aliasing_fxaa">FXAA</string> <string name="anti_aliasing_smaa">SMAA</string> + <!-- Screen Layouts --> + <string name="screen_layout_landscape">横向大屏</string> + <string name="screen_layout_portrait">纵向屏幕</string> + <string name="screen_layout_auto">自動</string> + <!-- Aspect Ratios --> <string name="ratio_default">預設 (16:9)</string> <string name="ratio_force_four_three">強制 4:3</string> <string name="ratio_force_twenty_one_nine">強制 21:9</string> <string name="ratio_force_sixteen_ten">強制 16:10</string> - <string name="ratio_stretch">延伸視窗</string> + <string name="ratio_stretch">延展視窗</string> <!-- CPU Accuracy --> + <string name="cpu_accuracy_accurate">高精度</string> <string name="cpu_accuracy_unsafe">低精度</string> <string name="cpu_accuracy_paranoid">不合理 (慢)</string> @@ -307,8 +390,22 @@ <string name="theme_mode_light">淺色</string> <string name="theme_mode_dark">深色</string> + <!-- Audio output engines --> + <string name="cubeb">cubeb</string> + <!-- Black backgrounds theme --> - <string name="use_black_backgrounds">使用黑色背景</string> + <string name="use_black_backgrounds">黑色背景</string> <string name="use_black_backgrounds_description">使用深色主題時,套用黑色背景。</string> -</resources> + <!-- Picture-In-Picture --> + <string name="picture_in_picture">画中画</string> + <string name="picture_in_picture_description">模拟器位于后台时最小化窗口</string> + <string name="pause">暂停</string> + <string name="play">开始</string> + <string name="mute">靜音</string> + <string name="unmute">取消靜音</string> + + <!-- Licenses screen strings --> + <string name="licenses">授權</string> + <string name="license_fidelityfx_fsr_description">來自 AMD 的升級圖像品質</string> + </resources> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 9e4854221..c551a6106 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -72,7 +72,7 @@ <string name="invalid_keys_error">Invalid encryption keys</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string> - <string name="gpu_driver_manager">GPU Driver Manager</string> + <string name="gpu_driver_manager">GPU driver manager</string> <string name="install_gpu_driver">Install GPU driver</string> <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> <string name="advanced_settings">Advanced settings</string> @@ -124,6 +124,24 @@ <string name="share_save_file">Share save file</string> <string name="export_save_failed">Failed to export save</string> + <!-- Applet launcher strings --> + <string name="applets">Applet launcher</string> + <string name="applets_description">Launch system applets using installed firmware</string> + <string name="applets_error_firmware">Firmware not installed</string> + <string name="applets_error_applet">Applet not available</string> + <string name="applets_error_description"><![CDATA[Please ensure your <a href="https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys">prod.keys</a> file and <a href="https://yuzu-emu.org/help/quickstart/#dumping-system-firmware">firmware</a> are installed and try again.]]></string> + <string name="album_applet">Album</string> + <string name="album_applet_description">See images stored in the user screenshots folder with the system photo viewer</string> + <string name="mii_edit_applet">Mii edit</string> + <string name="mii_edit_applet_description">View and edit Miis with the system editor</string> + <string name="cabinet_applet">Cabinet</string> + <string name="cabinet_applet_description">Edit and delete data stored on amiibo</string> + <string name="cabinet_launcher">Cabinet launcher</string> + <string name="cabinet_nickname_and_owner">Nickname and owner settings</string> + <string name="cabinet_game_data_eraser">Game data eraser</string> + <string name="cabinet_restorer">Restorer</string> + <string name="cabinet_formatter">Formatter</string> + <!-- About screen strings --> <string name="gaia_is_not_real">Gaia isn\'t real</string> <string name="copied_to_clipboard">Copied to clipboard</string> diff --git a/src/android/app/src/main/res/xml/locales_config.xml b/src/android/app/src/main/res/xml/locales_config.xml deleted file mode 100644 index 51b88d9dc..000000000 --- a/src/android/app/src/main/res/xml/locales_config.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<locale-config xmlns:android="http://schemas.android.com/apk/res/android"> - <locale android:name="en" /> <!-- English (default) --> - <locale android:name="de" /> <!-- German --> - <locale android:name="es" /> <!-- Spanish --> - <locale android:name="fr" /> <!-- French --> - <locale android:name="it" /> <!-- Italian --> - <locale android:name="ja" /> <!-- Japanese --> - <locale android:name="nb" /> <!-- Norwegian Bokmal --> - <locale android:name="pl" /> <!-- Polish --> - <locale android:name="pt-rBR" /> <!-- Portuguese (Brazil) --> - <locale android:name="pt-RPT" /> <!-- Portuguese (Portugal) --> - <locale android:name="ru" /> <!-- Russian --> - <locale android:name="uk" /> <!-- Ukranian --> - <locale android:name="zh-rCN" /> <!-- Chinese (China) --> - <locale android:name="zh-rTW" /> <!-- Chinese (Taiwan) --> -</locale-config> diff --git a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp index 2c16d3769..e2b9eb566 100644 --- a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp +++ b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp @@ -1,107 +1,107 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "audio_core/adsp/apps/opus/opus_decode_object.h"
-#include "common/assert.h"
-
-namespace AudioCore::ADSP::OpusDecoder {
-namespace {
-bool IsValidChannelCount(u32 channel_count) {
- return channel_count == 1 || channel_count == 2;
-}
-} // namespace
-
-u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
- if (!IsValidChannelCount(channel_count)) {
- return 0;
- }
- return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
-}
-
-OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
- auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
- auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
-
- if (new_decoder->magic == DecodeObjectMagic) {
- if (!new_decoder->initialized ||
- (new_decoder->initialized && new_decoder->self == comparison)) {
- new_decoder->state_valid = true;
- }
- } else {
- new_decoder->initialized = false;
- new_decoder->state_valid = true;
- }
- return *new_decoder;
-}
-
-s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- if (initialized) {
- return OPUS_OK;
- }
-
- // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include
- // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer
- // provided.
- // We could use _create and have libopus allocate it for us, but then we have to separately
- // track which decoder is being used between this and multistream in order to call the correct
- // destroy from the host side.
- // This is a bit cringe, but is safe as these objects are only ever initialized inside the given
- // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow.
- decoder = (LibOpusDecoder*)(this + 1);
- s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
- if (ret == OPUS_OK) {
- magic = DecodeObjectMagic;
- initialized = true;
- state_valid = true;
- self = this;
- final_range = 0;
- }
- return ret;
-}
-
-s32 OpusDecodeObject::Shutdown() {
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- if (initialized) {
- magic = 0x0;
- initialized = false;
- state_valid = false;
- self = nullptr;
- final_range = 0;
- decoder = nullptr;
- }
- return OPUS_OK;
-}
-
-s32 OpusDecodeObject::ResetDecoder() {
- return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
-}
-
-s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
- u64 input_data, u64 input_data_size) {
- ASSERT(initialized);
- out_sample_count = 0;
-
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- auto ret_code_or_samples = opus_decode(
- decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
- reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
-
- if (ret_code_or_samples < OPUS_OK) {
- return ret_code_or_samples;
- }
-
- out_sample_count = ret_code_or_samples;
- return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
-}
-
-} // namespace AudioCore::ADSP::OpusDecoder
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/adsp/apps/opus/opus_decode_object.h" +#include "common/assert.h" + +namespace AudioCore::ADSP::OpusDecoder { +namespace { +bool IsValidChannelCount(u32 channel_count) { + return channel_count == 1 || channel_count == 2; +} +} // namespace + +u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { + if (!IsValidChannelCount(channel_count)) { + return 0; + } + return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); +} + +OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { + auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer); + auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2); + + if (new_decoder->magic == DecodeObjectMagic) { + if (!new_decoder->initialized || + (new_decoder->initialized && new_decoder->self == comparison)) { + new_decoder->state_valid = true; + } + } else { + new_decoder->initialized = false; + new_decoder->state_valid = true; + } + return *new_decoder; +} + +s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + return OPUS_OK; + } + + // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include + // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer + // provided. + // We could use _create and have libopus allocate it for us, but then we have to separately + // track which decoder is being used between this and multistream in order to call the correct + // destroy from the host side. + // This is a bit cringe, but is safe as these objects are only ever initialized inside the given + // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow. + decoder = (LibOpusDecoder*)(this + 1); + s32 ret = opus_decoder_init(decoder, sample_rate, channel_count); + if (ret == OPUS_OK) { + magic = DecodeObjectMagic; + initialized = true; + state_valid = true; + self = this; + final_range = 0; + } + return ret; +} + +s32 OpusDecodeObject::Shutdown() { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + magic = 0x0; + initialized = false; + state_valid = false; + self = nullptr; + final_range = 0; + decoder = nullptr; + } + return OPUS_OK; +} + +s32 OpusDecodeObject::ResetDecoder() { + return opus_decoder_ctl(decoder, OPUS_RESET_STATE); +} + +s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, + u64 input_data, u64 input_data_size) { + ASSERT(initialized); + out_sample_count = 0; + + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + auto ret_code_or_samples = opus_decode( + decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), + reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); + + if (ret_code_or_samples < OPUS_OK) { + return ret_code_or_samples; + } + + out_sample_count = ret_code_or_samples; + return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); +} + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.cpp b/src/audio_core/adsp/apps/opus/opus_decoder.cpp index 2084de128..75f0fb9ad 100644 --- a/src/audio_core/adsp/apps/opus/opus_decoder.cpp +++ b/src/audio_core/adsp/apps/opus/opus_decoder.cpp @@ -30,9 +30,9 @@ bool IsValidMultiStreamChannelCount(u32 channel_count) { return channel_count <= OpusStreamCountMax; } -bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) { +bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_count) { return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && - sterero_stream_count > 0 && sterero_stream_count <= total_stream_count; + stereo_stream_count >= 0 && stereo_stream_count <= total_stream_count; } } // namespace diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp index f6d362e68..7f1ed0450 100644 --- a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp @@ -1,111 +1,111 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
-#include "common/assert.h"
-
-namespace AudioCore::ADSP::OpusDecoder {
-
-namespace {
-bool IsValidChannelCount(u32 channel_count) {
- return channel_count == 1 || channel_count == 2;
-}
-
-bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
- return total_stream_count > 0 && stereo_stream_count > 0 &&
- stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
-}
-} // namespace
-
-u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
- u32 stereo_stream_count) {
- if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
- return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
- opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
- }
- return 0;
-}
-
-OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
- auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
- auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
-
- if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
- if (!new_decoder->initialized ||
- (new_decoder->initialized && new_decoder->self == comparison)) {
- new_decoder->state_valid = true;
- }
- } else {
- new_decoder->initialized = false;
- new_decoder->state_valid = true;
- }
- return *new_decoder;
-}
-
-s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
- u32 channel_count, u32 stereo_stream_count,
- u8* mappings) {
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- if (initialized) {
- return OPUS_OK;
- }
-
- // See OpusDecodeObject::InitializeDecoder for an explanation of this
- decoder = (LibOpusMSDecoder*)(this + 1);
- s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
- stereo_stream_count, mappings);
- if (ret == OPUS_OK) {
- magic = DecodeMultiStreamObjectMagic;
- initialized = true;
- state_valid = true;
- self = this;
- final_range = 0;
- }
- return ret;
-}
-
-s32 OpusMultiStreamDecodeObject::Shutdown() {
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- if (initialized) {
- magic = 0x0;
- initialized = false;
- state_valid = false;
- self = nullptr;
- final_range = 0;
- decoder = nullptr;
- }
- return OPUS_OK;
-}
-
-s32 OpusMultiStreamDecodeObject::ResetDecoder() {
- return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
-}
-
-s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
- u64 output_data_size, u64 input_data, u64 input_data_size) {
- ASSERT(initialized);
- out_sample_count = 0;
-
- if (!state_valid) {
- return OPUS_INVALID_STATE;
- }
-
- auto ret_code_or_samples = opus_multistream_decode(
- decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
- reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
-
- if (ret_code_or_samples < OPUS_OK) {
- return ret_code_or_samples;
- }
-
- out_sample_count = ret_code_or_samples;
- return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
-}
-
-} // namespace AudioCore::ADSP::OpusDecoder
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" +#include "common/assert.h" + +namespace AudioCore::ADSP::OpusDecoder { + +namespace { +bool IsValidChannelCount(u32 channel_count) { + return channel_count == 1 || channel_count == 2; +} + +bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { + return total_stream_count > 0 && stereo_stream_count > 0 && + stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); +} +} // namespace + +u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count, + u32 stereo_stream_count) { + if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) { + return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) + + opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count); + } + return 0; +} + +OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) { + auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer); + auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2); + + if (new_decoder->magic == DecodeMultiStreamObjectMagic) { + if (!new_decoder->initialized || + (new_decoder->initialized && new_decoder->self == comparison)) { + new_decoder->state_valid = true; + } + } else { + new_decoder->initialized = false; + new_decoder->state_valid = true; + } + return *new_decoder; +} + +s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count, + u32 channel_count, u32 stereo_stream_count, + u8* mappings) { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + return OPUS_OK; + } + + // See OpusDecodeObject::InitializeDecoder for an explanation of this + decoder = (LibOpusMSDecoder*)(this + 1); + s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count, + stereo_stream_count, mappings); + if (ret == OPUS_OK) { + magic = DecodeMultiStreamObjectMagic; + initialized = true; + state_valid = true; + self = this; + final_range = 0; + } + return ret; +} + +s32 OpusMultiStreamDecodeObject::Shutdown() { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + magic = 0x0; + initialized = false; + state_valid = false; + self = nullptr; + final_range = 0; + decoder = nullptr; + } + return OPUS_OK; +} + +s32 OpusMultiStreamDecodeObject::ResetDecoder() { + return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE); +} + +s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data, + u64 output_data_size, u64 input_data, u64 input_data_size) { + ASSERT(initialized); + out_sample_count = 0; + + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + auto ret_code_or_samples = opus_multistream_decode( + decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), + reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); + + if (ret_code_or_samples < OPUS_OK) { + return ret_code_or_samples; + } + + out_sample_count = ret_code_or_samples; + return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); +} + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp index 5b23fce14..c6fd45f47 100644 --- a/src/audio_core/opus/decoder.cpp +++ b/src/audio_core/opus/decoder.cpp @@ -1,179 +1,179 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "audio_core/opus/decoder.h"
-#include "audio_core/opus/hardware_opus.h"
-#include "audio_core/opus/parameters.h"
-#include "common/alignment.h"
-#include "common/swap.h"
-#include "core/core.h"
-
-namespace AudioCore::OpusDecoder {
-using namespace Service::Audio;
-namespace {
-OpusPacketHeader ReverseHeader(OpusPacketHeader header) {
- OpusPacketHeader out;
- out.size = Common::swap32(header.size);
- out.final_range = Common::swap32(header.final_range);
- return out;
-}
-} // namespace
-
-OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
- : system{system_}, hardware_opus{hardware_opus_} {}
-
-OpusDecoder::~OpusDecoder() {
- if (decode_object_initialized) {
- hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size);
- }
-}
-
-Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
- u64 transfer_memory_size) {
- auto frame_size{params.use_large_frame_size ? 5760 : 1920};
- shared_buffer_size = transfer_memory_size;
- shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
- shared_memory_mapped = true;
-
- buffer_size =
- Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
-
- out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
- size_t in_data_size{0x600u};
- in_data = {out_data.data() - in_data_size, in_data_size};
-
- ON_RESULT_FAILURE {
- if (shared_memory_mapped) {
- shared_memory_mapped = false;
- ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
- }
- };
-
- R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count,
- shared_buffer.get(), shared_buffer_size));
-
- sample_rate = params.sample_rate;
- channel_count = params.channel_count;
- use_large_frame_size = params.use_large_frame_size;
- decode_object_initialized = true;
- R_SUCCEED();
-}
-
-Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
- Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
- auto frame_size{params.use_large_frame_size ? 5760 : 1920};
- shared_buffer_size = transfer_memory_size;
- shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
- shared_memory_mapped = true;
-
- buffer_size =
- Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
-
- out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
- size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)};
- in_data = {out_data.data() - in_data_size, in_data_size};
-
- ON_RESULT_FAILURE {
- if (shared_memory_mapped) {
- shared_memory_mapped = false;
- ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
- }
- };
-
- R_TRY(hardware_opus.InitializeMultiStreamDecodeObject(
- params.sample_rate, params.channel_count, params.total_stream_count,
- params.stereo_stream_count, params.mappings.data(), shared_buffer.get(),
- shared_buffer_size));
-
- sample_rate = params.sample_rate;
- channel_count = params.channel_count;
- total_stream_count = params.total_stream_count;
- stereo_stream_count = params.stereo_stream_count;
- use_large_frame_size = params.use_large_frame_size;
- decode_object_initialized = true;
- R_SUCCEED();
-}
-
-Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken,
- u32* out_sample_count, std::span<const u8> input_data,
- std::span<u8> output_data, bool reset) {
- u32 out_samples;
- u64 time_taken{};
-
- R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
-
- auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
- OpusPacketHeader header{ReverseHeader(*header_p)};
-
- R_UNLESS(in_data.size_bytes() >= header.size &&
- header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
- ResultBufferTooSmall);
-
- if (!shared_memory_mapped) {
- R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
- shared_memory_mapped = true;
- }
-
- std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
-
- R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(),
- channel_count, in_data.data(), header.size,
- shared_buffer.get(), time_taken, reset));
-
- std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
-
- *out_data_size = header.size + sizeof(OpusPacketHeader);
- *out_sample_count = out_samples;
- if (out_time_taken) {
- *out_time_taken = time_taken / 1000;
- }
- R_SUCCEED();
-}
-
-Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) {
- R_SUCCEED_IF(shared_memory_mapped);
- shared_memory_mapped = true;
- R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
-}
-
-Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
- u32* out_sample_count,
- std::span<const u8> input_data,
- std::span<u8> output_data, bool reset) {
- u32 out_samples;
- u64 time_taken{};
-
- R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
-
- auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
- OpusPacketHeader header{ReverseHeader(*header_p)};
-
- LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
- header.size, input_data.size_bytes(), in_data.size_bytes());
-
- R_UNLESS(in_data.size_bytes() >= header.size &&
- header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
- ResultBufferTooSmall);
-
- if (!shared_memory_mapped) {
- R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
- shared_memory_mapped = true;
- }
-
- std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
-
- R_TRY(hardware_opus.DecodeInterleavedForMultiStream(
- out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(),
- header.size, shared_buffer.get(), time_taken, reset));
-
- std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
-
- *out_data_size = header.size + sizeof(OpusPacketHeader);
- *out_sample_count = out_samples;
- if (out_time_taken) {
- *out_time_taken = time_taken / 1000;
- }
- R_SUCCEED();
-}
-
-} // namespace AudioCore::OpusDecoder
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/opus/decoder.h" +#include "audio_core/opus/hardware_opus.h" +#include "audio_core/opus/parameters.h" +#include "common/alignment.h" +#include "common/swap.h" +#include "core/core.h" + +namespace AudioCore::OpusDecoder { +using namespace Service::Audio; +namespace { +OpusPacketHeader ReverseHeader(OpusPacketHeader header) { + OpusPacketHeader out; + out.size = Common::swap32(header.size); + out.final_range = Common::swap32(header.final_range); + return out; +} +} // namespace + +OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_) + : system{system_}, hardware_opus{hardware_opus_} {} + +OpusDecoder::~OpusDecoder() { + if (decode_object_initialized) { + hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size); + } +} + +Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size) { + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + shared_buffer_size = transfer_memory_size; + shared_buffer = std::make_unique<u8[]>(shared_buffer_size); + shared_memory_mapped = true; + + buffer_size = + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); + + out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; + size_t in_data_size{0x600u}; + in_data = {out_data.data() - in_data_size, in_data_size}; + + ON_RESULT_FAILURE { + if (shared_memory_mapped) { + shared_memory_mapped = false; + ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); + } + }; + + R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count, + shared_buffer.get(), shared_buffer_size)); + + sample_rate = params.sample_rate; + channel_count = params.channel_count; + use_large_frame_size = params.use_large_frame_size; + decode_object_initialized = true; + R_SUCCEED(); +} + +Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params, + Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + shared_buffer_size = transfer_memory_size; + shared_buffer = std::make_unique<u8[]>(shared_buffer_size); + shared_memory_mapped = true; + + buffer_size = + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); + + out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; + size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)}; + in_data = {out_data.data() - in_data_size, in_data_size}; + + ON_RESULT_FAILURE { + if (shared_memory_mapped) { + shared_memory_mapped = false; + ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); + } + }; + + R_TRY(hardware_opus.InitializeMultiStreamDecodeObject( + params.sample_rate, params.channel_count, params.total_stream_count, + params.stereo_stream_count, params.mappings.data(), shared_buffer.get(), + shared_buffer_size)); + + sample_rate = params.sample_rate; + channel_count = params.channel_count; + total_stream_count = params.total_stream_count; + stereo_stream_count = params.stereo_stream_count; + use_large_frame_size = params.use_large_frame_size; + decode_object_initialized = true; + R_SUCCEED(); +} + +Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken, + u32* out_sample_count, std::span<const u8> input_data, + std::span<u8> output_data, bool reset) { + u32 out_samples; + u64 time_taken{}; + + R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); + + auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; + OpusPacketHeader header{ReverseHeader(*header_p)}; + + R_UNLESS(in_data.size_bytes() >= header.size && + header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), + ResultBufferTooSmall); + + if (!shared_memory_mapped) { + R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); + shared_memory_mapped = true; + } + + std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); + + R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(), + channel_count, in_data.data(), header.size, + shared_buffer.get(), time_taken, reset)); + + std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); + + *out_data_size = header.size + sizeof(OpusPacketHeader); + *out_sample_count = out_samples; + if (out_time_taken) { + *out_time_taken = time_taken / 1000; + } + R_SUCCEED(); +} + +Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) { + R_SUCCEED_IF(shared_memory_mapped); + shared_memory_mapped = true; + R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); +} + +Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, + u32* out_sample_count, + std::span<const u8> input_data, + std::span<u8> output_data, bool reset) { + u32 out_samples; + u64 time_taken{}; + + R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); + + auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; + OpusPacketHeader header{ReverseHeader(*header_p)}; + + LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", + header.size, input_data.size_bytes(), in_data.size_bytes()); + + R_UNLESS(in_data.size_bytes() >= header.size && + header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), + ResultBufferTooSmall); + + if (!shared_memory_mapped) { + R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); + shared_memory_mapped = true; + } + + std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); + + R_TRY(hardware_opus.DecodeInterleavedForMultiStream( + out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(), + header.size, shared_buffer.get(), time_taken, reset)); + + std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); + + *out_data_size = header.size + sizeof(OpusPacketHeader); + *out_sample_count = out_samples; + if (out_time_taken) { + *out_time_taken = time_taken / 1000; + } + R_SUCCEED(); +} + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/decoder.h b/src/audio_core/opus/decoder.h index d08d8a4a4..fd728958a 100644 --- a/src/audio_core/opus/decoder.h +++ b/src/audio_core/opus/decoder.h @@ -1,53 +1,53 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <span>
-
-#include "audio_core/opus/parameters.h"
-#include "common/common_types.h"
-#include "core/hle/kernel/k_transfer_memory.h"
-#include "core/hle/service/audio/errors.h"
-
-namespace Core {
-class System;
-}
-
-namespace AudioCore::OpusDecoder {
-class HardwareOpus;
-
-class OpusDecoder {
-public:
- explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
- ~OpusDecoder();
-
- Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
- u64 transfer_memory_size);
- Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
- u64 transfer_memory_size);
- Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count,
- std::span<const u8> input_data, std::span<u8> output_data, bool reset);
- Result SetContext([[maybe_unused]] std::span<const u8> context);
- Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
- u32* out_sample_count, std::span<const u8> input_data,
- std::span<u8> output_data, bool reset);
-
-private:
- Core::System& system;
- HardwareOpus& hardware_opus;
- std::unique_ptr<u8[]> shared_buffer{};
- u64 shared_buffer_size;
- std::span<u8> in_data{};
- std::span<u8> out_data{};
- u64 buffer_size{};
- s32 sample_rate{};
- s32 channel_count{};
- bool use_large_frame_size{false};
- s32 total_stream_count{};
- s32 stereo_stream_count{};
- bool shared_memory_mapped{false};
- bool decode_object_initialized{false};
-};
-
-} // namespace AudioCore::OpusDecoder
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <span> + +#include "audio_core/opus/parameters.h" +#include "common/common_types.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/audio/errors.h" + +namespace Core { +class System; +} + +namespace AudioCore::OpusDecoder { +class HardwareOpus; + +class OpusDecoder { +public: + explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_); + ~OpusDecoder(); + + Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size); + Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size); + Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count, + std::span<const u8> input_data, std::span<u8> output_data, bool reset); + Result SetContext([[maybe_unused]] std::span<const u8> context); + Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, + u32* out_sample_count, std::span<const u8> input_data, + std::span<u8> output_data, bool reset); + +private: + Core::System& system; + HardwareOpus& hardware_opus; + std::unique_ptr<u8[]> shared_buffer{}; + u64 shared_buffer_size; + std::span<u8> in_data{}; + std::span<u8> out_data{}; + u64 buffer_size{}; + s32 sample_rate{}; + s32 channel_count{}; + bool use_large_frame_size{false}; + s32 total_stream_count{}; + s32 stereo_stream_count{}; + bool shared_memory_mapped{false}; + bool decode_object_initialized{false}; +}; + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/decoder_manager.cpp b/src/audio_core/opus/decoder_manager.cpp index 4a5382973..1464880a1 100644 --- a/src/audio_core/opus/decoder_manager.cpp +++ b/src/audio_core/opus/decoder_manager.cpp @@ -1,102 +1,102 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "audio_core/adsp/apps/opus/opus_decoder.h"
-#include "audio_core/opus/decoder_manager.h"
-#include "common/alignment.h"
-#include "core/core.h"
-
-namespace AudioCore::OpusDecoder {
-using namespace Service::Audio;
-
-namespace {
-bool IsValidChannelCount(u32 channel_count) {
- return channel_count == 1 || channel_count == 2;
-}
-
-bool IsValidMultiStreamChannelCount(u32 channel_count) {
- return channel_count > 0 && channel_count <= OpusStreamCountMax;
-}
-
-bool IsValidSampleRate(u32 sample_rate) {
- return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 ||
- sample_rate == 24'000 || sample_rate == 48'000;
-}
-
-bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) {
- return total_stream_count > 0 && stereo_stream_count > 0 &&
- stereo_stream_count <= total_stream_count &&
- total_stream_count + stereo_stream_count <= channel_count;
-}
-
-} // namespace
-
-OpusDecoderManager::OpusDecoderManager(Core::System& system_)
- : system{system_}, hardware_opus{system} {
- for (u32 i = 0; i < MaxChannels; i++) {
- required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i);
- }
-}
-
-Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) {
- OpusParametersEx ex{
- .sample_rate = params.sample_rate,
- .channel_count = params.channel_count,
- .use_large_frame_size = false,
- };
- R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
-}
-
-Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
- R_RETURN(GetWorkBufferSizeExEx(params, out_size));
-}
-
-Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) {
- R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
- R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
-
- auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]};
- auto frame_size{params.use_large_frame_size ? 5760 : 1920};
- work_buffer_size +=
- Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
- out_size = work_buffer_size + 0x600;
- R_SUCCEED();
-}
-
-Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
- u64& out_size) {
- OpusMultiStreamParametersEx ex{
- .sample_rate = params.sample_rate,
- .channel_count = params.channel_count,
- .total_stream_count = params.total_stream_count,
- .stereo_stream_count = params.stereo_stream_count,
- .use_large_frame_size = false,
- .mappings = {},
- };
- R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
-}
-
-Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
- u64& out_size) {
- R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
-}
-
-Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
- u64& out_size) {
- R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
- R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
- R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
- params.stereo_stream_count),
- ResultInvalidOpusSampleRate);
-
- auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream(
- params.total_stream_count, params.stereo_stream_count)};
- auto frame_size{params.use_large_frame_size ? 5760 : 1920};
- work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64);
- work_buffer_size +=
- Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
- out_size = work_buffer_size;
- R_SUCCEED();
-}
-
-} // namespace AudioCore::OpusDecoder
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/adsp/apps/opus/opus_decoder.h" +#include "audio_core/opus/decoder_manager.h" +#include "common/alignment.h" +#include "core/core.h" + +namespace AudioCore::OpusDecoder { +using namespace Service::Audio; + +namespace { +bool IsValidChannelCount(u32 channel_count) { + return channel_count == 1 || channel_count == 2; +} + +bool IsValidMultiStreamChannelCount(u32 channel_count) { + return channel_count > 0 && channel_count <= OpusStreamCountMax; +} + +bool IsValidSampleRate(u32 sample_rate) { + return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 || + sample_rate == 24'000 || sample_rate == 48'000; +} + +bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { + return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 && + stereo_stream_count <= total_stream_count && + total_stream_count + stereo_stream_count <= channel_count; +} + +} // namespace + +OpusDecoderManager::OpusDecoderManager(Core::System& system_) + : system{system_}, hardware_opus{system} { + for (u32 i = 0; i < MaxChannels; i++) { + required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i); + } +} + +Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) { + OpusParametersEx ex{ + .sample_rate = params.sample_rate, + .channel_count = params.channel_count, + .use_large_frame_size = false, + }; + R_RETURN(GetWorkBufferSizeExEx(ex, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) { + R_RETURN(GetWorkBufferSizeExEx(params, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) { + R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount); + R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); + + auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]}; + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + work_buffer_size += + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); + out_size = work_buffer_size + 0x600; + R_SUCCEED(); +} + +Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, + u64& out_size) { + OpusMultiStreamParametersEx ex{ + .sample_rate = params.sample_rate, + .channel_count = params.channel_count, + .total_stream_count = params.total_stream_count, + .stereo_stream_count = params.stereo_stream_count, + .use_large_frame_size = false, + .mappings = {}, + }; + R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, + u64& out_size) { + R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, + u64& out_size) { + R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount); + R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); + R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count, + params.stereo_stream_count), + ResultInvalidOpusSampleRate); + + auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream( + params.total_stream_count, params.stereo_stream_count)}; + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64); + work_buffer_size += + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); + out_size = work_buffer_size; + R_SUCCEED(); +} + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/decoder_manager.h b/src/audio_core/opus/decoder_manager.h index 466e1967b..70ebc4bab 100644 --- a/src/audio_core/opus/decoder_manager.h +++ b/src/audio_core/opus/decoder_manager.h @@ -1,38 +1,38 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "audio_core/opus/hardware_opus.h"
-#include "audio_core/opus/parameters.h"
-#include "common/common_types.h"
-#include "core/hle/service/audio/errors.h"
-
-namespace Core {
-class System;
-}
-
-namespace AudioCore::OpusDecoder {
-
-class OpusDecoderManager {
-public:
- OpusDecoderManager(Core::System& system);
-
- HardwareOpus& GetHardwareOpus() {
- return hardware_opus;
- }
-
- Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
- Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
- Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
- Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
- Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
- Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
-
-private:
- Core::System& system;
- HardwareOpus hardware_opus;
- std::array<u64, MaxChannels> required_workbuffer_sizes{};
-};
-
-} // namespace AudioCore::OpusDecoder
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "audio_core/opus/hardware_opus.h" +#include "audio_core/opus/parameters.h" +#include "common/common_types.h" +#include "core/hle/service/audio/errors.h" + +namespace Core { +class System; +} + +namespace AudioCore::OpusDecoder { + +class OpusDecoderManager { +public: + OpusDecoderManager(Core::System& system); + + HardwareOpus& GetHardwareOpus() { + return hardware_opus; + } + + Result GetWorkBufferSize(OpusParameters& params, u64& out_size); + Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size); + Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size); + Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size); + Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size); + Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size); + +private: + Core::System& system; + HardwareOpus hardware_opus; + std::array<u64, MaxChannels> required_workbuffer_sizes{}; +}; + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/hardware_opus.cpp b/src/audio_core/opus/hardware_opus.cpp index d6544dcb0..5ff71ab2d 100644 --- a/src/audio_core/opus/hardware_opus.cpp +++ b/src/audio_core/opus/hardware_opus.cpp @@ -1,241 +1,241 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <array>
-
-#include "audio_core/audio_core.h"
-#include "audio_core/opus/hardware_opus.h"
-#include "core/core.h"
-
-namespace AudioCore::OpusDecoder {
-namespace {
-using namespace Service::Audio;
-
-static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
- s32 error{static_cast<s32>(error_code)};
- ASSERT(error <= OPUS_OK);
- switch (error) {
- case OPUS_ALLOC_FAIL:
- R_THROW(ResultLibOpusAllocFail);
- case OPUS_INVALID_STATE:
- R_THROW(ResultLibOpusInvalidState);
- case OPUS_UNIMPLEMENTED:
- R_THROW(ResultLibOpusUnimplemented);
- case OPUS_INVALID_PACKET:
- R_THROW(ResultLibOpusInvalidPacket);
- case OPUS_INTERNAL_ERROR:
- R_THROW(ResultLibOpusInternalError);
- case OPUS_BUFFER_TOO_SMALL:
- R_THROW(ResultBufferTooSmall);
- case OPUS_BAD_ARG:
- R_THROW(ResultLibOpusBadArg);
- case OPUS_OK:
- R_RETURN(ResultSuccess);
- }
- UNREACHABLE();
-}
-
-} // namespace
-
-HardwareOpus::HardwareOpus(Core::System& system_)
- : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
- opus_decoder.SetSharedMemory(shared_memory);
-}
-
-u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
- if (!opus_decoder.IsRunning()) {
- return 0;
- }
- std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = channel;
- opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize);
- auto msg = opus_decoder.Receive(ADSP::Direction::Host);
- if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
- return 0;
- }
- return shared_memory.dsp_return_data[0];
-}
-
-u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
- std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = total_stream_count;
- shared_memory.host_send_data[1] = stereo_stream_count;
- opus_decoder.Send(ADSP::Direction::DSP,
- ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream);
- auto msg = opus_decoder.Receive(ADSP::Direction::Host);
- if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
- return 0;
- }
- return shared_memory.dsp_return_data[0];
-}
-
-Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
- u64 buffer_size) {
- std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = (u64)buffer;
- shared_memory.host_send_data[1] = buffer_size;
- shared_memory.host_send_data[2] = sample_rate;
- shared_memory.host_send_data[3] = channel_count;
-
- opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject);
- auto msg = opus_decoder.Receive(ADSP::Direction::Host);
- if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg);
- R_THROW(ResultInvalidOpusDSPReturnCode);
- }
-
- R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
-}
-
-Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
- u32 total_stream_count,
- u32 stereo_stream_count, void* mappings,
- void* buffer, u64 buffer_size) {
- std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = (u64)buffer;
- shared_memory.host_send_data[1] = buffer_size;
- shared_memory.host_send_data[2] = sample_rate;
- shared_memory.host_send_data[3] = channel_count;
- shared_memory.host_send_data[4] = total_stream_count;
- shared_memory.host_send_data[5] = stereo_stream_count;
-
- ASSERT(channel_count <= MaxChannels);
- std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
-
- opus_decoder.Send(ADSP::Direction::DSP,
- ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject);
- auto msg = opus_decoder.Receive(ADSP::Direction::Host);
- if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg);
- R_THROW(ResultInvalidOpusDSPReturnCode);
- }
-
- R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
-}
-
-Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
- std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = (u64)buffer;
- shared_memory.host_send_data[1] = buffer_size;
-
- opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject);
- auto msg = opus_decoder.Receive(ADSP::Direction::Host);
- ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK,
- "Expected Opus shutdown code {}, got {}",
- ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
-
- R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
-}
-
-Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
- std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = (u64)buffer;
- shared_memory.host_send_data[1] = buffer_size;
-
- opus_decoder.Send(ADSP::Direction::DSP,
- ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject);
- auto msg = opus_decoder.Receive(ADSP::Direction::Host);
- ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK,
- "Expected Opus shutdown code {}, got {}",
- ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
-
- R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
-}
-
-Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
- u64 output_data_size, u32 channel_count, void* input_data,
- u64 input_data_size, void* buffer, u64& out_time_taken,
- bool reset) {
- std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = (u64)buffer;
- shared_memory.host_send_data[1] = (u64)input_data;
- shared_memory.host_send_data[2] = input_data_size;
- shared_memory.host_send_data[3] = (u64)output_data;
- shared_memory.host_send_data[4] = output_data_size;
- shared_memory.host_send_data[5] = 0;
- shared_memory.host_send_data[6] = reset;
-
- opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved);
- auto msg = opus_decoder.Receive(ADSP::Direction::Host);
- if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg);
- R_THROW(ResultInvalidOpusDSPReturnCode);
- }
-
- auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
- if (error_code == OPUS_OK) {
- out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
- out_time_taken = 1000 * shared_memory.dsp_return_data[2];
- }
- R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
-}
-
-Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
- u64 output_data_size, u32 channel_count,
- void* input_data, u64 input_data_size,
- void* buffer, u64& out_time_taken,
- bool reset) {
- std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = (u64)buffer;
- shared_memory.host_send_data[1] = (u64)input_data;
- shared_memory.host_send_data[2] = input_data_size;
- shared_memory.host_send_data[3] = (u64)output_data;
- shared_memory.host_send_data[4] = output_data_size;
- shared_memory.host_send_data[5] = 0;
- shared_memory.host_send_data[6] = reset;
-
- opus_decoder.Send(ADSP::Direction::DSP,
- ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
- auto msg = opus_decoder.Receive(ADSP::Direction::Host);
- if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
- R_THROW(ResultInvalidOpusDSPReturnCode);
- }
-
- auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
- if (error_code == OPUS_OK) {
- out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
- out_time_taken = 1000 * shared_memory.dsp_return_data[2];
- }
- R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
-}
-
-Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
- std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = (u64)buffer;
- shared_memory.host_send_data[1] = buffer_size;
-
- opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
- auto msg = opus_decoder.Receive(ADSP::Direction::Host);
- if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::MapMemoryOK, msg);
- R_THROW(ResultInvalidOpusDSPReturnCode);
- }
- R_SUCCEED();
-}
-
-Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
- std::scoped_lock l{mutex};
- shared_memory.host_send_data[0] = (u64)buffer;
- shared_memory.host_send_data[1] = buffer_size;
-
- opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
- auto msg = opus_decoder.Receive(ADSP::Direction::Host);
- if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
- LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
- ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
- R_THROW(ResultInvalidOpusDSPReturnCode);
- }
- R_SUCCEED();
-}
-
-} // namespace AudioCore::OpusDecoder
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <array> + +#include "audio_core/audio_core.h" +#include "audio_core/opus/hardware_opus.h" +#include "core/core.h" + +namespace AudioCore::OpusDecoder { +namespace { +using namespace Service::Audio; + +static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) { + s32 error{static_cast<s32>(error_code)}; + ASSERT(error <= OPUS_OK); + switch (error) { + case OPUS_ALLOC_FAIL: + R_THROW(ResultLibOpusAllocFail); + case OPUS_INVALID_STATE: + R_THROW(ResultLibOpusInvalidState); + case OPUS_UNIMPLEMENTED: + R_THROW(ResultLibOpusUnimplemented); + case OPUS_INVALID_PACKET: + R_THROW(ResultLibOpusInvalidPacket); + case OPUS_INTERNAL_ERROR: + R_THROW(ResultLibOpusInternalError); + case OPUS_BUFFER_TOO_SMALL: + R_THROW(ResultBufferTooSmall); + case OPUS_BAD_ARG: + R_THROW(ResultLibOpusBadArg); + case OPUS_OK: + R_RETURN(ResultSuccess); + } + UNREACHABLE(); +} + +} // namespace + +HardwareOpus::HardwareOpus(Core::System& system_) + : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} { + opus_decoder.SetSharedMemory(shared_memory); +} + +u64 HardwareOpus::GetWorkBufferSize(u32 channel) { + if (!opus_decoder.IsRunning()) { + return 0; + } + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = channel; + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg); + return 0; + } + return shared_memory.dsp_return_data[0]; +} + +u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = total_stream_count; + shared_memory.host_send_data[1] = stereo_stream_count; + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg); + return 0; + } + return shared_memory.dsp_return_data[0]; +} + +Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, + u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + shared_memory.host_send_data[2] = sample_rate; + shared_memory.host_send_data[3] = channel_count; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, + u32 total_stream_count, + u32 stereo_stream_count, void* mappings, + void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + shared_memory.host_send_data[2] = sample_rate; + shared_memory.host_send_data[3] = channel_count; + shared_memory.host_send_data[4] = total_stream_count; + shared_memory.host_send_data[5] = stereo_stream_count; + + ASSERT(channel_count <= MaxChannels); + std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); + + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, + "Expected Opus shutdown code {}, got {}", + ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, + "Expected Opus shutdown code {}, got {}", + ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, + u64 output_data_size, u32 channel_count, void* input_data, + u64 input_data_size, void* buffer, u64& out_time_taken, + bool reset) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = (u64)input_data; + shared_memory.host_send_data[2] = input_data_size; + shared_memory.host_send_data[3] = (u64)output_data; + shared_memory.host_send_data[4] = output_data_size; + shared_memory.host_send_data[5] = 0; + shared_memory.host_send_data[6] = reset; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; + if (error_code == OPUS_OK) { + out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); + out_time_taken = 1000 * shared_memory.dsp_return_data[2]; + } + R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); +} + +Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, + u64 output_data_size, u32 channel_count, + void* input_data, u64 input_data_size, + void* buffer, u64& out_time_taken, + bool reset) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = (u64)input_data; + shared_memory.host_send_data[2] = input_data_size; + shared_memory.host_send_data[3] = (u64)output_data; + shared_memory.host_send_data[4] = output_data_size; + shared_memory.host_send_data[5] = 0; + shared_memory.host_send_data[6] = reset; + + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; + if (error_code == OPUS_OK) { + out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); + out_time_taken = 1000 * shared_memory.dsp_return_data[2]; + } + R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); +} + +Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::MapMemoryOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + R_SUCCEED(); +} + +Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::UnmapMemoryOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + R_SUCCEED(); +} + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/hardware_opus.h b/src/audio_core/opus/hardware_opus.h index 7013a6b40..b10184baa 100644 --- a/src/audio_core/opus/hardware_opus.h +++ b/src/audio_core/opus/hardware_opus.h @@ -1,45 +1,45 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <mutex>
-#include <opus.h>
-
-#include "audio_core/adsp/apps/opus/opus_decoder.h"
-#include "audio_core/adsp/apps/opus/shared_memory.h"
-#include "audio_core/adsp/mailbox.h"
-#include "core/hle/service/audio/errors.h"
-
-namespace AudioCore::OpusDecoder {
-class HardwareOpus {
-public:
- HardwareOpus(Core::System& system);
-
- u64 GetWorkBufferSize(u32 channel);
- u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
-
- Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
- u64 buffer_size);
- Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
- u32 totaL_stream_count, u32 stereo_stream_count,
- void* mappings, void* buffer, u64 buffer_size);
- Result ShutdownDecodeObject(void* buffer, u64 buffer_size);
- Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size);
- Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size,
- u32 channel_count, void* input_data, u64 input_data_size, void* buffer,
- u64& out_time_taken, bool reset);
- Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
- u64 output_data_size, u32 channel_count,
- void* input_data, u64 input_data_size, void* buffer,
- u64& out_time_taken, bool reset);
- Result MapMemory(void* buffer, u64 buffer_size);
- Result UnmapMemory(void* buffer, u64 buffer_size);
-
-private:
- Core::System& system;
- std::mutex mutex;
- ADSP::OpusDecoder::OpusDecoder& opus_decoder;
- ADSP::OpusDecoder::SharedMemory shared_memory;
-};
-} // namespace AudioCore::OpusDecoder
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> +#include <opus.h> + +#include "audio_core/adsp/apps/opus/opus_decoder.h" +#include "audio_core/adsp/apps/opus/shared_memory.h" +#include "audio_core/adsp/mailbox.h" +#include "core/hle/service/audio/errors.h" + +namespace AudioCore::OpusDecoder { +class HardwareOpus { +public: + HardwareOpus(Core::System& system); + + u64 GetWorkBufferSize(u32 channel); + u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); + + Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, + u64 buffer_size); + Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, + u32 totaL_stream_count, u32 stereo_stream_count, + void* mappings, void* buffer, u64 buffer_size); + Result ShutdownDecodeObject(void* buffer, u64 buffer_size); + Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size); + Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size, + u32 channel_count, void* input_data, u64 input_data_size, void* buffer, + u64& out_time_taken, bool reset); + Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, + u64 output_data_size, u32 channel_count, + void* input_data, u64 input_data_size, void* buffer, + u64& out_time_taken, bool reset); + Result MapMemory(void* buffer, u64 buffer_size); + Result UnmapMemory(void* buffer, u64 buffer_size); + +private: + Core::System& system; + std::mutex mutex; + ADSP::OpusDecoder::OpusDecoder& opus_decoder; + ADSP::OpusDecoder::SharedMemory shared_memory; +}; +} // namespace AudioCore::OpusDecoder diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 8a1861051..e216eb3de 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -120,6 +120,8 @@ add_library(common STATIC socket_types.h spin_lock.cpp spin_lock.h + stb.cpp + stb.h steady_clock.cpp steady_clock.h stream.cpp @@ -208,6 +210,8 @@ if (MSVC) /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data /we4800 # Implicit conversion from 'type' to bool. Possible information loss ) +else() + set_source_files_properties(stb.cpp PROPERTIES COMPILE_OPTIONS "-Wno-implicit-fallthrough;-Wno-missing-declarations;-Wno-missing-field-initializers") endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") @@ -223,7 +227,7 @@ endif() create_target_directory_groups(common) -target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads) +target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile stb::headers Threads::Threads) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle) if (ANDROID) diff --git a/src/common/arm64/native_clock.cpp b/src/common/arm64/native_clock.cpp index 88fdba527..f437d7187 100644 --- a/src/common/arm64/native_clock.cpp +++ b/src/common/arm64/native_clock.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#ifdef ANDROID +#include <sys/system_properties.h> +#endif #include "common/arm64/native_clock.h" namespace Common::Arm64 { @@ -65,7 +68,23 @@ bool NativeClock::IsNative() const { u64 NativeClock::GetHostCNTFRQ() { u64 cntfrq_el0 = 0; - asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); + std::string_view board{""}; +#ifdef ANDROID + char buffer[PROP_VALUE_MAX]; + int len{__system_property_get("ro.product.board", buffer)}; + board = std::string_view(buffer, static_cast<size_t>(len)); +#endif + if (board == "s5e9925") { // Exynos 2200 + cntfrq_el0 = 25600000; + } else if (board == "exynos2100") { // Exynos 2100 + cntfrq_el0 = 26000000; + } else if (board == "exynos9810") { // Exynos 9810 + cntfrq_el0 = 26000000; + } else if (board == "s5e8825") { // Exynos 1280 + cntfrq_el0 = 26000000; + } else { + asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); + } return cntfrq_el0; } diff --git a/src/common/fs/fs_android.cpp b/src/common/fs/fs_android.cpp index 298a79bac..1dd826a4a 100644 --- a/src/common/fs/fs_android.cpp +++ b/src/common/fs/fs_android.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/fs/fs_android.h" +#include "common/string_util.h" namespace Common::FS::Android { @@ -28,28 +29,35 @@ void RegisterCallbacks(JNIEnv* env, jclass clazz) { env->GetJavaVM(&g_jvm); native_library = clazz; +#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ + F(JMethodID, JMethodName, Signature) #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ F(JMethodID, JMethodName, Signature) #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ F(JMethodID, JMethodName, Signature) #define F(JMethodID, JMethodName, Signature) \ JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature); + ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) ANDROID_STORAGE_FUNCTIONS(FS) #undef F #undef FS #undef FR +#undef FH } void UnRegisterCallbacks() { +#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) #define F(JMethodID) JMethodID = nullptr; + ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) ANDROID_STORAGE_FUNCTIONS(FS) #undef F #undef FS #undef FR +#undef FH } bool IsContentUri(const std::string& path) { @@ -95,4 +103,29 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) #undef F #undef FR +#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ + F(FunctionName, JMethodID, Caller) +#define F(FunctionName, JMethodID, Caller) \ + std::string FunctionName(const std::string& filepath) { \ + if (JMethodID == nullptr) { \ + return 0; \ + } \ + auto env = GetEnvForThread(); \ + jstring j_filepath = env->NewStringUTF(filepath.c_str()); \ + jstring j_return = \ + static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \ + if (!j_return) { \ + return {}; \ + } \ + const jchar* jchars = env->GetStringChars(j_return, nullptr); \ + const jsize length = env->GetStringLength(j_return); \ + const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \ + const std::string converted_string = Common::UTF16ToUTF8(string_view); \ + env->ReleaseStringChars(j_return, jchars); \ + return converted_string; \ + } +ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) +#undef F +#undef FH + } // namespace Common::FS::Android diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h index b441c2a12..2c9234313 100644 --- a/src/common/fs/fs_android.h +++ b/src/common/fs/fs_android.h @@ -17,19 +17,28 @@ "(Ljava/lang/String;)Z") \ V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z") +#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \ + V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \ + "(Ljava/lang/String;)Ljava/lang/String;") \ + V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \ + "(Ljava/lang/String;)Ljava/lang/String;") + namespace Common::FS::Android { static JavaVM* g_jvm = nullptr; static jclass native_library = nullptr; +#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) #define F(JMethodID) static jmethodID JMethodID = nullptr; +ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) ANDROID_STORAGE_FUNCTIONS(FS) #undef F #undef FS #undef FR +#undef FH enum class OpenMode { Read, @@ -62,4 +71,10 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) #undef F #undef FR +#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName) +#define F(FunctionName) std::string FunctionName(const std::string& filepath); +ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) +#undef F +#undef FH + } // namespace Common::FS::Android diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h index 441c8af97..bcf447089 100644 --- a/src/common/fs/fs_paths.h +++ b/src/common/fs/fs_paths.h @@ -13,6 +13,7 @@ #define AMIIBO_DIR "amiibo" #define CACHE_DIR "cache" #define CONFIG_DIR "config" +#define CRASH_DUMPS_DIR "crash_dumps" #define DUMP_DIR "dump" #define KEYS_DIR "keys" #define LOAD_DIR "load" diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 0abd81a45..c3a81f9a9 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -119,6 +119,7 @@ public: GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR); GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache); GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config); + GenerateYuzuPath(YuzuPath::CrashDumpsDir, yuzu_path / CRASH_DUMPS_DIR); GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR); GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR); @@ -400,6 +401,16 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se } std::string_view GetParentPath(std::string_view path) { + if (path.empty()) { + return path; + } + +#ifdef ANDROID + if (path[0] != '/') { + std::string path_string{path}; + return FS::Android::GetParentDirectory(path_string); + } +#endif const auto name_bck_index = path.rfind('\\'); const auto name_fwd_index = path.rfind('/'); std::size_t name_index; diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index 63801c924..2874ea738 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h @@ -15,6 +15,7 @@ enum class YuzuPath { AmiiboDir, // Where Amiibo backups are stored. CacheDir, // Where cached filesystem data is stored. ConfigDir, // Where config files are stored. + CrashDumpsDir, // Where crash dumps are stored. DumpDir, // Where dumped data is stored. KeysDir, // Where key files are stored. LoadDir, // Where cheat/mod files are stored. diff --git a/src/common/nvidia_flags.cpp b/src/common/nvidia_flags.cpp index 7ed7690ee..fa3747782 100644 --- a/src/common/nvidia_flags.cpp +++ b/src/common/nvidia_flags.cpp @@ -25,6 +25,7 @@ void ConfigureNvidiaEnvironmentFlags() { void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str())); void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1")); + void(_putenv("__GL_THREADED_OPTIMIZATIONS=1")); #endif } diff --git a/src/common/settings.h b/src/common/settings.h index 236e33bee..9317075f7 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -505,7 +505,6 @@ struct Values { linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false}; Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers", Category::Debugging}; - Setting<bool> create_crash_dumps{linkage, false, "create_crash_dumps", Category::Debugging}; Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging}; // Miscellaneous diff --git a/src/common/stb.cpp b/src/common/stb.cpp new file mode 100644 index 000000000..d3b16665d --- /dev/null +++ b/src/common/stb.cpp @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION + +#include "common/stb.h" diff --git a/src/common/stb.h b/src/common/stb.h new file mode 100644 index 000000000..e5c197c11 --- /dev/null +++ b/src/common/stb.h @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <stb_image.h> +#include <stb_image_resize.h> +#include <stb_image_write.h> diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 4c7aba3f5..72c481798 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -14,6 +14,10 @@ #include <windows.h> #endif +#ifdef ANDROID +#include <common/fs/fs_android.h> +#endif + namespace Common { /// Make a string lowercase @@ -63,6 +67,14 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ if (full_path.empty()) return false; +#ifdef ANDROID + if (full_path[0] != '/') { + *_pPath = Common::FS::Android::GetParentDirectory(full_path); + *_pFilename = Common::FS::Android::GetFilename(full_path); + return true; + } +#endif + std::size_t dir_end = full_path.find_last_of("/" // windows needs the : included for something like just "C:" to be considered a directory #ifdef _WIN32 diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 0c012f094..5e27dde58 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -86,9 +86,9 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt std::map<std::string, Symbols::Symbols> symbols; for (const auto& module : modules) { - symbols.insert_or_assign( - module.second, Symbols::GetSymbols(module.first, system.ApplicationMemory(), - system.ApplicationProcess()->Is64BitProcess())); + symbols.insert_or_assign(module.second, + Symbols::GetSymbols(module.first, system.ApplicationMemory(), + system.ApplicationProcess()->Is64Bit())); } for (auto& entry : out) { diff --git a/src/core/core.cpp b/src/core/core.cpp index d7e2efbd7..14d6c8c27 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -309,17 +309,10 @@ struct System::Impl { telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); - // Create a resource limit for the process. - const auto physical_memory_size = - kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); - auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size); - // Create the process. auto main_process = Kernel::KProcess::Create(system.Kernel()); - ASSERT(Kernel::KProcess::Initialize(main_process, system, "main", - Kernel::KProcess::ProcessType::Userland, resource_limit) - .IsSuccess()); Kernel::KProcess::Register(system.Kernel(), main_process); + kernel.AppendNewProcess(main_process); kernel.MakeApplicationProcess(main_process); const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); if (load_result != Loader::ResultStatus::Success) { @@ -418,6 +411,7 @@ struct System::Impl { services->KillNVNFlinger(); } kernel.CloseServices(); + kernel.ShutdownCores(); services.reset(); service_manager.reset(); cheat_engine.reset(); @@ -429,7 +423,6 @@ struct System::Impl { gpu_core.reset(); host1x_core.reset(); perf_stats.reset(); - kernel.ShutdownCores(); cpu_manager.Shutdown(); debugger.reset(); kernel.Shutdown(); diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index a1589fecb..0e270eb50 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -258,20 +258,20 @@ private: Kernel::KScopedSchedulerLock sl{system.Kernel()}; // Put all threads to sleep on next scheduler round. - for (auto* thread : ThreadList()) { - thread->RequestSuspend(Kernel::SuspendType::Debug); + for (auto& thread : ThreadList()) { + thread.RequestSuspend(Kernel::SuspendType::Debug); } } void ResumeEmulation(Kernel::KThread* except = nullptr) { // Wake up all threads. - for (auto* thread : ThreadList()) { - if (thread == except) { + for (auto& thread : ThreadList()) { + if (std::addressof(thread) == except) { continue; } - thread->SetStepState(Kernel::StepState::NotStepping); - thread->Resume(Kernel::SuspendType::Debug); + thread.SetStepState(Kernel::StepState::NotStepping); + thread.Resume(Kernel::SuspendType::Debug); } } @@ -283,13 +283,17 @@ private: } void UpdateActiveThread() { - const auto& threads{ThreadList()}; - if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) { - state->active_thread = threads.front(); + auto& threads{ThreadList()}; + for (auto& thread : threads) { + if (std::addressof(thread) == state->active_thread) { + // Thread is still alive, no need to update. + return; + } } + state->active_thread = std::addressof(threads.front()); } - const std::list<Kernel::KThread*>& ThreadList() { + Kernel::KProcess::ThreadList& ThreadList() { return system.ApplicationProcess()->GetThreadList(); } diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 2076aa8a2..6f5f5156b 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -109,7 +109,7 @@ static std::string EscapeXML(std::string_view data) { GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) : DebuggerFrontend(backend_), system{system_} { - if (system.ApplicationProcess()->Is64BitProcess()) { + if (system.ApplicationProcess()->Is64Bit()) { arch = std::make_unique<GDBStubA64>(); } else { arch = std::make_unique<GDBStubA32>(); @@ -446,10 +446,10 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) { // See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory, - const Kernel::KThread* thread) { + const Kernel::KThread& thread) { // Read thread type from TLS - const VAddr tls_thread_type{memory.Read32(thread->GetTlsAddress() + 0x1fc)}; - const VAddr argument_thread_type{thread->GetArgument()}; + const VAddr tls_thread_type{memory.Read32(thread.GetTlsAddress() + 0x1fc)}; + const VAddr argument_thread_type{thread.GetArgument()}; if (argument_thread_type && tls_thread_type != argument_thread_type) { // Probably not created by nnsdk, no name available. @@ -477,10 +477,10 @@ static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& } static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory, - const Kernel::KThread* thread) { + const Kernel::KThread& thread) { // Read thread type from TLS - const VAddr tls_thread_type{memory.Read64(thread->GetTlsAddress() + 0x1f8)}; - const VAddr argument_thread_type{thread->GetArgument()}; + const VAddr tls_thread_type{memory.Read64(thread.GetTlsAddress() + 0x1f8)}; + const VAddr argument_thread_type{thread.GetArgument()}; if (argument_thread_type && tls_thread_type != argument_thread_type) { // Probably not created by nnsdk, no name available. @@ -508,16 +508,16 @@ static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& } static std::optional<std::string> GetThreadName(Core::System& system, - const Kernel::KThread* thread) { - if (system.ApplicationProcess()->Is64BitProcess()) { + const Kernel::KThread& thread) { + if (system.ApplicationProcess()->Is64Bit()) { return GetNameFromThreadType64(system.ApplicationMemory(), thread); } else { return GetNameFromThreadType32(system.ApplicationMemory(), thread); } } -static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { - switch (thread->GetWaitReasonForDebugging()) { +static std::string_view GetThreadWaitReason(const Kernel::KThread& thread) { + switch (thread.GetWaitReasonForDebugging()) { case Kernel::ThreadWaitReasonForDebugging::Sleep: return "Sleep"; case Kernel::ThreadWaitReasonForDebugging::IPC: @@ -535,8 +535,8 @@ static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { } } -static std::string GetThreadState(const Kernel::KThread* thread) { - switch (thread->GetState()) { +static std::string GetThreadState(const Kernel::KThread& thread) { + switch (thread.GetState()) { case Kernel::ThreadState::Initialized: return "Initialized"; case Kernel::ThreadState::Waiting: @@ -604,7 +604,7 @@ void GDBStub::HandleQuery(std::string_view command) { const auto& threads = system.ApplicationProcess()->GetThreadList(); std::vector<std::string> thread_ids; for (const auto& thread : threads) { - thread_ids.push_back(fmt::format("{:x}", thread->GetThreadId())); + thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId())); } SendReply(fmt::format("m{}", fmt::join(thread_ids, ","))); } else if (command.starts_with("sThreadInfo")) { @@ -616,14 +616,14 @@ void GDBStub::HandleQuery(std::string_view command) { buffer += "<threads>"; const auto& threads = system.ApplicationProcess()->GetThreadList(); - for (const auto* thread : threads) { + for (const auto& thread : threads) { auto thread_name{GetThreadName(system, thread)}; if (!thread_name) { - thread_name = fmt::format("Thread {:d}", thread->GetThreadId()); + thread_name = fmt::format("Thread {:d}", thread.GetThreadId()); } buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)", - thread->GetThreadId(), thread->GetActiveCore(), + thread.GetThreadId(), thread.GetActiveCore(), EscapeXML(*thread_name), GetThreadState(thread)); } @@ -850,10 +850,10 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { } Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { - const auto& threads{system.ApplicationProcess()->GetThreadList()}; - for (auto* thread : threads) { - if (thread->GetThreadId() == thread_id) { - return thread; + auto& threads{system.ApplicationProcess()->GetThreadList()}; + for (auto& thread : threads) { + if (thread.GetThreadId() == thread_id) { + return std::addressof(thread); } } diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 8e291ff67..763a44fee 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -104,16 +104,16 @@ Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) { } /*static*/ ProgramMetadata ProgramMetadata::GetDefault() { - // Allow use of cores 0~3 and thread priorities 1~63. - constexpr u32 default_thread_info_capability = 0x30007F7; + // Allow use of cores 0~3 and thread priorities 16~63. + constexpr u32 default_thread_info_capability = 0x30043F7; ProgramMetadata result; result.LoadManual( true /*is_64_bit*/, FileSys::ProgramAddressSpaceType::Is39Bit /*address_space*/, - 0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x00100000 /*main_thread_stack_size*/, - 0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/, - 0x1FE00000 /*system_resource_size*/, {default_thread_info_capability} /*capabilities*/); + 0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x100000 /*main_thread_stack_size*/, + 0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/, 0 /*system_resource_size*/, + {default_thread_info_capability} /*capabilities*/); return result; } diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 9f8e74b13..76ee97d78 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -73,6 +73,9 @@ public: u64 GetFilesystemPermissions() const; u32 GetSystemResourceSize() const; const KernelCapabilityDescriptors& GetKernelCapabilities() const; + const std::array<u8, 0x10>& GetName() const { + return npdm_header.application_name; + } void Print() const; @@ -164,14 +167,14 @@ private: u32_le unk_size_2; }; - Header npdm_header; - AciHeader aci_header; - AcidHeader acid_header; + Header npdm_header{}; + AciHeader aci_header{}; + AcidHeader acid_header{}; - FileAccessControl acid_file_access; - FileAccessHeader aci_file_access; + FileAccessControl acid_file_access{}; + FileAccessHeader aci_file_access{}; - KernelCapabilityDescriptors aci_kernel_capabilities; + KernelCapabilityDescriptors aci_kernel_capabilities{}; }; } // namespace FileSys diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 1c580de57..1eb1f439a 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp @@ -35,13 +35,14 @@ struct RomFSHeader { static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); struct DirectoryEntry { + u32_le parent; u32_le sibling; u32_le child_dir; u32_le child_file; u32_le hash; u32_le name_length; }; -static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size."); +static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size."); struct FileEntry { u32_le parent; @@ -64,25 +65,22 @@ std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offs return {entry, string}; } -void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset, - u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) { - while (true) { +void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset, + u32 this_file_offset, std::shared_ptr<VectorVfsDirectory>& parent) { + while (this_file_offset != ROMFS_ENTRY_EMPTY) { auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); parent->AddFile(std::make_shared<OffsetVfsFile>( file, entry.first.size, entry.first.offset + data_offset, entry.second)); - if (entry.first.sibling == ROMFS_ENTRY_EMPTY) - break; - this_file_offset = entry.first.sibling; } } -void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset, +void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size_t file_offset, std::size_t data_offset, u32 this_dir_offset, - std::shared_ptr<VectorVfsDirectory> parent) { - while (true) { + std::shared_ptr<VectorVfsDirectory>& parent) { + while (this_dir_offset != ROMFS_ENTRY_EMPTY) { auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); auto current = std::make_shared<VectorVfsDirectory>( std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second); @@ -97,14 +95,12 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file } parent->AddDirectory(current); - if (entry.first.sibling == ROMFS_ENTRY_EMPTY) - break; this_dir_offset = entry.first.sibling; } } } // Anonymous namespace -VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { +VirtualDir ExtractRomFS(VirtualFile file) { RomFSHeader header{}; if (file->ReadObject(&header) != sizeof(RomFSHeader)) return nullptr; @@ -113,27 +109,17 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { return nullptr; const u64 file_offset = header.file_meta.offset; - const u64 dir_offset = header.directory_meta.offset + 4; - - auto root = - std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, - file->GetName(), file->GetContainingDirectory()); - - ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root); + const u64 dir_offset = header.directory_meta.offset; - VirtualDir out = std::move(root); + auto root_container = std::make_shared<VectorVfsDirectory>(); - if (type == RomFSExtractionType::SingleDiscard) - return out->GetSubdirectories().front(); + ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container); - while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) { - if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" && - type == RomFSExtractionType::Truncated) - break; - out = out->GetSubdirectories().front(); + if (auto root = root_container->GetSubdirectory(""); root) { + return std::make_shared<CachedVfsDirectory>(std::move(root)); } - return std::make_shared<CachedVfsDirectory>(std::move(out)); + return nullptr; } VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index 5d7f0c2a8..b75ff1aad 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h @@ -7,16 +7,9 @@ namespace FileSys { -enum class RomFSExtractionType { - Full, // Includes data directory - Truncated, // Traverses into data directory - SingleDiscard, // Traverses into the first subdirectory of root -}; - // Converts a RomFS binary blob to VFS Filesystem // Returns nullptr on failure -VirtualDir ExtractRomFS(VirtualFile file, - RomFSExtractionType type = RomFSExtractionType::Truncated); +VirtualDir ExtractRomFS(VirtualFile file); // Converts a VFS filesystem into a RomFS binary // Returns nullptr on failure diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 2af3f06fc..b08a71446 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -96,18 +96,7 @@ void EmulatedController::ReloadFromSettings() { } controller.color_values = {}; - controller.colors_state.fullkey = { - .body = GetNpadColor(player.body_color_left), - .button = GetNpadColor(player.button_color_left), - }; - controller.colors_state.left = { - .body = GetNpadColor(player.body_color_left), - .button = GetNpadColor(player.button_color_left), - }; - controller.colors_state.right = { - .body = GetNpadColor(player.body_color_right), - .button = GetNpadColor(player.button_color_right), - }; + ReloadColorsFromSettings(); ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs); @@ -128,6 +117,30 @@ void EmulatedController::ReloadFromSettings() { ReloadInput(); } +void EmulatedController::ReloadColorsFromSettings() { + const auto player_index = NpadIdTypeToIndex(npad_id_type); + const auto& player = Settings::values.players.GetValue()[player_index]; + + // Avoid updating colors if overridden by physical controller + if (controller.color_values[LeftIndex].body != 0 && + controller.color_values[RightIndex].body != 0) { + return; + } + + controller.colors_state.fullkey = { + .body = GetNpadColor(player.body_color_left), + .button = GetNpadColor(player.button_color_left), + }; + controller.colors_state.left = { + .body = GetNpadColor(player.body_color_left), + .button = GetNpadColor(player.button_color_left), + }; + controller.colors_state.right = { + .body = GetNpadColor(player.body_color_right), + .button = GetNpadColor(player.button_color_right), + }; +} + void EmulatedController::LoadDevices() { // TODO(german77): Use more buttons to detect the correct device const auto left_joycon = button_params[Settings::NativeButton::DRight]; @@ -1091,30 +1104,30 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac bool is_charging = false; bool is_powered = false; - NpadBatteryLevel battery_level = 0; + NpadBatteryLevel battery_level = NpadBatteryLevel::Empty; switch (controller.battery_values[index]) { case Common::Input::BatteryLevel::Charging: is_charging = true; is_powered = true; - battery_level = 6; + battery_level = NpadBatteryLevel::Full; break; case Common::Input::BatteryLevel::Medium: - battery_level = 6; + battery_level = NpadBatteryLevel::High; break; case Common::Input::BatteryLevel::Low: - battery_level = 4; + battery_level = NpadBatteryLevel::Low; break; case Common::Input::BatteryLevel::Critical: - battery_level = 2; + battery_level = NpadBatteryLevel::Critical; break; case Common::Input::BatteryLevel::Empty: - battery_level = 0; + battery_level = NpadBatteryLevel::Empty; break; case Common::Input::BatteryLevel::None: case Common::Input::BatteryLevel::Full: default: is_powered = true; - battery_level = 8; + battery_level = NpadBatteryLevel::Full; break; } diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index d4500583e..ea18c2343 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -253,6 +253,9 @@ public: /// Overrides current mapped devices with the stored configuration and reloads all input devices void ReloadFromSettings(); + /// Updates current colors with the ones stored in the configuration + void ReloadColorsFromSettings(); + /// Saves the current mapped configuration void SaveCurrentConfig(); diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 00beb40dd..7ba75a50c 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h @@ -302,6 +302,15 @@ enum class TouchScreenModeForNx : u8 { Heat2, }; +// This is nn::hid::system::NpadBatteryLevel +enum class NpadBatteryLevel : u32 { + Empty, + Critical, + Low, + High, + Full, +}; + // This is nn::hid::NpadStyleTag struct NpadStyleTag { union { @@ -385,16 +394,12 @@ struct NpadGcTriggerState { }; static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); -// This is nn::hid::system::NpadBatteryLevel -using NpadBatteryLevel = u32; -static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size"); - // This is nn::hid::system::NpadPowerInfo struct NpadPowerInfo { bool is_powered{}; bool is_charging{}; INSERT_PADDING_BYTES(0x6); - NpadBatteryLevel battery_level{8}; + NpadBatteryLevel battery_level{NpadBatteryLevel::Full}; }; static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp index 4cfdf4558..59364efa1 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp @@ -8,7 +8,11 @@ #include "core/hle/kernel/board/nintendo/nx/k_system_control.h" #include "core/hle/kernel/board/nintendo/nx/secure_monitor.h" +#include "core/hle/kernel/k_memory_manager.h" +#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_trace.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc_results.h" namespace Kernel::Board::Nintendo::Nx { @@ -30,6 +34,8 @@ constexpr const std::size_t RequiredNonSecureSystemMemorySize = constexpr const std::size_t RequiredNonSecureSystemMemorySizeWithFatal = RequiredNonSecureSystemMemorySize + impl::RequiredNonSecureSystemMemorySizeViFatal; +constexpr const std::size_t SecureAlignment = 128_KiB; + namespace { using namespace Common::Literals; @@ -183,4 +189,57 @@ u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) { return GenerateUniformRange(min, max, GenerateRandomU64); } +size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) { + if (pool == static_cast<u32>(KMemoryManager::Pool::Applet)) { + return 0; + } else { + // return KSystemControlBase::CalculateRequiredSecureMemorySize(size, pool); + return size; + } +} + +Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size, + u32 pool) { + // Applet secure memory is handled separately. + UNIMPLEMENTED_IF(pool == static_cast<u32>(KMemoryManager::Pool::Applet)); + + // Ensure the size is aligned. + const size_t alignment = + (pool == static_cast<u32>(KMemoryManager::Pool::System) ? PageSize : SecureAlignment); + R_UNLESS(Common::IsAligned(size, alignment), ResultInvalidSize); + + // Allocate the memory. + const size_t num_pages = size / PageSize; + const KPhysicalAddress paddr = kernel.MemoryManager().AllocateAndOpenContinuous( + num_pages, alignment / PageSize, + KMemoryManager::EncodeOption(static_cast<KMemoryManager::Pool>(pool), + KMemoryManager::Direction::FromFront)); + R_UNLESS(paddr != 0, ResultOutOfMemory); + + // Ensure we don't leak references to the memory on error. + ON_RESULT_FAILURE { + kernel.MemoryManager().Close(paddr, num_pages); + }; + + // We succeeded. + *out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr); + R_SUCCEED(); +} + +void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size, + u32 pool) { + // Applet secure memory is handled separately. + UNIMPLEMENTED_IF(pool == static_cast<u32>(KMemoryManager::Pool::Applet)); + + // Ensure the size is aligned. + const size_t alignment = + (pool == static_cast<u32>(KMemoryManager::Pool::System) ? PageSize : SecureAlignment); + ASSERT(Common::IsAligned(GetInteger(address), alignment)); + ASSERT(Common::IsAligned(size, alignment)); + + // Close the secure region's pages. + kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address), + size / PageSize); +} + } // namespace Kernel::Board::Nintendo::Nx diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h index b477e8193..ff1feec70 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h @@ -4,6 +4,11 @@ #pragma once #include "core/hle/kernel/k_typed_address.h" +#include "core/hle/result.h" + +namespace Kernel { +class KernelCore; +} namespace Kernel::Board::Nintendo::Nx { @@ -25,8 +30,16 @@ public: static std::size_t GetMinimumNonSecureSystemPoolSize(); }; + // Randomness. static u64 GenerateRandomRange(u64 min, u64 max); static u64 GenerateRandomU64(); + + // Secure Memory. + static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool); + static Result AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size, + u32 pool); + static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size, + u32 pool); }; } // namespace Kernel::Board::Nintendo::Nx diff --git a/src/core/hle/kernel/k_capabilities.h b/src/core/hle/kernel/k_capabilities.h index de766c811..ebd4eedb1 100644 --- a/src/core/hle/kernel/k_capabilities.h +++ b/src/core/hle/kernel/k_capabilities.h @@ -200,8 +200,8 @@ private: RawCapabilityValue raw; BitField<0, 15, CapabilityType> id; - BitField<15, 4, u32> major_version; - BitField<19, 13, u32> minor_version; + BitField<15, 4, u32> minor_version; + BitField<19, 13, u32> major_version; }; union HandleTable { diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index efbac0e6a..7633a51fb 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -107,12 +107,12 @@ KConditionVariable::KConditionVariable(Core::System& system) KConditionVariable::~KConditionVariable() = default; -Result KConditionVariable::SignalToAddress(KProcessAddress addr) { - KThread* owner_thread = GetCurrentThreadPointer(m_kernel); +Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress addr) { + KThread* owner_thread = GetCurrentThreadPointer(kernel); // Signal the address. { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Remove waiter thread. bool has_waiters{}; @@ -133,7 +133,7 @@ Result KConditionVariable::SignalToAddress(KProcessAddress addr) { // Write the value to userspace. Result result{ResultSuccess}; - if (WriteToUser(m_kernel, addr, std::addressof(next_value))) [[likely]] { + if (WriteToUser(kernel, addr, std::addressof(next_value))) [[likely]] { result = ResultSuccess; } else { result = ResultInvalidCurrentMemory; @@ -148,28 +148,28 @@ Result KConditionVariable::SignalToAddress(KProcessAddress addr) { } } -Result KConditionVariable::WaitForAddress(Handle handle, KProcessAddress addr, u32 value) { - KThread* cur_thread = GetCurrentThreadPointer(m_kernel); - ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(m_kernel); +Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, + u32 value) { + KThread* cur_thread = GetCurrentThreadPointer(kernel); + ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel); // Wait for the address. KThread* owner_thread{}; { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Check if the thread should terminate. R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested); // Read the tag from userspace. u32 test_tag{}; - R_UNLESS(ReadFromUser(m_kernel, std::addressof(test_tag), addr), - ResultInvalidCurrentMemory); + R_UNLESS(ReadFromUser(kernel, std::addressof(test_tag), addr), ResultInvalidCurrentMemory); // If the tag isn't the handle (with wait mask), we're done. R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask)); // Get the lock owner thread. - owner_thread = GetCurrentProcess(m_kernel) + owner_thread = GetCurrentProcess(kernel) .GetHandleTable() .GetObjectWithoutPseudoHandle<KThread>(handle) .ReleasePointerUnsafe(); diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h index 8c2f3ae51..2620c8e39 100644 --- a/src/core/hle/kernel/k_condition_variable.h +++ b/src/core/hle/kernel/k_condition_variable.h @@ -24,11 +24,12 @@ public: explicit KConditionVariable(Core::System& system); ~KConditionVariable(); - // Arbitration - Result SignalToAddress(KProcessAddress addr); - Result WaitForAddress(Handle handle, KProcessAddress addr, u32 value); + // Arbitration. + static Result SignalToAddress(KernelCore& kernel, KProcessAddress addr); + static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, + u32 value); - // Condition variable + // Condition variable. void Signal(u64 cv_key, s32 count); Result Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout); diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp index fe6a20168..22d79569a 100644 --- a/src/core/hle/kernel/k_interrupt_manager.cpp +++ b/src/core/hle/kernel/k_interrupt_manager.cpp @@ -22,7 +22,7 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) { KScopedSchedulerLock sl{kernel}; // Pin the current thread. - process->PinCurrentThread(core_id); + process->PinCurrentThread(); // Set the interrupt flag for the thread. GetCurrentThread(kernel).SetInterruptFlag(); diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index 637558e10..cdc5572d8 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp @@ -11,6 +11,7 @@ #include "core/hle/kernel/initial_process.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_page_group.h" +#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" @@ -168,11 +169,37 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage } Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) { - UNREACHABLE(); + const u32 pool_index = static_cast<u32>(pool); + + // Lock the pool. + KScopedLightLock lk(m_pool_locks[pool_index]); + + // Check that we don't already have an optimized process. + R_UNLESS(!m_has_optimized_process[pool_index], ResultBusy); + + // Set the optimized process id. + m_optimized_process_ids[pool_index] = process_id; + m_has_optimized_process[pool_index] = true; + + // Clear the management area for the optimized process. + for (auto* manager = this->GetFirstManager(pool, Direction::FromFront); manager != nullptr; + manager = this->GetNextManager(manager, Direction::FromFront)) { + manager->InitializeOptimizedMemory(m_system.Kernel()); + } + + R_SUCCEED(); } void KMemoryManager::FinalizeOptimizedMemory(u64 process_id, Pool pool) { - UNREACHABLE(); + const u32 pool_index = static_cast<u32>(pool); + + // Lock the pool. + KScopedLightLock lk(m_pool_locks[pool_index]); + + // If the process was optimized, clear it. + if (m_has_optimized_process[pool_index] && m_optimized_process_ids[pool_index] == process_id) { + m_has_optimized_process[pool_index] = false; + } } KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, @@ -207,7 +234,7 @@ KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, siz // Maintain the optimized memory bitmap, if we should. if (m_has_optimized_process[static_cast<size_t>(pool)]) { - UNIMPLEMENTED(); + chosen_manager->TrackUnoptimizedAllocation(m_system.Kernel(), allocated_block, num_pages); } // Open the first reference to the pages. @@ -255,7 +282,8 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, // Maintain the optimized memory bitmap, if we should. if (unoptimized) { - UNIMPLEMENTED(); + cur_manager->TrackUnoptimizedAllocation(m_system.Kernel(), allocated_block, + pages_per_alloc); } num_pages -= pages_per_alloc; @@ -358,8 +386,8 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 // Process part or all of the block. const size_t cur_pages = std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); - any_new = - manager.ProcessOptimizedAllocation(cur_address, cur_pages, fill_pattern); + any_new = manager.ProcessOptimizedAllocation(m_system.Kernel(), cur_address, + cur_pages, fill_pattern); // Advance. cur_address += cur_pages * PageSize; @@ -382,7 +410,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 // Track some or all of the current pages. const size_t cur_pages = std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); - manager.TrackOptimizedAllocation(cur_address, cur_pages); + manager.TrackOptimizedAllocation(m_system.Kernel(), cur_address, cur_pages); // Advance. cur_address += cur_pages * PageSize; @@ -427,17 +455,86 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size, return total_management_size; } -void KMemoryManager::Impl::TrackUnoptimizedAllocation(KPhysicalAddress block, size_t num_pages) { - UNREACHABLE(); +void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) { + auto optimize_pa = + KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); + auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); + + std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); } -void KMemoryManager::Impl::TrackOptimizedAllocation(KPhysicalAddress block, size_t num_pages) { - UNREACHABLE(); +void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, + size_t num_pages) { + auto optimize_pa = + KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); + auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); + + // Get the range we're tracking. + size_t offset = this->GetPageOffset(block); + const size_t last = offset + num_pages - 1; + + // Track. + while (offset <= last) { + // Mark the page as not being optimized-allocated. + optimize_map[offset / Common::BitSize<u64>()] &= + ~(u64(1) << (offset % Common::BitSize<u64>())); + + offset++; + } +} + +void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, + size_t num_pages) { + auto optimize_pa = + KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); + auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); + + // Get the range we're tracking. + size_t offset = this->GetPageOffset(block); + const size_t last = offset + num_pages - 1; + + // Track. + while (offset <= last) { + // Mark the page as being optimized-allocated. + optimize_map[offset / Common::BitSize<u64>()] |= + (u64(1) << (offset % Common::BitSize<u64>())); + + offset++; + } } -bool KMemoryManager::Impl::ProcessOptimizedAllocation(KPhysicalAddress block, size_t num_pages, - u8 fill_pattern) { - UNREACHABLE(); +bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, + size_t num_pages, u8 fill_pattern) { + auto& device_memory = kernel.System().DeviceMemory(); + auto optimize_pa = + KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); + auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa); + + // We want to return whether any pages were newly allocated. + bool any_new = false; + + // Get the range we're processing. + size_t offset = this->GetPageOffset(block); + const size_t last = offset + num_pages - 1; + + // Process. + while (offset <= last) { + // Check if the page has been optimized-allocated before. + if ((optimize_map[offset / Common::BitSize<u64>()] & + (u64(1) << (offset % Common::BitSize<u64>()))) == 0) { + // If not, it's new. + any_new = true; + + // Fill the page. + auto* ptr = device_memory.GetPointer<u8>(m_heap.GetAddress()); + std::memset(ptr + offset * PageSize, fill_pattern, PageSize); + } + + offset++; + } + + // Return the number of pages we processed. + return any_new; } size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) { diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h index 7e4b41319..c5a487af9 100644 --- a/src/core/hle/kernel/k_memory_manager.h +++ b/src/core/hle/kernel/k_memory_manager.h @@ -216,14 +216,14 @@ private: m_heap.SetInitialUsedSize(reserved_size); } - void InitializeOptimizedMemory() { - UNIMPLEMENTED(); - } + void InitializeOptimizedMemory(KernelCore& kernel); - void TrackUnoptimizedAllocation(KPhysicalAddress block, size_t num_pages); - void TrackOptimizedAllocation(KPhysicalAddress block, size_t num_pages); + void TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, + size_t num_pages); + void TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, size_t num_pages); - bool ProcessOptimizedAllocation(KPhysicalAddress block, size_t num_pages, u8 fill_pattern); + bool ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, + size_t num_pages, u8 fill_pattern); constexpr Pool GetPool() const { return m_pool; diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 217ccbae3..1d47bdf6b 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -82,14 +82,14 @@ public: using namespace Common::Literals; -constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) { +constexpr size_t GetAddressSpaceWidthFromType(Svc::CreateProcessFlag as_type) { switch (as_type) { - case FileSys::ProgramAddressSpaceType::Is32Bit: - case FileSys::ProgramAddressSpaceType::Is32BitNoMap: + case Svc::CreateProcessFlag::AddressSpace32Bit: + case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias: return 32; - case FileSys::ProgramAddressSpaceType::Is36Bit: + case Svc::CreateProcessFlag::AddressSpace64BitDeprecated: return 36; - case FileSys::ProgramAddressSpaceType::Is39Bit: + case Svc::CreateProcessFlag::AddressSpace64Bit: return 39; default: ASSERT(false); @@ -105,7 +105,7 @@ KPageTable::KPageTable(Core::System& system_) KPageTable::~KPageTable() = default; -Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, +Result KPageTable::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_addr, size_t code_size, KSystemResource* system_resource, @@ -133,7 +133,7 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type ASSERT(code_addr + code_size - 1 <= end - 1); // Adjust heap/alias size if we don't have an alias region - if (as_type == FileSys::ProgramAddressSpaceType::Is32BitNoMap) { + if (as_type == Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias) { heap_region_size += alias_region_size; alias_region_size = 0; } diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 3d64b6fb0..66f16faaf 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -63,7 +63,7 @@ public: explicit KPageTable(Core::System& system_); ~KPageTable(); - Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, + Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_addr, size_t code_size, KSystemResource* system_resource, KResourceLimit* resource_limit, @@ -400,7 +400,7 @@ public: constexpr size_t GetAliasCodeRegionSize() const { return m_alias_code_region_end - m_alias_code_region_start; } - size_t GetNormalMemorySize() { + size_t GetNormalMemorySize() const { KScopedLightLock lk(m_general_lock); return GetHeapSize() + m_mapped_physical_memory_size; } diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 7fa34d693..1f4b0755d 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -1,515 +1,598 @@ -// SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <algorithm> -#include <bitset> -#include <ctime> -#include <memory> #include <random> -#include "common/alignment.h" -#include "common/assert.h" -#include "common/logging/log.h" #include "common/scope_exit.h" #include "common/settings.h" #include "core/core.h" -#include "core/file_sys/program_metadata.h" -#include "core/hle/kernel/code_set.h" -#include "core/hle/kernel/k_memory_block_manager.h" -#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_resource_limit.h" -#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory_info.h" -#include "core/hle/kernel/k_thread.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/svc_results.h" -#include "core/memory.h" +#include "core/hle/kernel/k_thread_local_page.h" +#include "core/hle/kernel/k_thread_queue.h" +#include "core/hle/kernel/k_worker_task_manager.h" namespace Kernel { -namespace { -/** - * Sets up the primary application thread - * - * @param system The system instance to create the main thread under. - * @param owner_process The parent process for the main thread - * @param priority The priority to give the main thread - */ -void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, - KProcessAddress stack_top) { - const KProcessAddress entry_point = owner_process.GetEntryPoint(); - ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1)); - - KThread* thread = KThread::Create(system.Kernel()); - SCOPE_EXIT({ thread->Close(); }); - - ASSERT(KThread::InitializeUserThread(system, thread, entry_point, 0, stack_top, priority, - owner_process.GetIdealCoreId(), - std::addressof(owner_process)) - .IsSuccess()); - - // Register 1 must be a handle to the main thread - Handle thread_handle{}; - owner_process.GetHandleTable().Add(std::addressof(thread_handle), thread); - - thread->GetContext32().cpu_registers[0] = 0; - thread->GetContext64().cpu_registers[0] = 0; - thread->GetContext32().cpu_registers[1] = thread_handle; - thread->GetContext64().cpu_registers[1] = thread_handle; - - if (system.DebuggerEnabled()) { - thread->RequestSuspend(SuspendType::Debug); - } - // Run our thread. - void(thread->Run()); -} -} // Anonymous namespace +namespace { -Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, - ProcessType type, KResourceLimit* res_limit) { - auto& kernel = system.Kernel(); +Result TerminateChildren(KernelCore& kernel, KProcess* process, + const KThread* thread_to_not_terminate) { + // Request that all children threads terminate. + { + KScopedLightLock proc_lk(process->GetListLock()); + KScopedSchedulerLock sl(kernel); + + if (thread_to_not_terminate != nullptr && + process->GetPinnedThread(GetCurrentCoreId(kernel)) == thread_to_not_terminate) { + // NOTE: Here Nintendo unpins the current thread instead of the thread_to_not_terminate. + // This is valid because the only caller which uses non-nullptr as argument uses + // GetCurrentThreadPointer(), but it's still notable because it seems incorrect at + // first glance. + process->UnpinCurrentThread(); + } - process->name = std::move(process_name); - process->m_resource_limit = res_limit; - process->m_system_resource_address = 0; - process->m_state = State::Created; - process->m_program_id = 0; - process->m_process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() - : kernel.CreateNewUserProcessID(); - process->m_capabilities.InitializeForMetadatalessProcess(); - process->m_is_initialized = true; + auto& thread_list = process->GetThreadList(); + for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { + if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) { + if (thread->GetState() != ThreadState::Terminated) { + thread->RequestTerminate(); + } + } + } + } - std::mt19937 rng(Settings::values.rng_seed_enabled ? Settings::values.rng_seed.GetValue() - : static_cast<u32>(std::time(nullptr))); - std::uniform_int_distribution<u64> distribution; - std::generate(process->m_random_entropy.begin(), process->m_random_entropy.end(), - [&] { return distribution(rng); }); + // Wait for all children threads to terminate. + while (true) { + // Get the next child. + KThread* cur_child = nullptr; + { + KScopedLightLock proc_lk(process->GetListLock()); + + auto& thread_list = process->GetThreadList(); + for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { + if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) { + if (thread->GetState() != ThreadState::Terminated) { + if (thread->Open()) { + cur_child = thread; + break; + } + } + } + } + } - kernel.AppendNewProcess(process); + // If we didn't find any non-terminated children, we're done. + if (cur_child == nullptr) { + break; + } - // Clear remaining fields. - process->m_num_running_threads = 0; - process->m_is_signaled = false; - process->m_exception_thread = nullptr; - process->m_is_suspended = false; - process->m_schedule_count = 0; - process->m_is_handle_table_initialized = false; - process->m_is_hbl = false; + // Terminate and close the thread. + SCOPE_EXIT({ cur_child->Close(); }); - // Open a reference to the resource limit. - process->m_resource_limit->Open(); + if (const Result terminate_result = cur_child->Terminate(); + ResultTerminationRequested == terminate_result) { + R_THROW(terminate_result); + } + } R_SUCCEED(); } -void KProcess::DoWorkerTaskImpl() { - UNIMPLEMENTED(); -} - -KResourceLimit* KProcess::GetResourceLimit() const { - return m_resource_limit; -} +class ThreadQueueImplForKProcessEnterUserException final : public KThreadQueue { +private: + KThread** m_exception_thread; -void KProcess::IncrementRunningThreadCount() { - ASSERT(m_num_running_threads.load() >= 0); - ++m_num_running_threads; -} +public: + explicit ThreadQueueImplForKProcessEnterUserException(KernelCore& kernel, KThread** t) + : KThreadQueue(kernel), m_exception_thread(t) {} -void KProcess::DecrementRunningThreadCount() { - ASSERT(m_num_running_threads.load() > 0); + virtual void EndWait(KThread* waiting_thread, Result wait_result) override { + // Set the exception thread. + *m_exception_thread = waiting_thread; - if (const auto prev = m_num_running_threads--; prev == 1) { - // TODO(bunnei): Process termination to be implemented when multiprocess is supported. + // Invoke the base end wait handler. + KThreadQueue::EndWait(waiting_thread, wait_result); } -} -u64 KProcess::GetTotalPhysicalMemoryAvailable() { - const u64 capacity{m_resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) + - m_page_table.GetNormalMemorySize() + GetSystemResourceSize() + m_image_size + - m_main_thread_stack_size}; - if (const auto pool_size = m_kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); - capacity != pool_size) { - LOG_WARNING(Kernel, "capacity {} != application pool size {}", capacity, pool_size); - } - if (capacity < m_memory_usage_capacity) { - return capacity; + virtual void CancelWait(KThread* waiting_thread, Result wait_result, + bool cancel_timer_task) override { + // Remove the thread as a waiter on its mutex owner. + waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread); + + // Invoke the base cancel wait handler. + KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); } - return m_memory_usage_capacity; -} +}; -u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() { - return this->GetTotalPhysicalMemoryAvailable() - this->GetSystemResourceSize(); +void GenerateRandom(std::span<u64> out_random) { + std::mt19937 rng(Settings::values.rng_seed_enabled ? Settings::values.rng_seed.GetValue() + : static_cast<u32>(std::time(nullptr))); + std::uniform_int_distribution<u64> distribution; + std::generate(out_random.begin(), out_random.end(), [&] { return distribution(rng); }); } -u64 KProcess::GetTotalPhysicalMemoryUsed() { - return m_image_size + m_main_thread_stack_size + m_page_table.GetNormalMemorySize() + - this->GetSystemResourceSize(); -} +} // namespace -u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() { - return this->GetTotalPhysicalMemoryUsed() - this->GetSystemResourceSize(); -} +void KProcess::Finalize() { + // Delete the process local region. + this->DeleteThreadLocalRegion(m_plr_address); -bool KProcess::ReleaseUserException(KThread* thread) { - KScopedSchedulerLock sl{m_kernel}; + // Get the used memory size. + const size_t used_memory_size = this->GetUsedNonSystemUserPhysicalMemorySize(); - if (m_exception_thread == thread) { - m_exception_thread = nullptr; + // Finalize the page table. + m_page_table.Finalize(); - // Remove waiter thread. - bool has_waiters{}; - if (KThread* next = thread->RemoveKernelWaiterByKey( - std::addressof(has_waiters), - reinterpret_cast<uintptr_t>(std::addressof(m_exception_thread))); - next != nullptr) { - next->EndWait(ResultSuccess); + // Finish using our system resource. + if (m_system_resource) { + if (m_system_resource->IsSecureResource()) { + // Finalize optimized memory. If memory wasn't optimized, this is a no-op. + m_kernel.MemoryManager().FinalizeOptimizedMemory(this->GetId(), m_memory_pool); } - KScheduler::SetSchedulerUpdateNeeded(m_kernel); - - return true; - } else { - return false; + m_system_resource->Close(); + m_system_resource = nullptr; } -} - -void KProcess::PinCurrentThread(s32 core_id) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - // Get the current thread. - KThread* cur_thread = - m_kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread(); + // Free all shared memory infos. + { + auto it = m_shared_memory_list.begin(); + while (it != m_shared_memory_list.end()) { + KSharedMemoryInfo* info = std::addressof(*it); + KSharedMemory* shmem = info->GetSharedMemory(); - // If the thread isn't terminated, pin it. - if (!cur_thread->IsTerminationRequested()) { - // Pin it. - this->PinThread(core_id, cur_thread); - cur_thread->Pin(core_id); + while (!info->Close()) { + shmem->Close(); + } + shmem->Close(); - // An update is needed. - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + it = m_shared_memory_list.erase(it); + KSharedMemoryInfo::Free(m_kernel, info); + } } -} -void KProcess::UnpinCurrentThread(s32 core_id) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - - // Get the current thread. - KThread* cur_thread = - m_kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread(); + // Our thread local page list must be empty at this point. + ASSERT(m_partially_used_tlp_tree.empty()); + ASSERT(m_fully_used_tlp_tree.empty()); - // Unpin it. - cur_thread->Unpin(); - this->UnpinThread(core_id, cur_thread); + // Release memory to the resource limit. + if (m_resource_limit != nullptr) { + ASSERT(used_memory_size >= m_memory_release_hint); + m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, used_memory_size, + used_memory_size - m_memory_release_hint); + m_resource_limit->Close(); + } - // An update is needed. - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + // Perform inherited finalization. + KSynchronizationObject::Finalize(); } -void KProcess::UnpinThread(KThread* thread) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - - // Get the thread's core id. - const auto core_id = thread->GetActiveCore(); +Result KProcess::Initialize(const Svc::CreateProcessParameter& params, KResourceLimit* res_limit, + bool is_real) { + // TODO: remove this special case + if (is_real) { + // Create and clear the process local region. + R_TRY(this->CreateThreadLocalRegion(std::addressof(m_plr_address))); + this->GetMemory().ZeroBlock(m_plr_address, Svc::ThreadLocalRegionSize); + } - // Unpin it. - this->UnpinThread(core_id, thread); - thread->Unpin(); + // Copy in the name from parameters. + static_assert(sizeof(params.name) < sizeof(m_name)); + std::memcpy(m_name.data(), params.name.data(), sizeof(params.name)); + m_name[sizeof(params.name)] = 0; + + // Set misc fields. + m_state = State::Created; + m_main_thread_stack_size = 0; + m_used_kernel_memory_size = 0; + m_ideal_core_id = 0; + m_flags = params.flags; + m_version = params.version; + m_program_id = params.program_id; + m_code_address = params.code_address; + m_code_size = params.code_num_pages * PageSize; + m_is_application = True(params.flags & Svc::CreateProcessFlag::IsApplication); + + // Set thread fields. + for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + m_running_threads[i] = nullptr; + m_pinned_threads[i] = nullptr; + m_running_thread_idle_counts[i] = 0; + m_running_thread_switch_counts[i] = 0; + } - // An update is needed. - KScheduler::SetSchedulerUpdateNeeded(m_kernel); -} + // Set max memory based on address space type. + switch ((params.flags & Svc::CreateProcessFlag::AddressSpaceMask)) { + case Svc::CreateProcessFlag::AddressSpace32Bit: + case Svc::CreateProcessFlag::AddressSpace64BitDeprecated: + case Svc::CreateProcessFlag::AddressSpace64Bit: + m_max_process_memory = m_page_table.GetHeapRegionSize(); + break; + case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias: + m_max_process_memory = m_page_table.GetHeapRegionSize() + m_page_table.GetAliasRegionSize(); + break; + default: + UNREACHABLE(); + } -Result KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] KProcessAddress address, - [[maybe_unused]] size_t size) { - // Lock ourselves, to prevent concurrent access. - KScopedLightLock lk(m_state_lock); + // Generate random entropy. + GenerateRandom(m_entropy); - // Try to find an existing info for the memory. - KSharedMemoryInfo* shemen_info = nullptr; - const auto iter = std::find_if( - m_shared_memory_list.begin(), m_shared_memory_list.end(), - [shmem](const KSharedMemoryInfo* info) { return info->GetSharedMemory() == shmem; }); - if (iter != m_shared_memory_list.end()) { - shemen_info = *iter; - } + // Clear remaining fields. + m_num_running_threads = 0; + m_num_process_switches = 0; + m_num_thread_switches = 0; + m_num_fpu_switches = 0; + m_num_supervisor_calls = 0; + m_num_ipc_messages = 0; - if (shemen_info == nullptr) { - shemen_info = KSharedMemoryInfo::Allocate(m_kernel); - R_UNLESS(shemen_info != nullptr, ResultOutOfMemory); + m_is_signaled = false; + m_exception_thread = nullptr; + m_is_suspended = false; + m_memory_release_hint = 0; + m_schedule_count = 0; + m_is_handle_table_initialized = false; - shemen_info->Initialize(shmem); - m_shared_memory_list.push_back(shemen_info); - } + // Open a reference to our resource limit. + m_resource_limit = res_limit; + m_resource_limit->Open(); - // Open a reference to the shared memory and its info. - shmem->Open(); - shemen_info->Open(); + // We're initialized! + m_is_initialized = true; R_SUCCEED(); } -void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] KProcessAddress address, - [[maybe_unused]] size_t size) { - // Lock ourselves, to prevent concurrent access. - KScopedLightLock lk(m_state_lock); +Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPageGroup& pg, + std::span<const u32> caps, KResourceLimit* res_limit, + KMemoryManager::Pool pool, bool immortal) { + ASSERT(res_limit != nullptr); + ASSERT((params.code_num_pages * PageSize) / PageSize == + static_cast<size_t>(params.code_num_pages)); + + // Set members. + m_memory_pool = pool; + m_is_default_application_system_resource = false; + m_is_immortal = immortal; + + // Setup our system resource. + if (const size_t system_resource_num_pages = params.system_resource_num_pages; + system_resource_num_pages != 0) { + // Create a secure system resource. + KSecureSystemResource* secure_resource = KSecureSystemResource::Create(m_kernel); + R_UNLESS(secure_resource != nullptr, ResultOutOfResource); + + ON_RESULT_FAILURE { + secure_resource->Close(); + }; + + // Initialize the secure resource. + R_TRY(secure_resource->Initialize(system_resource_num_pages * PageSize, res_limit, + m_memory_pool)); + + // Set our system resource. + m_system_resource = secure_resource; + } else { + // Use the system-wide system resource. + const bool is_app = True(params.flags & Svc::CreateProcessFlag::IsApplication); + m_system_resource = std::addressof(is_app ? m_kernel.GetAppSystemResource() + : m_kernel.GetSystemSystemResource()); - KSharedMemoryInfo* shemen_info = nullptr; - const auto iter = std::find_if( - m_shared_memory_list.begin(), m_shared_memory_list.end(), - [shmem](const KSharedMemoryInfo* info) { return info->GetSharedMemory() == shmem; }); - if (iter != m_shared_memory_list.end()) { - shemen_info = *iter; + m_is_default_application_system_resource = is_app; + + // Open reference to the system resource. + m_system_resource->Open(); } - ASSERT(shemen_info != nullptr); + // Ensure we clean up our secure resource, if we fail. + ON_RESULT_FAILURE { + m_system_resource->Close(); + m_system_resource = nullptr; + }; - if (shemen_info->Close()) { - m_shared_memory_list.erase(iter); - KSharedMemoryInfo::Free(m_kernel, shemen_info); + // Setup page table. + { + const auto as_type = params.flags & Svc::CreateProcessFlag::AddressSpaceMask; + const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); + const bool enable_das_merge = + False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); + R_TRY(m_page_table.InitializeForProcess( + as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, + params.code_num_pages * PageSize, m_system_resource, res_limit, this->GetMemory())); } + ON_RESULT_FAILURE_2 { + m_page_table.Finalize(); + }; - // Close a reference to the shared memory. - shmem->Close(); -} + // Ensure we can insert the code region. + R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize, + KMemoryState::Code), + ResultInvalidMemoryRegion); -void KProcess::RegisterThread(KThread* thread) { - KScopedLightLock lk{m_list_lock}; + // Map the code region. + R_TRY(m_page_table.MapPageGroup(params.code_address, pg, KMemoryState::Code, + KMemoryPermission::KernelRead)); - m_thread_list.push_back(thread); -} + // Initialize capabilities. + R_TRY(m_capabilities.InitializeForKip(caps, std::addressof(m_page_table))); -void KProcess::UnregisterThread(KThread* thread) { - KScopedLightLock lk{m_list_lock}; + // Initialize the process id. + m_process_id = m_kernel.CreateNewUserProcessID(); + ASSERT(InitialProcessIdMin <= m_process_id); + ASSERT(m_process_id <= InitialProcessIdMax); - m_thread_list.remove(thread); -} + // Initialize the rest of the process. + R_TRY(this->Initialize(params, res_limit, true)); -u64 KProcess::GetFreeThreadCount() const { - if (m_resource_limit != nullptr) { - const auto current_value = - m_resource_limit->GetCurrentValue(LimitableResource::ThreadCountMax); - const auto limit_value = m_resource_limit->GetLimitValue(LimitableResource::ThreadCountMax); - return limit_value - current_value; - } else { - return 0; - } + // We succeeded! + R_SUCCEED(); } -Result KProcess::Reset() { - // Lock the process and the scheduler. - KScopedLightLock lk(m_state_lock); - KScopedSchedulerLock sl{m_kernel}; +Result KProcess::Initialize(const Svc::CreateProcessParameter& params, + std::span<const u32> user_caps, KResourceLimit* res_limit, + KMemoryManager::Pool pool) { + ASSERT(res_limit != nullptr); - // Validate that we're in a state that we can reset. - R_UNLESS(m_state != State::Terminated, ResultInvalidState); - R_UNLESS(m_is_signaled, ResultInvalidState); + // Set members. + m_memory_pool = pool; + m_is_default_application_system_resource = false; + m_is_immortal = false; - // Clear signaled. - m_is_signaled = false; - R_SUCCEED(); -} + // Get the memory sizes. + const size_t code_num_pages = params.code_num_pages; + const size_t system_resource_num_pages = params.system_resource_num_pages; + const size_t code_size = code_num_pages * PageSize; + const size_t system_resource_size = system_resource_num_pages * PageSize; -Result KProcess::SetActivity(ProcessActivity activity) { - // Lock ourselves and the scheduler. - KScopedLightLock lk{m_state_lock}; - KScopedLightLock list_lk{m_list_lock}; - KScopedSchedulerLock sl{m_kernel}; + // Reserve memory for our code resource. + KScopedResourceReservation memory_reservation( + res_limit, Svc::LimitableResource::PhysicalMemoryMax, code_size); + R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); - // Validate our state. - R_UNLESS(m_state != State::Terminating, ResultInvalidState); - R_UNLESS(m_state != State::Terminated, ResultInvalidState); + // Setup our system resource. + if (system_resource_num_pages != 0) { + // Create a secure system resource. + KSecureSystemResource* secure_resource = KSecureSystemResource::Create(m_kernel); + R_UNLESS(secure_resource != nullptr, ResultOutOfResource); - // Either pause or resume. - if (activity == ProcessActivity::Paused) { - // Verify that we're not suspended. - R_UNLESS(!m_is_suspended, ResultInvalidState); + ON_RESULT_FAILURE { + secure_resource->Close(); + }; - // Suspend all threads. - for (auto* thread : this->GetThreadList()) { - thread->RequestSuspend(SuspendType::Process); - } + // Initialize the secure resource. + R_TRY(secure_resource->Initialize(system_resource_size, res_limit, m_memory_pool)); + + // Set our system resource. + m_system_resource = secure_resource; - // Set ourselves as suspended. - this->SetSuspended(true); } else { - ASSERT(activity == ProcessActivity::Runnable); + // Use the system-wide system resource. + const bool is_app = True(params.flags & Svc::CreateProcessFlag::IsApplication); + m_system_resource = std::addressof(is_app ? m_kernel.GetAppSystemResource() + : m_kernel.GetSystemSystemResource()); - // Verify that we're suspended. - R_UNLESS(m_is_suspended, ResultInvalidState); + m_is_default_application_system_resource = is_app; - // Resume all threads. - for (auto* thread : this->GetThreadList()) { - thread->Resume(SuspendType::Process); - } + // Open reference to the system resource. + m_system_resource->Open(); + } - // Set ourselves as resumed. - this->SetSuspended(false); + // Ensure we clean up our secure resource, if we fail. + ON_RESULT_FAILURE { + m_system_resource->Close(); + m_system_resource = nullptr; + }; + + // Setup page table. + { + const auto as_type = params.flags & Svc::CreateProcessFlag::AddressSpaceMask; + const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); + const bool enable_das_merge = + False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); + R_TRY(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, + !enable_aslr, pool, params.code_address, code_size, + m_system_resource, res_limit, this->GetMemory())); + } + ON_RESULT_FAILURE_2 { + m_page_table.Finalize(); + }; + + // Ensure we can insert the code region. + R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code), + ResultInvalidMemoryRegion); + + // Map the code region. + R_TRY(m_page_table.MapPages(params.code_address, code_num_pages, KMemoryState::Code, + KMemoryPermission::KernelRead | KMemoryPermission::NotMapped)); + + // Initialize capabilities. + R_TRY(m_capabilities.InitializeForUser(user_caps, std::addressof(m_page_table))); + + // Initialize the process id. + m_process_id = m_kernel.CreateNewUserProcessID(); + ASSERT(ProcessIdMin <= m_process_id); + ASSERT(m_process_id <= ProcessIdMax); + + // If we should optimize memory allocations, do so. + if (m_system_resource->IsSecureResource() && + True(params.flags & Svc::CreateProcessFlag::OptimizeMemoryAllocation)) { + R_TRY(m_kernel.MemoryManager().InitializeOptimizedMemory(m_process_id, pool)); } + // Initialize the rest of the process. + R_TRY(this->Initialize(params, res_limit, true)); + + // We succeeded, so commit our memory reservation. + memory_reservation.Commit(); R_SUCCEED(); } -Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, - bool is_hbl) { - m_program_id = metadata.GetTitleID(); - m_ideal_core = metadata.GetMainThreadCore(); - m_is_64bit_process = metadata.Is64BitProgram(); - m_system_resource_size = metadata.GetSystemResourceSize(); - m_image_size = code_size; - m_is_hbl = is_hbl; +void KProcess::DoWorkerTaskImpl() { + // Terminate child threads. + TerminateChildren(m_kernel, this, nullptr); - if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit) { - // For 39-bit processes, the ASLR region starts at 0x800'0000 and is ~512GiB large. - // However, some (buggy) programs/libraries like skyline incorrectly depend on the - // existence of ASLR pages before the entry point, so we will adjust the load address - // to point to about 2GiB into the ASLR region. - m_code_address = 0x8000'0000; - } else { - // All other processes can be mapped at the beginning of the code region. - if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is36Bit) { - m_code_address = 0x800'0000; - } else { - m_code_address = 0x20'0000; - } + // Finalize the handle table, if we're not immortal. + if (!m_is_immortal && m_is_handle_table_initialized) { + this->FinalizeHandleTable(); } - KScopedResourceReservation memory_reservation( - m_resource_limit, LimitableResource::PhysicalMemoryMax, code_size + m_system_resource_size); - if (!memory_reservation.Succeeded()) { - LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", - code_size + m_system_resource_size); - R_RETURN(ResultLimitReached); - } - // Initialize process address space - if (const Result result{m_page_table.InitializeForProcess( - metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application, - this->GetEntryPoint(), code_size, std::addressof(m_kernel.GetAppSystemResource()), - m_resource_limit, m_kernel.System().ApplicationMemory())}; - result.IsError()) { - R_RETURN(result); - } - - // Map process code region - if (const Result result{m_page_table.MapProcessCode(this->GetEntryPoint(), code_size / PageSize, - KMemoryState::Code, - KMemoryPermission::None)}; - result.IsError()) { - R_RETURN(result); - } - - // Initialize process capabilities - const auto& caps{metadata.GetKernelCapabilities()}; - if (const Result result{ - m_capabilities.InitializeForUserProcess(caps.data(), caps.size(), m_page_table)}; - result.IsError()) { - R_RETURN(result); - } - - // Set memory usage capacity - switch (metadata.GetAddressSpaceType()) { - case FileSys::ProgramAddressSpaceType::Is32Bit: - case FileSys::ProgramAddressSpaceType::Is36Bit: - case FileSys::ProgramAddressSpaceType::Is39Bit: - m_memory_usage_capacity = - m_page_table.GetHeapRegionEnd() - m_page_table.GetHeapRegionStart(); - break; + // Finish termination. + this->FinishTermination(); +} - case FileSys::ProgramAddressSpaceType::Is32BitNoMap: - m_memory_usage_capacity = - (m_page_table.GetHeapRegionEnd() - m_page_table.GetHeapRegionStart()) + - (m_page_table.GetAliasRegionEnd() - m_page_table.GetAliasRegionStart()); - break; +Result KProcess::StartTermination() { + // Finalize the handle table when we're done, if the process isn't immortal. + SCOPE_EXIT({ + if (!m_is_immortal) { + this->FinalizeHandleTable(); + } + }); - default: - ASSERT(false); - break; - } + // Terminate child threads other than the current one. + R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel))); +} - // Create TLS region - R_TRY(this->CreateThreadLocalRegion(std::addressof(m_plr_address))); - memory_reservation.Commit(); +void KProcess::FinishTermination() { + // Only allow termination to occur if the process isn't immortal. + if (!m_is_immortal) { + // Release resource limit hint. + if (m_resource_limit != nullptr) { + m_memory_release_hint = this->GetUsedNonSystemUserPhysicalMemorySize(); + m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, 0, + m_memory_release_hint); + } + + // Change state. + { + KScopedSchedulerLock sl(m_kernel); + this->ChangeState(State::Terminated); + } - R_RETURN(m_handle_table.Initialize(m_capabilities.GetHandleTableSize())); + // Close. + this->Close(); + } } -void KProcess::Run(s32 main_thread_priority, u64 stack_size) { - ASSERT(this->AllocateMainThreadStack(stack_size) == ResultSuccess); - m_resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); +void KProcess::Exit() { + // Determine whether we need to start terminating + bool needs_terminate = false; + { + KScopedLightLock lk(m_state_lock); + KScopedSchedulerLock sl(m_kernel); + + ASSERT(m_state != State::Created); + ASSERT(m_state != State::CreatedAttached); + ASSERT(m_state != State::Crashed); + ASSERT(m_state != State::Terminated); + if (m_state == State::Running || m_state == State::RunningAttached || + m_state == State::DebugBreak) { + this->ChangeState(State::Terminating); + needs_terminate = true; + } + } - const std::size_t heap_capacity{m_memory_usage_capacity - - (m_main_thread_stack_size + m_image_size)}; - ASSERT(!m_page_table.SetMaxHeapSize(heap_capacity).IsError()); + // If we need to start termination, do so. + if (needs_terminate) { + this->StartTermination(); - this->ChangeState(State::Running); + // Register the process as a work task. + m_kernel.WorkerTaskManager().AddTask(m_kernel, KWorkerTaskManager::WorkerType::Exit, this); + } - SetupMainThread(m_kernel.System(), *this, main_thread_priority, m_main_thread_stack_top); + // Exit the current thread. + GetCurrentThread(m_kernel).Exit(); } -void KProcess::PrepareForTermination() { - this->ChangeState(State::Terminating); +Result KProcess::Terminate() { + // Determine whether we need to start terminating. + bool needs_terminate = false; + { + KScopedLightLock lk(m_state_lock); - const auto stop_threads = [this](const std::vector<KThread*>& in_thread_list) { - for (auto* thread : in_thread_list) { - if (thread->GetOwnerProcess() != this) - continue; + // Check whether we're allowed to terminate. + R_UNLESS(m_state != State::Created, ResultInvalidState); + R_UNLESS(m_state != State::CreatedAttached, ResultInvalidState); - if (thread == GetCurrentThreadPointer(m_kernel)) - continue; + KScopedSchedulerLock sl(m_kernel); - // TODO(Subv): When are the other running/ready threads terminated? - ASSERT_MSG(thread->GetState() == ThreadState::Waiting, - "Exiting processes with non-waiting threads is currently unimplemented"); + if (m_state == State::Running || m_state == State::RunningAttached || + m_state == State::Crashed || m_state == State::DebugBreak) { + this->ChangeState(State::Terminating); + needs_terminate = true; + } + } - thread->Exit(); + // If we need to terminate, do so. + if (needs_terminate) { + // Start termination. + if (R_SUCCEEDED(this->StartTermination())) { + // Finish termination. + this->FinishTermination(); + } else { + // Register the process as a work task. + m_kernel.WorkerTaskManager().AddTask(m_kernel, KWorkerTaskManager::WorkerType::Exit, + this); } - }; + } - stop_threads(m_kernel.System().GlobalSchedulerContext().GetThreadList()); + R_SUCCEED(); +} - this->DeleteThreadLocalRegion(m_plr_address); - m_plr_address = 0; +Result KProcess::AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size) { + // Lock ourselves, to prevent concurrent access. + KScopedLightLock lk(m_state_lock); - if (m_resource_limit) { - m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, - m_main_thread_stack_size + m_image_size); + // Try to find an existing info for the memory. + KSharedMemoryInfo* info = nullptr; + for (auto it = m_shared_memory_list.begin(); it != m_shared_memory_list.end(); ++it) { + if (it->GetSharedMemory() == shmem) { + info = std::addressof(*it); + break; + } } - this->ChangeState(State::Terminated); -} + // If we didn't find an info, create one. + if (info == nullptr) { + // Allocate a new info. + info = KSharedMemoryInfo::Allocate(m_kernel); + R_UNLESS(info != nullptr, ResultOutOfResource); -void KProcess::Finalize() { - // Free all shared memory infos. - { - auto it = m_shared_memory_list.begin(); - while (it != m_shared_memory_list.end()) { - KSharedMemoryInfo* info = *it; - KSharedMemory* shmem = info->GetSharedMemory(); + // Initialize the info and add it to our list. + info->Initialize(shmem); + m_shared_memory_list.push_back(*info); + } - while (!info->Close()) { - shmem->Close(); - } + // Open a reference to the shared memory and its info. + shmem->Open(); + info->Open(); - shmem->Close(); + R_SUCCEED(); +} - it = m_shared_memory_list.erase(it); - KSharedMemoryInfo::Free(m_kernel, info); +void KProcess::RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size) { + // Lock ourselves, to prevent concurrent access. + KScopedLightLock lk(m_state_lock); + + // Find an existing info for the memory. + KSharedMemoryInfo* info = nullptr; + auto it = m_shared_memory_list.begin(); + for (; it != m_shared_memory_list.end(); ++it) { + if (it->GetSharedMemory() == shmem) { + info = std::addressof(*it); + break; } } + ASSERT(info != nullptr); - // Release memory to the resource limit. - if (m_resource_limit != nullptr) { - m_resource_limit->Close(); - m_resource_limit = nullptr; + // Close a reference to the info and its memory. + if (info->Close()) { + m_shared_memory_list.erase(it); + KSharedMemoryInfo::Free(m_kernel, info); } - // Finalize the page table. - m_page_table.Finalize(); - - // Perform inherited finalization. - KSynchronizationObject::Finalize(); + shmem->Close(); } Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { @@ -518,7 +601,7 @@ Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { // See if we can get a region from a partially used TLP. { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl(m_kernel); if (auto it = m_partially_used_tlp_tree.begin(); it != m_partially_used_tlp_tree.end()) { tlr = it->Reserve(); @@ -538,7 +621,9 @@ Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { // Allocate a new page. tlp = KThreadLocalPage::Allocate(m_kernel); R_UNLESS(tlp != nullptr, ResultOutOfMemory); - auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(m_kernel, tlp); }); + ON_RESULT_FAILURE { + KThreadLocalPage::Free(m_kernel, tlp); + }; // Initialize the new page. R_TRY(tlp->Initialize(m_kernel, this)); @@ -549,7 +634,7 @@ Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { // Insert into our tree. { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl(m_kernel); if (tlp->IsAllUsed()) { m_fully_used_tlp_tree.insert(*tlp); } else { @@ -558,7 +643,6 @@ Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { } // We succeeded! - tlp_guard.Cancel(); *out = tlr; R_SUCCEED(); } @@ -568,7 +652,7 @@ Result KProcess::DeleteThreadLocalRegion(KProcessAddress addr) { // Release the region. { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl(m_kernel); // Try to find the page in the partially used list. auto it = m_partially_used_tlp_tree.find_key(Common::AlignDown(GetInteger(addr), PageSize)); @@ -611,95 +695,213 @@ Result KProcess::DeleteThreadLocalRegion(KProcessAddress addr) { R_SUCCEED(); } -bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { - const auto watch{std::find_if(m_watchpoints.begin(), m_watchpoints.end(), [&](const auto& wp) { - return wp.type == DebugWatchpointType::None; - })}; +bool KProcess::ReserveResource(Svc::LimitableResource which, s64 value) { + if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { + return rl->Reserve(which, value); + } else { + return true; + } +} - if (watch == m_watchpoints.end()) { - return false; +bool KProcess::ReserveResource(Svc::LimitableResource which, s64 value, s64 timeout) { + if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { + return rl->Reserve(which, value, timeout); + } else { + return true; } +} - watch->start_address = addr; - watch->end_address = addr + size; - watch->type = type; +void KProcess::ReleaseResource(Svc::LimitableResource which, s64 value) { + if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { + rl->Release(which, value); + } +} - for (KProcessAddress page = Common::AlignDown(GetInteger(addr), PageSize); page < addr + size; - page += PageSize) { - m_debug_page_refcounts[page]++; - this->GetMemory().MarkRegionDebug(page, PageSize, true); +void KProcess::ReleaseResource(Svc::LimitableResource which, s64 value, s64 hint) { + if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { + rl->Release(which, value, hint); } +} - return true; +void KProcess::IncrementRunningThreadCount() { + ASSERT(m_num_running_threads.load() >= 0); + + ++m_num_running_threads; } -bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { - const auto watch{std::find_if(m_watchpoints.begin(), m_watchpoints.end(), [&](const auto& wp) { - return wp.start_address == addr && wp.end_address == addr + size && wp.type == type; - })}; +void KProcess::DecrementRunningThreadCount() { + ASSERT(m_num_running_threads.load() > 0); - if (watch == m_watchpoints.end()) { + if (const auto prev = m_num_running_threads--; prev == 1) { + this->Terminate(); + } +} + +bool KProcess::EnterUserException() { + // Get the current thread. + KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + ASSERT(this == cur_thread->GetOwnerProcess()); + + // Check that we haven't already claimed the exception thread. + if (m_exception_thread == cur_thread) { return false; } - watch->start_address = 0; - watch->end_address = 0; - watch->type = DebugWatchpointType::None; + // Create the wait queue we'll be using. + ThreadQueueImplForKProcessEnterUserException wait_queue(m_kernel, + std::addressof(m_exception_thread)); - for (KProcessAddress page = Common::AlignDown(GetInteger(addr), PageSize); page < addr + size; - page += PageSize) { - m_debug_page_refcounts[page]--; - if (!m_debug_page_refcounts[page]) { - this->GetMemory().MarkRegionDebug(page, PageSize, false); + // Claim the exception thread. + { + // Lock the scheduler. + KScopedSchedulerLock sl(m_kernel); + + // Check that we're not terminating. + if (cur_thread->IsTerminationRequested()) { + return false; + } + + // If we don't have an exception thread, we can just claim it directly. + if (m_exception_thread == nullptr) { + m_exception_thread = cur_thread; + KScheduler::SetSchedulerUpdateNeeded(m_kernel); + return true; } + + // Otherwise, we need to wait until we don't have an exception thread. + + // Add the current thread as a waiter on the current exception thread. + cur_thread->SetKernelAddressKey( + reinterpret_cast<uintptr_t>(std::addressof(m_exception_thread)) | 1); + m_exception_thread->AddWaiter(cur_thread); + + // Wait to claim the exception thread. + cur_thread->BeginWait(std::addressof(wait_queue)); } - return true; + // If our wait didn't end due to thread termination, we succeeded. + return ResultTerminationRequested != cur_thread->GetWaitResult(); } -void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { - const auto ReprotectSegment = [&](const CodeSet::Segment& segment, - Svc::MemoryPermission permission) { - m_page_table.SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); - }; +bool KProcess::LeaveUserException() { + return this->ReleaseUserException(GetCurrentThreadPointer(m_kernel)); +} - this->GetMemory().WriteBlock(base_addr, code_set.memory.data(), code_set.memory.size()); +bool KProcess::ReleaseUserException(KThread* thread) { + KScopedSchedulerLock sl(m_kernel); - ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute); - ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read); - ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); + if (m_exception_thread == thread) { + m_exception_thread = nullptr; + + // Remove waiter thread. + bool has_waiters; + if (KThread* next = thread->RemoveKernelWaiterByKey( + std::addressof(has_waiters), + reinterpret_cast<uintptr_t>(std::addressof(m_exception_thread)) | 1); + next != nullptr) { + next->EndWait(ResultSuccess); + } + + KScheduler::SetSchedulerUpdateNeeded(m_kernel); + + return true; + } else { + return false; + } } -bool KProcess::IsSignaled() const { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - return m_is_signaled; +void KProcess::RegisterThread(KThread* thread) { + KScopedLightLock lk(m_list_lock); + + m_thread_list.push_back(*thread); } -KProcess::KProcess(KernelCore& kernel) - : KAutoObjectWithSlabHeapAndContainer{kernel}, m_page_table{m_kernel.System()}, - m_handle_table{m_kernel}, m_address_arbiter{m_kernel.System()}, - m_condition_var{m_kernel.System()}, m_state_lock{m_kernel}, m_list_lock{m_kernel} {} +void KProcess::UnregisterThread(KThread* thread) { + KScopedLightLock lk(m_list_lock); -KProcess::~KProcess() = default; + m_thread_list.erase(m_thread_list.iterator_to(*thread)); +} + +size_t KProcess::GetUsedUserPhysicalMemorySize() const { + const size_t norm_size = m_page_table.GetNormalMemorySize(); + const size_t other_size = m_code_size + m_main_thread_stack_size; + const size_t sec_size = this->GetRequiredSecureMemorySizeNonDefault(); -void KProcess::ChangeState(State new_state) { - if (m_state == new_state) { - return; + return norm_size + other_size + sec_size; +} + +size_t KProcess::GetTotalUserPhysicalMemorySize() const { + // Get the amount of free and used size. + const size_t free_size = + m_resource_limit->GetFreeValue(Svc::LimitableResource::PhysicalMemoryMax); + const size_t max_size = m_max_process_memory; + + // Determine used size. + // NOTE: This does *not* check this->IsDefaultApplicationSystemResource(), unlike + // GetUsedUserPhysicalMemorySize(). + const size_t norm_size = m_page_table.GetNormalMemorySize(); + const size_t other_size = m_code_size + m_main_thread_stack_size; + const size_t sec_size = this->GetRequiredSecureMemorySize(); + const size_t used_size = norm_size + other_size + sec_size; + + // NOTE: These function calls will recalculate, introducing a race...it is unclear why Nintendo + // does it this way. + if (used_size + free_size > max_size) { + return max_size; + } else { + return free_size + this->GetUsedUserPhysicalMemorySize(); } +} - m_state = new_state; - m_is_signaled = true; - this->NotifyAvailable(); +size_t KProcess::GetUsedNonSystemUserPhysicalMemorySize() const { + const size_t norm_size = m_page_table.GetNormalMemorySize(); + const size_t other_size = m_code_size + m_main_thread_stack_size; + + return norm_size + other_size; +} + +size_t KProcess::GetTotalNonSystemUserPhysicalMemorySize() const { + // Get the amount of free and used size. + const size_t free_size = + m_resource_limit->GetFreeValue(Svc::LimitableResource::PhysicalMemoryMax); + const size_t max_size = m_max_process_memory; + + // Determine used size. + // NOTE: This does *not* check this->IsDefaultApplicationSystemResource(), unlike + // GetUsedUserPhysicalMemorySize(). + const size_t norm_size = m_page_table.GetNormalMemorySize(); + const size_t other_size = m_code_size + m_main_thread_stack_size; + const size_t sec_size = this->GetRequiredSecureMemorySize(); + const size_t used_size = norm_size + other_size + sec_size; + + // NOTE: These function calls will recalculate, introducing a race...it is unclear why Nintendo + // does it this way. + if (used_size + free_size > max_size) { + return max_size - this->GetRequiredSecureMemorySizeNonDefault(); + } else { + return free_size + this->GetUsedNonSystemUserPhysicalMemorySize(); + } } -Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { +Result KProcess::Run(s32 priority, size_t stack_size) { + // Lock ourselves, to prevent concurrent access. + KScopedLightLock lk(m_state_lock); + + // Validate that we're in a state where we can initialize. + const auto state = m_state; + R_UNLESS(state == State::Created || state == State::CreatedAttached, ResultInvalidState); + + // Place a tentative reservation of a thread for this process. + KScopedResourceReservation thread_reservation(this, Svc::LimitableResource::ThreadCountMax); + R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached); + // Ensure that we haven't already allocated stack. ASSERT(m_main_thread_stack_size == 0); // Ensure that we're allocating a valid stack. stack_size = Common::AlignUp(stack_size, PageSize); - // R_UNLESS(stack_size + image_size <= m_max_process_memory, ResultOutOfMemory); - R_UNLESS(stack_size + m_image_size >= m_image_size, ResultOutOfMemory); + R_UNLESS(stack_size + m_code_size <= m_max_process_memory, ResultOutOfMemory); + R_UNLESS(stack_size + m_code_size >= m_code_size, ResultOutOfMemory); // Place a tentative reservation of memory for our new stack. KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax, @@ -707,21 +909,359 @@ Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached); // Allocate and map our stack. + KProcessAddress stack_top = 0; if (stack_size) { KProcessAddress stack_bottom; R_TRY(m_page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize, KMemoryState::Stack, KMemoryPermission::UserReadWrite)); - m_main_thread_stack_top = stack_bottom + stack_size; + stack_top = stack_bottom + stack_size; m_main_thread_stack_size = stack_size; } + // Ensure our stack is safe to clean up on exit. + ON_RESULT_FAILURE { + if (m_main_thread_stack_size) { + ASSERT(R_SUCCEEDED(m_page_table.UnmapPages(stack_top - m_main_thread_stack_size, + m_main_thread_stack_size / PageSize, + KMemoryState::Stack))); + m_main_thread_stack_size = 0; + } + }; + + // Set our maximum heap size. + R_TRY(m_page_table.SetMaxHeapSize(m_max_process_memory - + (m_main_thread_stack_size + m_code_size))); + + // Initialize our handle table. + R_TRY(this->InitializeHandleTable(m_capabilities.GetHandleTableSize())); + ON_RESULT_FAILURE_2 { + this->FinalizeHandleTable(); + }; + + // Create a new thread for the process. + KThread* main_thread = KThread::Create(m_kernel); + R_UNLESS(main_thread != nullptr, ResultOutOfResource); + SCOPE_EXIT({ main_thread->Close(); }); + + // Initialize the thread. + R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0, + stack_top, priority, m_ideal_core_id, this)); + + // Register the thread, and commit our reservation. + KThread::Register(m_kernel, main_thread); + thread_reservation.Commit(); + + // Add the thread to our handle table. + Handle thread_handle; + R_TRY(m_handle_table.Add(std::addressof(thread_handle), main_thread)); + + // Set the thread arguments. + main_thread->GetContext32().cpu_registers[0] = 0; + main_thread->GetContext64().cpu_registers[0] = 0; + main_thread->GetContext32().cpu_registers[1] = thread_handle; + main_thread->GetContext64().cpu_registers[1] = thread_handle; + + // Update our state. + this->ChangeState((state == State::Created) ? State::Running : State::RunningAttached); + ON_RESULT_FAILURE_2 { + this->ChangeState(state); + }; + + // Suspend for debug, if we should. + if (m_kernel.System().DebuggerEnabled()) { + main_thread->RequestSuspend(SuspendType::Debug); + } + + // Run our thread. + R_TRY(main_thread->Run()); + + // Open a reference to represent that we're running. + this->Open(); + // We succeeded! Commit our memory reservation. mem_reservation.Commit(); R_SUCCEED(); } +Result KProcess::Reset() { + // Lock the process and the scheduler. + KScopedLightLock lk(m_state_lock); + KScopedSchedulerLock sl(m_kernel); + + // Validate that we're in a state that we can reset. + R_UNLESS(m_state != State::Terminated, ResultInvalidState); + R_UNLESS(m_is_signaled, ResultInvalidState); + + // Clear signaled. + m_is_signaled = false; + R_SUCCEED(); +} + +Result KProcess::SetActivity(Svc::ProcessActivity activity) { + // Lock ourselves and the scheduler. + KScopedLightLock lk(m_state_lock); + KScopedLightLock list_lk(m_list_lock); + KScopedSchedulerLock sl(m_kernel); + + // Validate our state. + R_UNLESS(m_state != State::Terminating, ResultInvalidState); + R_UNLESS(m_state != State::Terminated, ResultInvalidState); + + // Either pause or resume. + if (activity == Svc::ProcessActivity::Paused) { + // Verify that we're not suspended. + R_UNLESS(!m_is_suspended, ResultInvalidState); + + // Suspend all threads. + auto end = this->GetThreadList().end(); + for (auto it = this->GetThreadList().begin(); it != end; ++it) { + it->RequestSuspend(SuspendType::Process); + } + + // Set ourselves as suspended. + this->SetSuspended(true); + } else { + ASSERT(activity == Svc::ProcessActivity::Runnable); + + // Verify that we're suspended. + R_UNLESS(m_is_suspended, ResultInvalidState); + + // Resume all threads. + auto end = this->GetThreadList().end(); + for (auto it = this->GetThreadList().begin(); it != end; ++it) { + it->Resume(SuspendType::Process); + } + + // Set ourselves as resumed. + this->SetSuspended(false); + } + + R_SUCCEED(); +} + +void KProcess::PinCurrentThread() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + + // Get the current thread. + const s32 core_id = GetCurrentCoreId(m_kernel); + KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + + // If the thread isn't terminated, pin it. + if (!cur_thread->IsTerminationRequested()) { + // Pin it. + this->PinThread(core_id, cur_thread); + cur_thread->Pin(core_id); + + // An update is needed. + KScheduler::SetSchedulerUpdateNeeded(m_kernel); + } +} + +void KProcess::UnpinCurrentThread() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + + // Get the current thread. + const s32 core_id = GetCurrentCoreId(m_kernel); + KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + + // Unpin it. + cur_thread->Unpin(); + this->UnpinThread(core_id, cur_thread); + + // An update is needed. + KScheduler::SetSchedulerUpdateNeeded(m_kernel); +} + +void KProcess::UnpinThread(KThread* thread) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + + // Get the thread's core id. + const auto core_id = thread->GetActiveCore(); + + // Unpin it. + this->UnpinThread(core_id, thread); + thread->Unpin(); + + // An update is needed. + KScheduler::SetSchedulerUpdateNeeded(m_kernel); +} + +Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, + s32 max_out_count) { + // TODO: use current memory reference + auto& memory = m_kernel.System().ApplicationMemory(); + + // Lock the list. + KScopedLightLock lk(m_list_lock); + + // Iterate over the list. + s32 count = 0; + auto end = this->GetThreadList().end(); + for (auto it = this->GetThreadList().begin(); it != end; ++it) { + // If we're within array bounds, write the id. + if (count < max_out_count) { + // Get the thread id. + KThread* thread = std::addressof(*it); + const u64 id = thread->GetId(); + + // Copy the id to userland. + memory.Write64(out_thread_ids + count * sizeof(u64), id); + } + + // Increment the count. + ++count; + } + + // We successfully iterated the list. + *out_num_threads = count; + R_SUCCEED(); +} + +void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} + +KProcess::KProcess(KernelCore& kernel) + : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel.System()}, + m_state_lock{kernel}, m_list_lock{kernel}, m_cond_var{kernel.System()}, + m_address_arbiter{kernel.System()}, m_handle_table{kernel} {} +KProcess::~KProcess() = default; + +Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, + bool is_hbl) { + // Create a resource limit for the process. + const auto physical_memory_size = + m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); + auto* res_limit = + Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size); + + // Ensure we maintain a clean state on exit. + SCOPE_EXIT({ res_limit->Close(); }); + + // Declare flags and code address. + Svc::CreateProcessFlag flag{}; + u64 code_address{}; + + // We are an application. + flag |= Svc::CreateProcessFlag::IsApplication; + + // If we are 64-bit, create as such. + if (metadata.Is64BitProgram()) { + flag |= Svc::CreateProcessFlag::Is64Bit; + } + + // Set the address space type and code address. + switch (metadata.GetAddressSpaceType()) { + case FileSys::ProgramAddressSpaceType::Is39Bit: + flag |= Svc::CreateProcessFlag::AddressSpace64Bit; + + // For 39-bit processes, the ASLR region starts at 0x800'0000 and is ~512GiB large. + // However, some (buggy) programs/libraries like skyline incorrectly depend on the + // existence of ASLR pages before the entry point, so we will adjust the load address + // to point to about 2GiB into the ASLR region. + code_address = 0x8000'0000; + break; + case FileSys::ProgramAddressSpaceType::Is36Bit: + flag |= Svc::CreateProcessFlag::AddressSpace64BitDeprecated; + code_address = 0x800'0000; + break; + case FileSys::ProgramAddressSpaceType::Is32Bit: + flag |= Svc::CreateProcessFlag::AddressSpace32Bit; + code_address = 0x20'0000; + break; + case FileSys::ProgramAddressSpaceType::Is32BitNoMap: + flag |= Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias; + code_address = 0x20'0000; + break; + } + + Svc::CreateProcessParameter params{ + .name = {}, + .version = {}, + .program_id = metadata.GetTitleID(), + .code_address = code_address, + .code_num_pages = static_cast<s32>(code_size / PageSize), + .flags = flag, + .reslimit = Svc::InvalidHandle, + .system_resource_num_pages = static_cast<s32>(metadata.GetSystemResourceSize() / PageSize), + }; + + // Set the process name. + const auto& name = metadata.GetName(); + static_assert(sizeof(params.name) <= sizeof(name)); + std::memcpy(params.name.data(), name.data(), sizeof(params.name)); + + // Initialize for application process. + R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, + KMemoryManager::Pool::Application)); + + // Assign remaining properties. + m_is_hbl = is_hbl; + m_ideal_core_id = metadata.GetMainThreadCore(); + + // We succeeded. + R_SUCCEED(); +} + +void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { + const auto ReprotectSegment = [&](const CodeSet::Segment& segment, + Svc::MemoryPermission permission) { + m_page_table.SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); + }; + + this->GetMemory().WriteBlock(base_addr, code_set.memory.data(), code_set.memory.size()); + + ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute); + ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read); + ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); +} + +bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { + const auto watch{std::find_if(m_watchpoints.begin(), m_watchpoints.end(), [&](const auto& wp) { + return wp.type == DebugWatchpointType::None; + })}; + + if (watch == m_watchpoints.end()) { + return false; + } + + watch->start_address = addr; + watch->end_address = addr + size; + watch->type = type; + + for (KProcessAddress page = Common::AlignDown(GetInteger(addr), PageSize); page < addr + size; + page += PageSize) { + m_debug_page_refcounts[page]++; + this->GetMemory().MarkRegionDebug(page, PageSize, true); + } + + return true; +} + +bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { + const auto watch{std::find_if(m_watchpoints.begin(), m_watchpoints.end(), [&](const auto& wp) { + return wp.start_address == addr && wp.end_address == addr + size && wp.type == type; + })}; + + if (watch == m_watchpoints.end()) { + return false; + } + + watch->start_address = 0; + watch->end_address = 0; + watch->type = DebugWatchpointType::None; + + for (KProcessAddress page = Common::AlignDown(GetInteger(addr), PageSize); page < addr + size; + page += PageSize) { + m_debug_page_refcounts[page]--; + if (!m_debug_page_refcounts[page]) { + this->GetMemory().MarkRegionDebug(page, PageSize, false); + } + } + + return true; +} + Core::Memory::Memory& KProcess::GetMemory() const { // TODO: per-process memory return m_kernel.System().ApplicationMemory(); diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 146e07a57..f9f755afa 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -1,59 +1,23 @@ -// SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> -#include <cstddef> -#include <list> #include <map> -#include <string> + +#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_address_arbiter.h" -#include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/k_capabilities.h" #include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_page_table.h" -#include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/k_page_table_manager.h" +#include "core/hle/kernel/k_system_resource.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread_local_page.h" -#include "core/hle/kernel/k_typed_address.h" -#include "core/hle/kernel/k_worker_task.h" -#include "core/hle/kernel/process_capability.h" -#include "core/hle/kernel/slab_helpers.h" -#include "core/hle/result.h" - -namespace Core { -namespace Memory { -class Memory; -}; - -class System; -} // namespace Core - -namespace FileSys { -class ProgramMetadata; -} namespace Kernel { -class KernelCore; -class KResourceLimit; -class KThread; -class KSharedMemoryInfo; -class TLSPage; - -struct CodeSet; - -enum class MemoryRegion : u16 { - APPLICATION = 1, - SYSTEM = 2, - BASE = 3, -}; - -enum class ProcessActivity : u32 { - Runnable, - Paused, -}; - enum class DebugWatchpointType : u8 { None = 0, Read = 1 << 0, @@ -72,9 +36,6 @@ class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWor KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject); public: - explicit KProcess(KernelCore& kernel); - ~KProcess() override; - enum class State { Created = static_cast<u32>(Svc::ProcessState::Created), CreatedAttached = static_cast<u32>(Svc::ProcessState::CreatedAttached), @@ -86,470 +47,493 @@ public: DebugBreak = static_cast<u32>(Svc::ProcessState::DebugBreak), }; - enum : u64 { - /// Lowest allowed process ID for a kernel initial process. - InitialKIPIDMin = 1, - /// Highest allowed process ID for a kernel initial process. - InitialKIPIDMax = 80, - - /// Lowest allowed process ID for a userland process. - ProcessIDMin = 81, - /// Highest allowed process ID for a userland process. - ProcessIDMax = 0xFFFFFFFFFFFFFFFF, - }; + using ThreadList = Common::IntrusiveListMemberTraits<&KThread::m_process_list_node>::ListType; - // Used to determine how process IDs are assigned. - enum class ProcessType { - KernelInternal, - Userland, - }; + static constexpr size_t AslrAlignment = 2_MiB; - static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; +public: + static constexpr u64 InitialProcessIdMin = 1; + static constexpr u64 InitialProcessIdMax = 0x50; - static Result Initialize(KProcess* process, Core::System& system, std::string process_name, - ProcessType type, KResourceLimit* res_limit); + static constexpr u64 ProcessIdMin = InitialProcessIdMax + 1; + static constexpr u64 ProcessIdMax = std::numeric_limits<u64>::max(); - /// Gets a reference to the process' page table. - KPageTable& GetPageTable() { - return m_page_table; - } +private: + using SharedMemoryInfoList = Common::IntrusiveListBaseTraits<KSharedMemoryInfo>::ListType; + using TLPTree = + Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>; + using TLPIterator = TLPTree::iterator; - /// Gets const a reference to the process' page table. - const KPageTable& GetPageTable() const { - return m_page_table; - } +private: + KPageTable m_page_table; + std::atomic<size_t> m_used_kernel_memory_size{}; + TLPTree m_fully_used_tlp_tree{}; + TLPTree m_partially_used_tlp_tree{}; + s32 m_ideal_core_id{}; + KResourceLimit* m_resource_limit{}; + KSystemResource* m_system_resource{}; + size_t m_memory_release_hint{}; + State m_state{}; + KLightLock m_state_lock; + KLightLock m_list_lock; + KConditionVariable m_cond_var; + KAddressArbiter m_address_arbiter; + std::array<u64, 4> m_entropy{}; + bool m_is_signaled{}; + bool m_is_initialized{}; + bool m_is_application{}; + bool m_is_default_application_system_resource{}; + bool m_is_hbl{}; + std::array<char, 13> m_name{}; + std::atomic<u16> m_num_running_threads{}; + Svc::CreateProcessFlag m_flags{}; + KMemoryManager::Pool m_memory_pool{}; + s64 m_schedule_count{}; + KCapabilities m_capabilities{}; + u64 m_program_id{}; + u64 m_process_id{}; + KProcessAddress m_code_address{}; + size_t m_code_size{}; + size_t m_main_thread_stack_size{}; + size_t m_max_process_memory{}; + u32 m_version{}; + KHandleTable m_handle_table; + KProcessAddress m_plr_address{}; + KThread* m_exception_thread{}; + ThreadList m_thread_list{}; + SharedMemoryInfoList m_shared_memory_list{}; + bool m_is_suspended{}; + bool m_is_immortal{}; + bool m_is_handle_table_initialized{}; + std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_running_threads{}; + std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_idle_counts{}; + std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_switch_counts{}; + std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_pinned_threads{}; + std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{}; + std::map<KProcessAddress, u64> m_debug_page_refcounts{}; + std::atomic<s64> m_cpu_time{}; + std::atomic<s64> m_num_process_switches{}; + std::atomic<s64> m_num_thread_switches{}; + std::atomic<s64> m_num_fpu_switches{}; + std::atomic<s64> m_num_supervisor_calls{}; + std::atomic<s64> m_num_ipc_messages{}; + std::atomic<s64> m_num_ipc_replies{}; + std::atomic<s64> m_num_ipc_receives{}; - /// Gets a reference to the process' handle table. - KHandleTable& GetHandleTable() { - return m_handle_table; - } +private: + Result StartTermination(); + void FinishTermination(); - /// Gets a const reference to the process' handle table. - const KHandleTable& GetHandleTable() const { - return m_handle_table; + void PinThread(s32 core_id, KThread* thread) { + ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); + ASSERT(thread != nullptr); + ASSERT(m_pinned_threads[core_id] == nullptr); + m_pinned_threads[core_id] = thread; } - /// Gets a reference to process's memory. - Core::Memory::Memory& GetMemory() const; - - Result SignalToAddress(KProcessAddress address) { - return m_condition_var.SignalToAddress(address); + void UnpinThread(s32 core_id, KThread* thread) { + ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); + ASSERT(thread != nullptr); + ASSERT(m_pinned_threads[core_id] == thread); + m_pinned_threads[core_id] = nullptr; } - Result WaitForAddress(Handle handle, KProcessAddress address, u32 tag) { - return m_condition_var.WaitForAddress(handle, address, tag); - } +public: + explicit KProcess(KernelCore& kernel); + ~KProcess() override; - void SignalConditionVariable(u64 cv_key, int32_t count) { - return m_condition_var.Signal(cv_key, count); - } + Result Initialize(const Svc::CreateProcessParameter& params, KResourceLimit* res_limit, + bool is_real); - Result WaitConditionVariable(KProcessAddress address, u64 cv_key, u32 tag, s64 ns) { - R_RETURN(m_condition_var.Wait(address, cv_key, tag, ns)); - } + Result Initialize(const Svc::CreateProcessParameter& params, const KPageGroup& pg, + std::span<const u32> caps, KResourceLimit* res_limit, + KMemoryManager::Pool pool, bool immortal); + Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps, + KResourceLimit* res_limit, KMemoryManager::Pool pool); + void Exit(); - Result SignalAddressArbiter(uint64_t address, Svc::SignalType signal_type, s32 value, - s32 count) { - R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count)); + const char* GetName() const { + return m_name.data(); } - Result WaitAddressArbiter(uint64_t address, Svc::ArbitrationType arb_type, s32 value, - s64 timeout) { - R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout)); + u64 GetProgramId() const { + return m_program_id; } - KProcessAddress GetProcessLocalRegionAddress() const { - return m_plr_address; + u64 GetProcessId() const { + return m_process_id; } - /// Gets the current status of the process State GetState() const { return m_state; } - /// Gets the unique ID that identifies this particular process. - u64 GetProcessId() const { - return m_process_id; + u64 GetCoreMask() const { + return m_capabilities.GetCoreMask(); + } + u64 GetPhysicalCoreMask() const { + return m_capabilities.GetPhysicalCoreMask(); + } + u64 GetPriorityMask() const { + return m_capabilities.GetPriorityMask(); } - /// Gets the program ID corresponding to this process. - u64 GetProgramId() const { - return m_program_id; + s32 GetIdealCoreId() const { + return m_ideal_core_id; + } + void SetIdealCoreId(s32 core_id) { + m_ideal_core_id = core_id; } - KProcessAddress GetEntryPoint() const { - return m_code_address; + bool CheckThreadPriority(s32 prio) const { + return ((1ULL << prio) & this->GetPriorityMask()) != 0; } - /// Gets the resource limit descriptor for this process - KResourceLimit* GetResourceLimit() const; + u32 GetCreateProcessFlags() const { + return static_cast<u32>(m_flags); + } - /// Gets the ideal CPU core ID for this process - u8 GetIdealCoreId() const { - return m_ideal_core; + bool Is64Bit() const { + return True(m_flags & Svc::CreateProcessFlag::Is64Bit); } - /// Checks if the specified thread priority is valid. - bool CheckThreadPriority(s32 prio) const { - return ((1ULL << prio) & GetPriorityMask()) != 0; + KProcessAddress GetEntryPoint() const { + return m_code_address; } - /// Gets the bitmask of allowed cores that this process' threads can run on. - u64 GetCoreMask() const { - return m_capabilities.GetCoreMask(); + size_t GetMainStackSize() const { + return m_main_thread_stack_size; } - /// Gets the bitmask of allowed thread priorities. - u64 GetPriorityMask() const { - return m_capabilities.GetPriorityMask(); + KMemoryManager::Pool GetMemoryPool() const { + return m_memory_pool; } - /// Gets the amount of secure memory to allocate for memory management. - u32 GetSystemResourceSize() const { - return m_system_resource_size; + u64 GetRandomEntropy(size_t i) const { + return m_entropy[i]; } - /// Gets the amount of secure memory currently in use for memory management. - u32 GetSystemResourceUsage() const { - // On hardware, this returns the amount of system resource memory that has - // been used by the kernel. This is problematic for Yuzu to emulate, because - // system resource memory is used for page tables -- and yuzu doesn't really - // have a way to calculate how much memory is required for page tables for - // the current process at any given time. - // TODO: Is this even worth implementing? Games may retrieve this value via - // an SDK function that gets used + available system resource size for debug - // or diagnostic purposes. However, it seems unlikely that a game would make - // decisions based on how much system memory is dedicated to its page tables. - // Is returning a value other than zero wise? - return 0; + bool IsApplication() const { + return m_is_application; } - /// Whether this process is an AArch64 or AArch32 process. - bool Is64BitProcess() const { - return m_is_64bit_process; + bool IsDefaultApplicationSystemResource() const { + return m_is_default_application_system_resource; } bool IsSuspended() const { return m_is_suspended; } - void SetSuspended(bool suspended) { m_is_suspended = suspended; } - /// Gets the total running time of the process instance in ticks. - u64 GetCPUTimeTicks() const { - return m_total_process_running_time_ticks; + Result Terminate(); + + bool IsTerminated() const { + return m_state == State::Terminated; } - /// Updates the total running time, adding the given ticks to it. - void UpdateCPUTimeTicks(u64 ticks) { - m_total_process_running_time_ticks += ticks; + bool IsPermittedSvc(u32 svc_id) const { + return m_capabilities.IsPermittedSvc(svc_id); } - /// Gets the process schedule count, used for thread yielding - s64 GetScheduledCount() const { - return m_schedule_count; + bool IsPermittedInterrupt(s32 interrupt_id) const { + return m_capabilities.IsPermittedInterrupt(interrupt_id); } - /// Increments the process schedule count, used for thread yielding. - void IncrementScheduledCount() { - ++m_schedule_count; + bool IsPermittedDebug() const { + return m_capabilities.IsPermittedDebug(); } - void IncrementRunningThreadCount(); - void DecrementRunningThreadCount(); + bool CanForceDebug() const { + return m_capabilities.CanForceDebug(); + } - void SetRunningThread(s32 core, KThread* thread, u64 idle_count) { - m_running_threads[core] = thread; - m_running_thread_idle_counts[core] = idle_count; + bool IsHbl() const { + return m_is_hbl; } - void ClearRunningThread(KThread* thread) { - for (size_t i = 0; i < m_running_threads.size(); ++i) { - if (m_running_threads[i] == thread) { - m_running_threads[i] = nullptr; - } - } + Kernel::KMemoryManager::Direction GetAllocateOption() const { + // TODO: property of the KPageTableBase + return KMemoryManager::Direction::FromFront; } - [[nodiscard]] KThread* GetRunningThread(s32 core) const { - return m_running_threads[core]; + ThreadList& GetThreadList() { + return m_thread_list; + } + const ThreadList& GetThreadList() const { + return m_thread_list; } + bool EnterUserException(); + bool LeaveUserException(); bool ReleaseUserException(KThread* thread); - [[nodiscard]] KThread* GetPinnedThread(s32 core_id) const { + KThread* GetPinnedThread(s32 core_id) const { ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); return m_pinned_threads[core_id]; } - /// Gets 8 bytes of random data for svcGetInfo RandomEntropy - u64 GetRandomEntropy(std::size_t index) const { - return m_random_entropy.at(index); + const Svc::SvcAccessFlagSet& GetSvcPermissions() const { + return m_capabilities.GetSvcPermissions(); } - /// Retrieves the total physical memory available to this process in bytes. - u64 GetTotalPhysicalMemoryAvailable(); - - /// Retrieves the total physical memory available to this process in bytes, - /// without the size of the personal system resource heap added to it. - u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource(); - - /// Retrieves the total physical memory used by this process in bytes. - u64 GetTotalPhysicalMemoryUsed(); - - /// Retrieves the total physical memory used by this process in bytes, - /// without the size of the personal system resource heap added to it. - u64 GetTotalPhysicalMemoryUsedWithoutSystemResource(); - - /// Gets the list of all threads created with this process as their owner. - std::list<KThread*>& GetThreadList() { - return m_thread_list; + KResourceLimit* GetResourceLimit() const { + return m_resource_limit; } - /// Registers a thread as being created under this process, - /// adding it to this process' thread list. - void RegisterThread(KThread* thread); + bool ReserveResource(Svc::LimitableResource which, s64 value); + bool ReserveResource(Svc::LimitableResource which, s64 value, s64 timeout); + void ReleaseResource(Svc::LimitableResource which, s64 value); + void ReleaseResource(Svc::LimitableResource which, s64 value, s64 hint); - /// Unregisters a thread from this process, removing it - /// from this process' thread list. - void UnregisterThread(KThread* thread); + KLightLock& GetStateLock() { + return m_state_lock; + } + KLightLock& GetListLock() { + return m_list_lock; + } - /// Retrieves the number of available threads for this process. - u64 GetFreeThreadCount() const; - - /// Clears the signaled state of the process if and only if it's signaled. - /// - /// @pre The process must not be already terminated. If this is called on a - /// terminated process, then ResultInvalidState will be returned. - /// - /// @pre The process must be in a signaled state. If this is called on a - /// process instance that is not signaled, ResultInvalidState will be - /// returned. - Result Reset(); + KPageTable& GetPageTable() { + return m_page_table; + } + const KPageTable& GetPageTable() const { + return m_page_table; + } - /** - * Loads process-specifics configuration info with metadata provided - * by an executable. - * - * @param metadata The provided metadata to load process specific info from. - * - * @returns ResultSuccess if all relevant metadata was able to be - * loaded and parsed. Otherwise, an error code is returned. - */ - Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, - bool is_hbl); + KHandleTable& GetHandleTable() { + return m_handle_table; + } + const KHandleTable& GetHandleTable() const { + return m_handle_table; + } - /** - * Starts the main application thread for this process. - * - * @param main_thread_priority The priority for the main thread. - * @param stack_size The stack size for the main thread in bytes. - */ - void Run(s32 main_thread_priority, u64 stack_size); + size_t GetUsedUserPhysicalMemorySize() const; + size_t GetTotalUserPhysicalMemorySize() const; + size_t GetUsedNonSystemUserPhysicalMemorySize() const; + size_t GetTotalNonSystemUserPhysicalMemorySize() const; - /** - * Prepares a process for termination by stopping all of its threads - * and clearing any other resources. - */ - void PrepareForTermination(); + Result AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size); + void RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size); - void LoadModule(CodeSet code_set, KProcessAddress base_addr); + Result CreateThreadLocalRegion(KProcessAddress* out); + Result DeleteThreadLocalRegion(KProcessAddress addr); - bool IsInitialized() const override { - return m_is_initialized; + KProcessAddress GetProcessLocalRegionAddress() const { + return m_plr_address; } - static void PostDestroy(uintptr_t arg) {} - - void Finalize() override; - - u64 GetId() const override { - return GetProcessId(); + KThread* GetExceptionThread() const { + return m_exception_thread; } - bool IsHbl() const { - return m_is_hbl; + void AddCpuTime(s64 diff) { + m_cpu_time += diff; + } + s64 GetCpuTime() { + return m_cpu_time.load(); } - bool IsSignaled() const override; - - void DoWorkerTaskImpl(); + s64 GetScheduledCount() const { + return m_schedule_count; + } + void IncrementScheduledCount() { + ++m_schedule_count; + } - Result SetActivity(ProcessActivity activity); + void IncrementRunningThreadCount(); + void DecrementRunningThreadCount(); - void PinCurrentThread(s32 core_id); - void UnpinCurrentThread(s32 core_id); - void UnpinThread(KThread* thread); + size_t GetRequiredSecureMemorySizeNonDefault() const { + if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) { + auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource); + return secure_system_resource->CalculateRequiredSecureMemorySize(); + } - KLightLock& GetStateLock() { - return m_state_lock; + return 0; } - Result AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size); - void RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size); - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Thread-local storage management - - // Marks the next available region as used and returns the address of the slot. - [[nodiscard]] Result CreateThreadLocalRegion(KProcessAddress* out); + size_t GetRequiredSecureMemorySize() const { + if (m_system_resource->IsSecureResource()) { + auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource); + return secure_system_resource->CalculateRequiredSecureMemorySize(); + } - // Frees a used TLS slot identified by the given address - Result DeleteThreadLocalRegion(KProcessAddress addr); + return 0; + } - /////////////////////////////////////////////////////////////////////////////////////////////// - // Debug watchpoint management + size_t GetTotalSystemResourceSize() const { + if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) { + auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource); + return secure_system_resource->GetSize(); + } - // Attempts to insert a watchpoint into a free slot. Returns false if none are available. - bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); + return 0; + } - // Attempts to remove the watchpoint specified by the given parameters. - bool RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); + size_t GetUsedSystemResourceSize() const { + if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) { + auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource); + return secure_system_resource->GetUsedSize(); + } - const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const { - return m_watchpoints; + return 0; } - const std::string& GetName() { - return name; + void SetRunningThread(s32 core, KThread* thread, u64 idle_count, u64 switch_count) { + m_running_threads[core] = thread; + m_running_thread_idle_counts[core] = idle_count; + m_running_thread_switch_counts[core] = switch_count; } -private: - void PinThread(s32 core_id, KThread* thread) { - ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); - ASSERT(thread != nullptr); - ASSERT(m_pinned_threads[core_id] == nullptr); - m_pinned_threads[core_id] = thread; + void ClearRunningThread(KThread* thread) { + for (size_t i = 0; i < m_running_threads.size(); ++i) { + if (m_running_threads[i] == thread) { + m_running_threads[i] = nullptr; + } + } } - void UnpinThread(s32 core_id, KThread* thread) { - ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); - ASSERT(thread != nullptr); - ASSERT(m_pinned_threads[core_id] == thread); - m_pinned_threads[core_id] = nullptr; + const KSystemResource& GetSystemResource() const { + return *m_system_resource; } - void FinalizeHandleTable() { - // Finalize the table. - m_handle_table.Finalize(); - - // Note that the table is finalized. - m_is_handle_table_initialized = false; + const KMemoryBlockSlabManager& GetMemoryBlockSlabManager() const { + return m_system_resource->GetMemoryBlockSlabManager(); + } + const KBlockInfoManager& GetBlockInfoManager() const { + return m_system_resource->GetBlockInfoManager(); + } + const KPageTableManager& GetPageTableManager() const { + return m_system_resource->GetPageTableManager(); } - void ChangeState(State new_state); - - /// Allocates the main thread stack for the process, given the stack size in bytes. - Result AllocateMainThreadStack(std::size_t stack_size); - - /// Memory manager for this process - KPageTable m_page_table; - - /// Current status of the process - State m_state{}; + KThread* GetRunningThread(s32 core) const { + return m_running_threads[core]; + } + u64 GetRunningThreadIdleCount(s32 core) const { + return m_running_thread_idle_counts[core]; + } + u64 GetRunningThreadSwitchCount(s32 core) const { + return m_running_thread_switch_counts[core]; + } - /// The ID of this process - u64 m_process_id = 0; + void RegisterThread(KThread* thread); + void UnregisterThread(KThread* thread); - /// Title ID corresponding to the process - u64 m_program_id = 0; + Result Run(s32 priority, size_t stack_size); - /// Specifies additional memory to be reserved for the process's memory management by the - /// system. When this is non-zero, secure memory is allocated and used for page table allocation - /// instead of using the normal global page tables/memory block management. - u32 m_system_resource_size = 0; + Result Reset(); - /// Resource limit descriptor for this process - KResourceLimit* m_resource_limit{}; + void SetDebugBreak() { + if (m_state == State::RunningAttached) { + this->ChangeState(State::DebugBreak); + } + } - KVirtualAddress m_system_resource_address{}; + void SetAttached() { + if (m_state == State::DebugBreak) { + this->ChangeState(State::RunningAttached); + } + } - /// The ideal CPU core for this process, threads are scheduled on this core by default. - u8 m_ideal_core = 0; + Result SetActivity(Svc::ProcessActivity activity); - /// Contains the parsed process capability descriptors. - ProcessCapabilities m_capabilities; + void PinCurrentThread(); + void UnpinCurrentThread(); + void UnpinThread(KThread* thread); - /// Whether or not this process is AArch64, or AArch32. - /// By default, we currently assume this is true, unless otherwise - /// specified by metadata provided to the process during loading. - bool m_is_64bit_process = true; + void SignalConditionVariable(uintptr_t cv_key, int32_t count) { + return m_cond_var.Signal(cv_key, count); + } - /// Total running time for the process in ticks. - std::atomic<u64> m_total_process_running_time_ticks = 0; + Result WaitConditionVariable(KProcessAddress address, uintptr_t cv_key, u32 tag, s64 ns) { + R_RETURN(m_cond_var.Wait(address, cv_key, tag, ns)); + } - /// Per-process handle table for storing created object handles in. - KHandleTable m_handle_table; + Result SignalAddressArbiter(uintptr_t address, Svc::SignalType signal_type, s32 value, + s32 count) { + R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count)); + } - /// Per-process address arbiter. - KAddressArbiter m_address_arbiter; + Result WaitAddressArbiter(uintptr_t address, Svc::ArbitrationType arb_type, s32 value, + s64 timeout) { + R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout)); + } - /// The per-process mutex lock instance used for handling various - /// forms of services, such as lock arbitration, and condition - /// variable related facilities. - KConditionVariable m_condition_var; + Result GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, s32 max_out_count); - /// Address indicating the location of the process' dedicated TLS region. - KProcessAddress m_plr_address = 0; + static void Switch(KProcess* cur_process, KProcess* next_process); - /// Address indicating the location of the process's entry point. - KProcessAddress m_code_address = 0; +public: + // Attempts to insert a watchpoint into a free slot. Returns false if none are available. + bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); - /// Random values for svcGetInfo RandomEntropy - std::array<u64, RANDOM_ENTROPY_SIZE> m_random_entropy{}; + // Attempts to remove the watchpoint specified by the given parameters. + bool RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); - /// List of threads that are running with this process as their owner. - std::list<KThread*> m_thread_list; + const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const { + return m_watchpoints; + } - /// List of shared memory that are running with this process as their owner. - std::list<KSharedMemoryInfo*> m_shared_memory_list; +public: + Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, + bool is_hbl); - /// Address of the top of the main thread's stack - KProcessAddress m_main_thread_stack_top{}; + void LoadModule(CodeSet code_set, KProcessAddress base_addr); - /// Size of the main thread's stack - std::size_t m_main_thread_stack_size{}; + Core::Memory::Memory& GetMemory() const; - /// Memory usage capacity for the process - std::size_t m_memory_usage_capacity{}; +public: + // Overridden parent functions. + bool IsInitialized() const override { + return m_is_initialized; + } - /// Process total image size - std::size_t m_image_size{}; + static void PostDestroy(uintptr_t arg) {} - /// Schedule count of this process - s64 m_schedule_count{}; + void Finalize() override; - size_t m_memory_release_hint{}; + u64 GetIdImpl() const { + return this->GetProcessId(); + } + u64 GetId() const override { + return this->GetIdImpl(); + } - std::string name{}; + virtual bool IsSignaled() const override { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + return m_is_signaled; + } - bool m_is_signaled{}; - bool m_is_suspended{}; - bool m_is_immortal{}; - bool m_is_handle_table_initialized{}; - bool m_is_initialized{}; - bool m_is_hbl{}; + void DoWorkerTaskImpl(); - std::atomic<u16> m_num_running_threads{}; +private: + void ChangeState(State new_state) { + if (m_state != new_state) { + m_state = new_state; + m_is_signaled = true; + this->NotifyAvailable(); + } + } - std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_running_threads{}; - std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_idle_counts{}; - std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_pinned_threads{}; - std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{}; - std::map<KProcessAddress, u64> m_debug_page_refcounts; + Result InitializeHandleTable(s32 size) { + // Try to initialize the handle table. + R_TRY(m_handle_table.Initialize(size)); - KThread* m_exception_thread{}; + // We succeeded, so note that we did. + m_is_handle_table_initialized = true; + R_SUCCEED(); + } - KLightLock m_state_lock; - KLightLock m_list_lock; + void FinalizeHandleTable() { + // Finalize the table. + m_handle_table.Finalize(); - using TLPTree = - Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>; - using TLPIterator = TLPTree::iterator; - TLPTree m_fully_used_tlp_tree; - TLPTree m_partially_used_tlp_tree; + // Note that the table is finalized. + m_is_handle_table_initialized = false; + } }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index d8143c650..1bce63a56 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -190,7 +190,7 @@ u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) { if (m_state.should_count_idle) { if (highest_thread != nullptr) [[likely]] { if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) { - process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count); + process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count, 0); } } else { m_state.idle_count++; @@ -356,7 +356,7 @@ void KScheduler::SwitchThread(KThread* next_thread) { const s64 tick_diff = cur_tick - prev_tick; cur_thread->AddCpuTime(m_core_id, tick_diff); if (cur_process != nullptr) { - cur_process->UpdateCPUTimeTicks(tick_diff); + cur_process->AddCpuTime(tick_diff); } m_last_context_switch_time = cur_tick; diff --git a/src/core/hle/kernel/k_system_resource.cpp b/src/core/hle/kernel/k_system_resource.cpp index e6c8d589a..07e92aa80 100644 --- a/src/core/hle/kernel/k_system_resource.cpp +++ b/src/core/hle/kernel/k_system_resource.cpp @@ -1,25 +1,100 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/core.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_system_resource.h" namespace Kernel { Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_limit, KMemoryManager::Pool pool) { - // Unimplemented - UNREACHABLE(); + // Set members. + m_resource_limit = resource_limit; + m_resource_size = size; + m_resource_pool = pool; + + // Determine required size for our secure resource. + const size_t secure_size = this->CalculateRequiredSecureMemorySize(); + + // Reserve memory for our secure resource. + KScopedResourceReservation memory_reservation( + m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, secure_size); + R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); + + // Allocate secure memory. + R_TRY(KSystemControl::AllocateSecureMemory(m_kernel, std::addressof(m_resource_address), + m_resource_size, static_cast<u32>(m_resource_pool))); + ASSERT(m_resource_address != 0); + + // Ensure we clean up the secure memory, if we fail past this point. + ON_RESULT_FAILURE { + KSystemControl::FreeSecureMemory(m_kernel, m_resource_address, m_resource_size, + static_cast<u32>(m_resource_pool)); + }; + + // Check that our allocation is bigger than the reference counts needed for it. + const size_t rc_size = + Common::AlignUp(KPageTableSlabHeap::CalculateReferenceCountSize(m_resource_size), PageSize); + R_UNLESS(m_resource_size > rc_size, ResultOutOfMemory); + + // Get resource pointer. + KPhysicalAddress resource_paddr = + KPageTable::GetHeapPhysicalAddress(m_kernel.MemoryLayout(), m_resource_address); + auto* resource = + m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr); + + // Initialize slab heaps. + m_dynamic_page_manager.Initialize(m_resource_address + rc_size, m_resource_size - rc_size, + PageSize); + m_page_table_heap.Initialize(std::addressof(m_dynamic_page_manager), 0, resource); + m_memory_block_heap.Initialize(std::addressof(m_dynamic_page_manager), 0); + m_block_info_heap.Initialize(std::addressof(m_dynamic_page_manager), 0); + + // Initialize managers. + m_page_table_manager.Initialize(std::addressof(m_dynamic_page_manager), + std::addressof(m_page_table_heap)); + m_memory_block_slab_manager.Initialize(std::addressof(m_dynamic_page_manager), + std::addressof(m_memory_block_heap)); + m_block_info_manager.Initialize(std::addressof(m_dynamic_page_manager), + std::addressof(m_block_info_heap)); + + // Set our managers. + this->SetManagers(m_memory_block_slab_manager, m_block_info_manager, m_page_table_manager); + + // Commit the memory reservation. + memory_reservation.Commit(); + + // Open reference to our resource limit. + m_resource_limit->Open(); + + // Set ourselves as initialized. + m_is_initialized = true; + + R_SUCCEED(); } void KSecureSystemResource::Finalize() { - // Unimplemented - UNREACHABLE(); + // Check that we have no outstanding allocations. + ASSERT(m_memory_block_slab_manager.GetUsed() == 0); + ASSERT(m_block_info_manager.GetUsed() == 0); + ASSERT(m_page_table_manager.GetUsed() == 0); + + // Free our secure memory. + KSystemControl::FreeSecureMemory(m_kernel, m_resource_address, m_resource_size, + static_cast<u32>(m_resource_pool)); + + // Release the memory reservation. + m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, + this->CalculateRequiredSecureMemorySize()); + + // Close reference to our resource limit. + m_resource_limit->Close(); } size_t KSecureSystemResource::CalculateRequiredSecureMemorySize(size_t size, KMemoryManager::Pool pool) { - // Unimplemented - UNREACHABLE(); + return KSystemControl::CalculateRequiredSecureMemorySize(size, static_cast<u32>(pool)); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 7df8fd7f7..a6deb50ec 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -122,16 +122,15 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress case ThreadType::Main: ASSERT(arg == 0); [[fallthrough]]; - case ThreadType::HighPriority: - [[fallthrough]]; - case ThreadType::Dummy: - [[fallthrough]]; case ThreadType::User: ASSERT(((owner == nullptr) || (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); ASSERT(((owner == nullptr) || (prio > Svc::LowestThreadPriority) || (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask())); break; + case ThreadType::HighPriority: + case ThreadType::Dummy: + break; case ThreadType::Kernel: UNIMPLEMENTED(); break; @@ -216,6 +215,7 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress // Setup the TLS, if needed. if (type == ThreadType::User) { R_TRY(owner->CreateThreadLocalRegion(std::addressof(m_tls_address))); + owner->GetMemory().ZeroBlock(m_tls_address, Svc::ThreadLocalRegionSize); } m_parent = owner; @@ -403,7 +403,7 @@ void KThread::StartTermination() { if (m_parent != nullptr) { m_parent->ReleaseUserException(this); if (m_parent->GetPinnedThread(GetCurrentCoreId(m_kernel)) == this) { - m_parent->UnpinCurrentThread(m_core_id); + m_parent->UnpinCurrentThread(); } } @@ -415,10 +415,6 @@ void KThread::StartTermination() { m_parent->ClearRunningThread(this); } - // Signal. - m_signaled = true; - KSynchronizationObject::NotifyAvailable(); - // Clear previous thread in KScheduler. KScheduler::ClearPreviousThread(m_kernel, this); @@ -437,6 +433,13 @@ void KThread::FinishTermination() { } } + // Acquire the scheduler lock. + KScopedSchedulerLock sl{m_kernel}; + + // Signal. + m_signaled = true; + KSynchronizationObject::NotifyAvailable(); + // Close the thread. this->Close(); } @@ -820,7 +823,7 @@ void KThread::CloneFpuStatus() { ASSERT(this->GetOwnerProcess() != nullptr); ASSERT(this->GetOwnerProcess() == GetCurrentProcessPointer(m_kernel)); - if (this->GetOwnerProcess()->Is64BitProcess()) { + if (this->GetOwnerProcess()->Is64Bit()) { // Clone FPSR and FPCR. ThreadContext64 cur_ctx{}; m_kernel.System().CurrentArmInterface().SaveContext(cur_ctx); @@ -923,7 +926,7 @@ Result KThread::GetThreadContext3(Common::ScratchBuffer<u8>& out) { // If we're not terminating, get the thread's user context. if (!this->IsTerminationRequested()) { - if (m_parent->Is64BitProcess()) { + if (m_parent->Is64Bit()) { // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. auto context = GetContext64(); context.pstate &= 0xFF0FFE20; @@ -1174,6 +1177,9 @@ Result KThread::Run() { owner->IncrementRunningThreadCount(); } + // Open a reference, now that we're running. + this->Open(); + // Set our state and finish. this->SetState(ThreadState::Runnable); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index d178c2453..e1f80b04f 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -721,6 +721,7 @@ private: // For core KThread implementation ThreadContext32 m_thread_context_32{}; ThreadContext64 m_thread_context_64{}; + Common::IntrusiveListNode m_process_list_node; Common::IntrusiveRedBlackTreeNode m_condvar_arbiter_tree_node{}; s32 m_priority{}; using ConditionVariableThreadTreeTraits = diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 24433d32b..4a1559291 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -101,35 +101,31 @@ struct KernelCore::Impl { void InitializeCores() { for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - cores[core_id]->Initialize((*application_process).Is64BitProcess()); + cores[core_id]->Initialize((*application_process).Is64Bit()); system.ApplicationMemory().SetCurrentPageTable(*application_process, core_id); } } - void CloseApplicationProcess() { - KProcess* old_process = application_process.exchange(nullptr); - if (old_process == nullptr) { - return; - } - - // old_process->Close(); - // TODO: The process should be destroyed based on accurate ref counting after - // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. - old_process->Finalize(); - old_process->Destroy(); + void TerminateApplicationProcess() { + application_process.load()->Terminate(); } void Shutdown() { is_shutting_down.store(true, std::memory_order_relaxed); SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); }); - process_list.clear(); - CloseServices(); + auto* old_process = application_process.exchange(nullptr); + if (old_process) { + old_process->Close(); + } + + process_list.clear(); + next_object_id = 0; - next_kernel_process_id = KProcess::InitialKIPIDMin; - next_user_process_id = KProcess::ProcessIDMin; + next_kernel_process_id = KProcess::InitialProcessIdMin; + next_user_process_id = KProcess::ProcessIdMin; next_thread_id = 1; global_handle_table->Finalize(); @@ -176,8 +172,6 @@ struct KernelCore::Impl { } } - CloseApplicationProcess(); - // Track kernel objects that were not freed on shutdown { std::scoped_lock lk{registered_objects_lock}; @@ -344,6 +338,8 @@ struct KernelCore::Impl { // Create the system page table managers. app_system_resource = std::make_unique<KSystemResource>(kernel); sys_system_resource = std::make_unique<KSystemResource>(kernel); + KAutoObject::Create(std::addressof(*app_system_resource)); + KAutoObject::Create(std::addressof(*sys_system_resource)); // Set the managers for the system resources. app_system_resource->SetManagers(*app_memory_block_manager, *app_block_info_manager, @@ -792,8 +788,8 @@ struct KernelCore::Impl { std::mutex registered_in_use_objects_lock; std::atomic<u32> next_object_id{0}; - std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin}; - std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin}; + std::atomic<u64> next_kernel_process_id{KProcess::InitialProcessIdMin}; + std::atomic<u64> next_user_process_id{KProcess::ProcessIdMin}; std::atomic<u64> next_thread_id{1}; // Lists all processes that exist in the current session. @@ -924,10 +920,6 @@ const KProcess* KernelCore::ApplicationProcess() const { return impl->application_process; } -void KernelCore::CloseApplicationProcess() { - impl->CloseApplicationProcess(); -} - const std::vector<KProcess*>& KernelCore::GetProcessList() const { return impl->process_list; } @@ -1128,8 +1120,8 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name, std::function<void()> func) { // Make a new process. KProcess* process = KProcess::Create(*this); - ASSERT(R_SUCCEEDED(KProcess::Initialize(process, System(), "", KProcess::ProcessType::Userland, - GetSystemResourceLimit()))); + ASSERT(R_SUCCEEDED( + process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); // Ensure that we don't hold onto any extra references. SCOPE_EXIT({ process->Close(); }); @@ -1156,8 +1148,8 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function // Make a new process. KProcess* process = KProcess::Create(*this); - ASSERT(R_SUCCEEDED(KProcess::Initialize(process, System(), "", KProcess::ProcessType::Userland, - GetSystemResourceLimit()))); + ASSERT(R_SUCCEEDED( + process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); // Ensure that we don't hold onto any extra references. SCOPE_EXIT({ process->Close(); }); @@ -1266,7 +1258,8 @@ const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const { void KernelCore::SuspendApplication(bool suspended) { const bool should_suspend{exception_exited || suspended}; - const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; + const auto activity = + should_suspend ? Svc::ProcessActivity::Paused : Svc::ProcessActivity::Runnable; // Get the application process. KScopedAutoObject<KProcess> process = ApplicationProcess(); @@ -1300,6 +1293,8 @@ void KernelCore::SuspendApplication(bool suspended) { } void KernelCore::ShutdownCores() { + impl->TerminateApplicationProcess(); + KScopedSchedulerLock lk{*this}; for (auto* thread : impl->shutdown_threads) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index d5b08eeb5..d8086c0ea 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -134,9 +134,6 @@ public: /// Retrieves a const pointer to the application process. const KProcess* ApplicationProcess() const; - /// Closes the application process. - void CloseApplicationProcess(); - /// Retrieves the list of processes. const std::vector<KProcess*>& GetProcessList() const; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 871d541d4..b76683969 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -4426,7 +4426,7 @@ void Call(Core::System& system, u32 imm) { auto& kernel = system.Kernel(); kernel.EnterSVCProfile(); - if (GetCurrentProcess(system.Kernel()).Is64BitProcess()) { + if (GetCurrentProcess(system.Kernel()).Is64Bit()) { Call64(system, imm); } else { Call32(system, imm); diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp index f99964028..ada998772 100644 --- a/src/core/hle/kernel/svc/svc_info.cpp +++ b/src/core/hle/kernel/svc/svc_info.cpp @@ -86,20 +86,19 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle R_SUCCEED(); case InfoType::TotalMemorySize: - *result = process->GetTotalPhysicalMemoryAvailable(); + *result = process->GetTotalUserPhysicalMemorySize(); R_SUCCEED(); case InfoType::UsedMemorySize: - *result = process->GetTotalPhysicalMemoryUsed(); + *result = process->GetUsedUserPhysicalMemorySize(); R_SUCCEED(); case InfoType::SystemResourceSizeTotal: - *result = process->GetSystemResourceSize(); + *result = process->GetTotalSystemResourceSize(); R_SUCCEED(); case InfoType::SystemResourceSizeUsed: - LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); - *result = process->GetSystemResourceUsage(); + *result = process->GetUsedSystemResourceSize(); R_SUCCEED(); case InfoType::ProgramId: @@ -111,20 +110,29 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle R_SUCCEED(); case InfoType::TotalNonSystemMemorySize: - *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); + *result = process->GetTotalNonSystemUserPhysicalMemorySize(); R_SUCCEED(); case InfoType::UsedNonSystemMemorySize: - *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); + *result = process->GetUsedNonSystemUserPhysicalMemorySize(); R_SUCCEED(); case InfoType::IsApplication: LOG_WARNING(Kernel_SVC, "(STUBBED) Assuming process is application"); - *result = true; + *result = process->IsApplication(); R_SUCCEED(); case InfoType::FreeThreadCount: - *result = process->GetFreeThreadCount(); + if (KResourceLimit* resource_limit = process->GetResourceLimit(); + resource_limit != nullptr) { + const auto current_value = + resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax); + const auto limit_value = + resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax); + *result = limit_value - current_value; + } else { + *result = 0; + } R_SUCCEED(); default: @@ -161,7 +169,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle case InfoType::RandomEntropy: R_UNLESS(handle == 0, ResultInvalidHandle); - R_UNLESS(info_sub_id < KProcess::RANDOM_ENTROPY_SIZE, ResultInvalidCombination); + R_UNLESS(info_sub_id < 4, ResultInvalidCombination); *result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id); R_SUCCEED(); diff --git a/src/core/hle/kernel/svc/svc_lock.cpp b/src/core/hle/kernel/svc/svc_lock.cpp index 1d7bc4246..5f0833fcb 100644 --- a/src/core/hle/kernel/svc/svc_lock.cpp +++ b/src/core/hle/kernel/svc/svc_lock.cpp @@ -17,7 +17,7 @@ Result ArbitrateLock(Core::System& system, Handle thread_handle, u64 address, u3 R_UNLESS(!IsKernelAddress(address), ResultInvalidCurrentMemory); R_UNLESS(Common::IsAligned(address, sizeof(u32)), ResultInvalidAddress); - R_RETURN(GetCurrentProcess(system.Kernel()).WaitForAddress(thread_handle, address, tag)); + R_RETURN(KConditionVariable::WaitForAddress(system.Kernel(), thread_handle, address, tag)); } /// Unlock a mutex @@ -28,7 +28,7 @@ Result ArbitrateUnlock(Core::System& system, u64 address) { R_UNLESS(!IsKernelAddress(address), ResultInvalidCurrentMemory); R_UNLESS(Common::IsAligned(address, sizeof(u32)), ResultInvalidAddress); - R_RETURN(GetCurrentProcess(system.Kernel()).SignalToAddress(address)); + R_RETURN(KConditionVariable::SignalToAddress(system.Kernel(), address)); } Result ArbitrateLock64(Core::System& system, Handle thread_handle, uint64_t address, uint32_t tag) { diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp index d3545f232..99330d02a 100644 --- a/src/core/hle/kernel/svc/svc_physical_memory.cpp +++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp @@ -46,7 +46,7 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) { KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())}; auto& page_table{current_process->GetPageTable()}; - if (current_process->GetSystemResourceSize() == 0) { + if (current_process->GetTotalSystemResourceSize() == 0) { LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); R_THROW(ResultInvalidState); } @@ -95,7 +95,7 @@ Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) { KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())}; auto& page_table{current_process->GetPageTable()}; - if (current_process->GetSystemResourceSize() == 0) { + if (current_process->GetTotalSystemResourceSize() == 0) { LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); R_THROW(ResultInvalidState); } diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp index 8ebc1bd1c..6c79cfd8d 100644 --- a/src/core/hle/kernel/svc/svc_synchronization.cpp +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp @@ -132,7 +132,7 @@ void SynchronizePreemptionState(Core::System& system) { GetCurrentThread(kernel).ClearInterruptFlag(); // Unpin the current thread. - cur_process->UnpinCurrentThread(core_id); + cur_process->UnpinCurrentThread(); } } diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp index 933b82e30..755fd62b5 100644 --- a/src/core/hle/kernel/svc/svc_thread.cpp +++ b/src/core/hle/kernel/svc/svc_thread.cpp @@ -85,10 +85,6 @@ Result StartThread(Core::System& system, Handle thread_handle) { // Try to start the thread. R_TRY(thread->Run()); - // If we succeeded, persist a reference to the thread. - thread->Open(); - system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe()); - R_SUCCEED(); } @@ -99,7 +95,6 @@ void ExitThread(Core::System& system) { auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); system.GlobalSchedulerContext().RemoveThread(current_thread); current_thread->Exit(); - system.Kernel().UnregisterInUseObject(current_thread); } /// Sleep the current thread @@ -260,7 +255,7 @@ Result GetThreadList(Core::System& system, s32* out_num_threads, u64 out_thread_ auto list_iter = thread_list.cbegin(); for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { - memory.Write64(out_thread_ids, (*list_iter)->GetThreadId()); + memory.Write64(out_thread_ids, list_iter->GetThreadId()); out_thread_ids += sizeof(u64); } diff --git a/src/core/hle/kernel/svc_generator.py b/src/core/hle/kernel/svc_generator.py index 7fcbb1ba1..5531faac6 100644 --- a/src/core/hle/kernel/svc_generator.py +++ b/src/core/hle/kernel/svc_generator.py @@ -592,7 +592,7 @@ void Call(Core::System& system, u32 imm) { auto& kernel = system.Kernel(); kernel.EnterSVCProfile(); - if (GetCurrentProcess(system.Kernel()).Is64BitProcess()) { + if (GetCurrentProcess(system.Kernel()).Is64Bit()) { Call64(system, imm); } else { Call32(system, imm); diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 251e6013c..50de02e36 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -604,13 +604,57 @@ enum class ProcessActivity : u32 { Paused, }; +enum class CreateProcessFlag : u32 { + // Is 64 bit? + Is64Bit = (1 << 0), + + // What kind of address space? + AddressSpaceShift = 1, + AddressSpaceMask = (7 << AddressSpaceShift), + AddressSpace32Bit = (0 << AddressSpaceShift), + AddressSpace64BitDeprecated = (1 << AddressSpaceShift), + AddressSpace32BitWithoutAlias = (2 << AddressSpaceShift), + AddressSpace64Bit = (3 << AddressSpaceShift), + + // Should JIT debug be done on crash? + EnableDebug = (1 << 4), + + // Should ASLR be enabled for the process? + EnableAslr = (1 << 5), + + // Is the process an application? + IsApplication = (1 << 6), + + // 4.x deprecated: Should use secure memory? + DeprecatedUseSecureMemory = (1 << 7), + + // 5.x+ Pool partition type. + PoolPartitionShift = 7, + PoolPartitionMask = (0xF << PoolPartitionShift), + PoolPartitionApplication = (0 << PoolPartitionShift), + PoolPartitionApplet = (1 << PoolPartitionShift), + PoolPartitionSystem = (2 << PoolPartitionShift), + PoolPartitionSystemNonSecure = (3 << PoolPartitionShift), + + // 7.x+ Should memory allocation be optimized? This requires IsApplication. + OptimizeMemoryAllocation = (1 << 11), + + // 11.x+ DisableDeviceAddressSpaceMerge. + DisableDeviceAddressSpaceMerge = (1 << 12), + + // Mask of all flags. + All = Is64Bit | AddressSpaceMask | EnableDebug | EnableAslr | IsApplication | + PoolPartitionMask | OptimizeMemoryAllocation | DisableDeviceAddressSpaceMerge, +}; +DECLARE_ENUM_FLAG_OPERATORS(CreateProcessFlag); + struct CreateProcessParameter { std::array<char, 12> name; u32 version; u64 program_id; u64 code_address; s32 code_num_pages; - u32 flags; + CreateProcessFlag flags; Handle reslimit; s32 system_resource_num_pages; }; diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 1b1c8190e..f21553644 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -3,11 +3,13 @@ #include <algorithm> #include <array> + #include "common/common_types.h" #include "common/fs/file.h" #include "common/fs/path_util.h" #include "common/logging/log.h" #include "common/polyfill_ranges.h" +#include "common/stb.h" #include "common/string_util.h" #include "common/swap.h" #include "core/constants.h" @@ -38,9 +40,36 @@ static std::filesystem::path GetImagePath(const Common::UUID& uuid) { fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); } -static constexpr u32 SanitizeJPEGSize(std::size_t size) { +static void JPGToMemory(void* context, void* data, int len) { + std::vector<u8>* jpg_image = static_cast<std::vector<u8>*>(context); + unsigned char* jpg = static_cast<unsigned char*>(data); + jpg_image->insert(jpg_image->end(), jpg, jpg + len); +} + +static void SanitizeJPEGImageSize(std::vector<u8>& image) { constexpr std::size_t max_jpeg_image_size = 0x20000; - return static_cast<u32>(std::min(size, max_jpeg_image_size)); + constexpr int profile_dimensions = 256; + int original_width, original_height, color_channels; + + const auto plain_image = + stbi_load_from_memory(image.data(), static_cast<int>(image.size()), &original_width, + &original_height, &color_channels, STBI_rgb); + + // Resize image to match 256*256 + if (original_width != profile_dimensions || original_height != profile_dimensions) { + // Use vector instead of array to avoid overflowing the stack + std::vector<u8> out_image(profile_dimensions * profile_dimensions * STBI_rgb); + stbir_resize_uint8_srgb(plain_image, original_width, original_height, 0, out_image.data(), + profile_dimensions, profile_dimensions, 0, STBI_rgb, 0, + STBIR_FILTER_BOX); + image.clear(); + if (!stbi_write_jpg_to_func(JPGToMemory, &image, profile_dimensions, profile_dimensions, + STBI_rgb, out_image.data(), 0)) { + LOG_ERROR(Service_ACC, "Failed to resize the user provided image."); + } + } + + image.resize(std::min(image.size(), max_jpeg_image_size)); } class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> { @@ -339,19 +368,20 @@ protected: LOG_WARNING(Service_ACC, "Failed to load user provided image! Falling back to built-in backup..."); ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG); - rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); + rb.Push(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); return; } - const u32 size = SanitizeJPEGSize(image.GetSize()); - std::vector<u8> buffer(size); + std::vector<u8> buffer(image.GetSize()); if (image.Read(buffer) != buffer.size()) { LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); } + SanitizeJPEGImageSize(buffer); + ctx.WriteBuffer(buffer); - rb.Push<u32>(size); + rb.Push(static_cast<u32>(buffer.size())); } void GetImageSize(HLERequestContext& ctx) { @@ -365,10 +395,18 @@ protected: if (!image.IsOpen()) { LOG_WARNING(Service_ACC, "Failed to load user provided image! Falling back to built-in backup..."); - rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); - } else { - rb.Push(SanitizeJPEGSize(image.GetSize())); + rb.Push(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); + return; } + + std::vector<u8> buffer(image.GetSize()); + + if (image.Read(buffer) != buffer.size()) { + LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); + } + + SanitizeJPEGImageSize(buffer); + rb.Push(static_cast<u32>(buffer.size())); } void Store(HLERequestContext& ctx) { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 98765b81a..cc643ea09 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -23,6 +23,7 @@ #include "core/hle/service/am/applets/applet_cabinet.h" #include "core/hle/service/am/applets/applet_mii_edit_types.h" #include "core/hle/service/am/applets/applet_profile_select.h" +#include "core/hle/service/am/applets/applet_software_keyboard_types.h" #include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/am/idle.h" @@ -31,6 +32,7 @@ #include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/apm/apm_interface.h" #include "core/hle/service/bcat/backend/backend.h" +#include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_types.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ipc_helpers.h" @@ -702,9 +704,17 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& c void ISelfController::SaveCurrentScreenshot(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto album_report_option = rp.PopEnum<Capture::AlbumReportOption>(); + const auto report_option = rp.PopEnum<Capture::AlbumReportOption>(); - LOG_WARNING(Service_AM, "(STUBBED) called. album_report_option={}", album_report_option); + LOG_INFO(Service_AM, "called, report_option={}", report_option); + + const auto screenshot_service = + system.ServiceManager().GetService<Service::Capture::IScreenShotApplicationService>( + "caps:su"); + + if (screenshot_service) { + screenshot_service->CaptureAndSaveScreenshot(report_option); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -796,7 +806,9 @@ ILockAccessor::ILockAccessor(Core::System& system_) lock_event = service_context.CreateEvent("ILockAccessor::LockEvent"); } -ILockAccessor::~ILockAccessor() = default; +ILockAccessor::~ILockAccessor() { + service_context.CloseEvent(lock_event); +}; void ILockAccessor::TryLock(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -909,7 +921,9 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); } -ICommonStateGetter::~ICommonStateGetter() = default; +ICommonStateGetter::~ICommonStateGetter() { + service_context.CloseEvent(sleep_lock_event); +}; void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); @@ -1558,7 +1572,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) {16, nullptr, "GetMainAppletStorageId"}, {17, nullptr, "GetCallerAppletIdentityInfoStack"}, {18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"}, - {19, nullptr, "GetDesirableKeyboardLayout"}, + {19, &ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout, "GetDesirableKeyboardLayout"}, {20, nullptr, "PopExtraStorage"}, {25, nullptr, "GetPopExtraStorageEvent"}, {30, nullptr, "UnpopInData"}, @@ -1577,7 +1591,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) {120, nullptr, "GetLaunchStorageInfoForDebug"}, {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, {140, nullptr, "SetApplicationMemoryReservation"}, - {150, nullptr, "ShouldSetGpuTimeSliceManually"}, + {150, &ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually, "ShouldSetGpuTimeSliceManually"}, }; // clang-format on RegisterHandlers(functions); @@ -1592,6 +1606,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) case Applets::AppletId::PhotoViewer: PushInShowAlbum(); break; + case Applets::AppletId::SoftwareKeyboard: + PushInShowSoftwareKeyboard(); + break; default: break; } @@ -1668,6 +1685,14 @@ void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& rb.PushRaw(applet_info); } +void ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(0); +} + void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) { const Service::Account::ProfileManager manager{}; bool is_empty{true}; @@ -1687,6 +1712,14 @@ void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& rb.Push(user_count); } +void ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.Push<u8>(0); +} + void ILibraryAppletSelfAccessor::PushInShowAlbum() { const Applets::CommonArguments arguments{ .arguments_version = Applets::CommonArgumentVersion::Version3, @@ -1755,6 +1788,61 @@ void ILibraryAppletSelfAccessor::PushInShowMiiEditData() { queue_data.emplace_back(std::move(argument_data)); } +void ILibraryAppletSelfAccessor::PushInShowSoftwareKeyboard() { + const Applets::CommonArguments arguments{ + .arguments_version = Applets::CommonArgumentVersion::Version3, + .size = Applets::CommonArgumentSize::Version3, + .library_version = static_cast<u32>(Applets::SwkbdAppletVersion::Version524301), + .theme_color = Applets::ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + std::vector<char16_t> initial_string(0); + + const Applets::SwkbdConfigCommon swkbd_config{ + .type = Applets::SwkbdType::Qwerty, + .ok_text{}, + .left_optional_symbol_key{}, + .right_optional_symbol_key{}, + .use_prediction = false, + .key_disable_flags{}, + .initial_cursor_position = Applets::SwkbdInitialCursorPosition::Start, + .header_text{}, + .sub_text{}, + .guide_text{}, + .max_text_length = 500, + .min_text_length = 0, + .password_mode = Applets::SwkbdPasswordMode::Disabled, + .text_draw_type = Applets::SwkbdTextDrawType::Box, + .enable_return_button = true, + .use_utf8 = false, + .use_blur_background = true, + .initial_string_offset{}, + .initial_string_length = static_cast<u32>(initial_string.size()), + .user_dictionary_offset{}, + .user_dictionary_entries{}, + .use_text_check = false, + }; + + Applets::SwkbdConfigNew swkbd_config_new{}; + + std::vector<u8> argument_data(sizeof(arguments)); + std::vector<u8> swkbd_data(sizeof(swkbd_config) + sizeof(swkbd_config_new)); + std::vector<u8> work_buffer(swkbd_config.initial_string_length * sizeof(char16_t)); + + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + std::memcpy(swkbd_data.data(), &swkbd_config, sizeof(swkbd_config)); + std::memcpy(swkbd_data.data() + sizeof(swkbd_config), &swkbd_config_new, + sizeof(Applets::SwkbdConfigNew)); + std::memcpy(work_buffer.data(), initial_string.data(), + swkbd_config.initial_string_length * sizeof(char16_t)); + + queue_data.emplace_back(std::move(argument_data)); + queue_data.emplace_back(std::move(swkbd_data)); + queue_data.emplace_back(std::move(work_buffer)); +} + IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_) : ServiceFramework{system_, "IAppletCommonFunctions"} { // clang-format off diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 64b3f3fe2..8f8cb8a9e 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -347,11 +347,14 @@ private: void GetLibraryAppletInfo(HLERequestContext& ctx); void ExitProcessAndReturn(HLERequestContext& ctx); void GetCallerAppletIdentityInfo(HLERequestContext& ctx); + void GetDesirableKeyboardLayout(HLERequestContext& ctx); void GetMainAppletAvailableUsers(HLERequestContext& ctx); + void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx); void PushInShowAlbum(); void PushInShowCabinetData(); void PushInShowMiiEditData(); + void PushInShowSoftwareKeyboard(); std::deque<std::vector<u8>> queue_data; }; diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index 19ed184e8..b379dadeb 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -25,7 +25,9 @@ Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_, service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent"); } -Cabinet::~Cabinet() = default; +Cabinet::~Cabinet() { + service_context.CloseEvent(availability_change_event); +}; void Cabinet::Initialize() { Applet::Initialize(); diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index 1c9a1dc29..b0ea2b381 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -330,8 +330,7 @@ void WebBrowser::ExtractOfflineRomFS() { LOG_DEBUG(Service_AM, "Extracting RomFS to {}", Common::FS::PathToUTF8String(offline_cache_dir)); - const auto extracted_romfs_dir = - FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); + const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs); const auto temp_dir = system.GetFilesystem()->CreateDirectory( Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index f02bbc450..0bf2598b7 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -69,6 +69,30 @@ enum class AppletId : u32 { MyPage = 0x1A, }; +enum class AppletProgramId : u64 { + QLaunch = 0x0100000000001000ull, + Auth = 0x0100000000001001ull, + Cabinet = 0x0100000000001002ull, + Controller = 0x0100000000001003ull, + DataErase = 0x0100000000001004ull, + Error = 0x0100000000001005ull, + NetConnect = 0x0100000000001006ull, + ProfileSelect = 0x0100000000001007ull, + SoftwareKeyboard = 0x0100000000001008ull, + MiiEdit = 0x0100000000001009ull, + Web = 0x010000000000100Aull, + Shop = 0x010000000000100Bull, + OverlayDisplay = 0x010000000000100Cull, + PhotoViewer = 0x010000000000100Dull, + Settings = 0x010000000000100Eull, + OfflineWeb = 0x010000000000100Full, + LoginShare = 0x0100000000001010ull, + WebAuth = 0x0100000000001011ull, + Starter = 0x0100000000001012ull, + MyPage = 0x0100000000001013ull, + MaxProgramId = 0x0100000000001FFFull, +}; + enum class LibraryAppletMode : u32 { AllForeground = 0, Background = 1, diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp index 9c9454b99..96b225d5f 100644 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -2,13 +2,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <sstream> -#include <stb_image.h> -#include <stb_image_resize.h> -#include <stb_image_write.h> #include "common/fs/file.h" #include "common/fs/path_util.h" #include "common/logging/log.h" +#include "common/stb.h" #include "core/core.h" #include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_result.h" @@ -230,12 +228,14 @@ Result AlbumManager::LoadAlbumScreenShotThumbnail( Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, - std::span<const u8> image_data, u64 aruid) { - return SaveScreenShot(out_entry, attribute, {}, image_data, aruid); + AlbumReportOption report_option, std::span<const u8> image_data, + u64 aruid) { + return SaveScreenShot(out_entry, attribute, report_option, {}, image_data, aruid); } Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, + AlbumReportOption report_option, const ApplicationData& app_data, std::span<const u8> image_data, u64 aruid) { const u64 title_id = system.GetApplicationProcessProgramID(); @@ -409,6 +409,16 @@ Result AlbumManager::LoadImage(std::span<u8> out_image, const std::filesystem::p return ResultSuccess; } +void AlbumManager::FlipVerticallyOnWrite(bool flip) { + stbi_flip_vertically_on_write(flip); +} + +static void PNGToMemory(void* context, void* data, int len) { + std::vector<u8>* png_image = static_cast<std::vector<u8>*>(context); + unsigned char* png = static_cast<unsigned char*>(data); + png_image->insert(png_image->end(), png, png + len); +} + Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const u8> image, u64 title_id, const AlbumFileDateTime& date) const { const auto screenshot_path = @@ -422,16 +432,12 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const const Common::FS::IOFile db_file{file_path, Common::FS::FileAccessMode::Write, Common::FS::FileType::BinaryFile}; - s32 len; - const u8* png = stbi_write_png_to_mem(image.data(), 0, 1280, 720, STBI_rgb_alpha, &len); - - if (!png) { + std::vector<u8> png_image; + if (!stbi_write_png_to_func(PNGToMemory, &png_image, 1280, 720, STBI_rgb_alpha, image.data(), + 0)) { return ResultFileCountLimit; } - std::vector<u8> png_image(len); - std::memcpy(png_image.data(), png, len); - if (db_file.Write(png_image) != png_image.size()) { return ResultFileCountLimit; } diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h index 44d85117f..e20c70c7b 100644 --- a/src/core/hle/service/caps/caps_manager.h +++ b/src/core/hle/service/caps/caps_manager.h @@ -59,14 +59,17 @@ public: const ScreenShotDecodeOption& decoder_options) const; Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, - std::span<const u8> image_data, u64 aruid); - Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, - const ApplicationData& app_data, std::span<const u8> image_data, + AlbumReportOption report_option, std::span<const u8> image_data, u64 aruid); + Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, + AlbumReportOption report_option, const ApplicationData& app_data, + std::span<const u8> image_data, u64 aruid); Result SaveEditedScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, const AlbumFileId& file_id, std::span<const u8> image_data); + void FlipVerticallyOnWrite(bool flip); + private: static constexpr std::size_t NandAlbumFileLimit = 1000; static constexpr std::size_t SdAlbumFileLimit = 10000; diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index 1ba2b7972..eab023568 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp @@ -34,7 +34,7 @@ void IScreenShotService::SaveScreenShotEx0(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { ScreenShotAttribute attribute{}; - u32 report_option{}; + AlbumReportOption report_option{}; INSERT_PADDING_BYTES(0x4); u64 applet_resource_user_id{}; }; @@ -49,13 +49,16 @@ void IScreenShotService::SaveScreenShotEx0(HLERequestContext& ctx) { parameters.applet_resource_user_id); ApplicationAlbumEntry entry{}; - const auto result = manager->SaveScreenShot(entry, parameters.attribute, image_data_buffer, - parameters.applet_resource_user_id); + manager->FlipVerticallyOnWrite(false); + const auto result = + manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, + image_data_buffer, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 10}; rb.Push(result); rb.PushRaw(entry); } + void IScreenShotService::SaveEditedScreenShotEx1(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { @@ -83,6 +86,7 @@ void IScreenShotService::SaveEditedScreenShotEx1(HLERequestContext& ctx) { image_data_buffer.size(), thumbnail_image_data_buffer.size()); ApplicationAlbumEntry entry{}; + manager->FlipVerticallyOnWrite(false); const auto result = manager->SaveEditedScreenShot(entry, parameters.attribute, parameters.file_id, image_data_buffer); diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index e85625ee4..296b07b00 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp @@ -2,10 +2,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_types.h" #include "core/hle/service/ipc_helpers.h" +#include "video_core/renderer_base.h" namespace Service::Capture { @@ -58,8 +60,10 @@ void IScreenShotApplicationService::SaveScreenShotEx0(HLERequestContext& ctx) { parameters.applet_resource_user_id); ApplicationAlbumEntry entry{}; - const auto result = manager->SaveScreenShot(entry, parameters.attribute, image_data_buffer, - parameters.applet_resource_user_id); + manager->FlipVerticallyOnWrite(false); + const auto result = + manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, + image_data_buffer, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 10}; rb.Push(result); @@ -88,13 +92,43 @@ void IScreenShotApplicationService::SaveScreenShotEx1(HLERequestContext& ctx) { ApplicationAlbumEntry entry{}; ApplicationData app_data{}; std::memcpy(&app_data, app_data_buffer.data(), sizeof(ApplicationData)); + manager->FlipVerticallyOnWrite(false); const auto result = - manager->SaveScreenShot(entry, parameters.attribute, app_data, image_data_buffer, - parameters.applet_resource_user_id); + manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, app_data, + image_data_buffer, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 10}; rb.Push(result); rb.PushRaw(entry); } +void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption report_option) { + auto& renderer = system.Renderer(); + Layout::FramebufferLayout layout = + Layout::DefaultFrameLayout(screenshot_width, screenshot_height); + + const Capture::ScreenShotAttribute attribute{ + .unknown_0{}, + .orientation = Capture::AlbumImageOrientation::None, + .unknown_1{}, + .unknown_2{}, + }; + + renderer.RequestScreenshot( + image_data.data(), + [attribute, report_option, this](bool invert_y) { + // Convert from BGRA to RGBA + for (std::size_t i = 0; i < image_data.size(); i += bytes_per_pixel) { + const u8 temp = image_data[i]; + image_data[i] = image_data[i + 2]; + image_data[i + 2] = temp; + } + + Capture::ApplicationAlbumEntry entry{}; + manager->FlipVerticallyOnWrite(invert_y); + manager->SaveScreenShot(entry, attribute, report_option, image_data, {}); + }, + layout); +} + } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index 89e71f506..21912e95f 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h @@ -10,6 +10,7 @@ class System; } namespace Service::Capture { +enum class AlbumReportOption : s32; class AlbumManager; class IScreenShotApplicationService final : public ServiceFramework<IScreenShotApplicationService> { @@ -18,11 +19,19 @@ public: std::shared_ptr<AlbumManager> album_manager); ~IScreenShotApplicationService() override; + void CaptureAndSaveScreenshot(AlbumReportOption report_option); + private: + static constexpr std::size_t screenshot_width = 1280; + static constexpr std::size_t screenshot_height = 720; + static constexpr std::size_t bytes_per_pixel = 4; + void SetShimLibraryVersion(HLERequestContext& ctx); void SaveScreenShotEx0(HLERequestContext& ctx); void SaveScreenShotEx1(HLERequestContext& ctx); + std::array<u8, screenshot_width * screenshot_height * bytes_per_pixel> image_data; + std::shared_ptr<AlbumManager> manager; }; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index bc822f19e..21695bda2 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -1108,9 +1108,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { shared_memory->sixaxis_dual_right_properties.raw = 0; shared_memory->sixaxis_left_properties.raw = 0; shared_memory->sixaxis_right_properties.raw = 0; - shared_memory->battery_level_dual = 0; - shared_memory->battery_level_left = 0; - shared_memory->battery_level_right = 0; + shared_memory->battery_level_dual = Core::HID::NpadBatteryLevel::Empty; + shared_memory->battery_level_left = Core::HID::NpadBatteryLevel::Empty; + shared_memory->battery_level_right = Core::HID::NpadBatteryLevel::Empty; shared_memory->fullkey_color = { .attribute = ColorAttribute::NoController, .fullkey = {}, diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp index 14c67e454..73a2a2b91 100644 --- a/src/core/hle/service/hid/controllers/palma.cpp +++ b/src/core/hle/service/hid/controllers/palma.cpp @@ -19,7 +19,9 @@ Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); } -Controller_Palma::~Controller_Palma() = default; +Controller_Palma::~Controller_Palma() { + service_context.CloseEvent(operation_complete_event); +}; void Controller_Palma::OnInit() {} diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 4d70006c1..1d4101be9 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1353,7 +1353,7 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) { void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - bool unintended_home_button_input_protection; + bool is_enabled; INSERT_PADDING_BYTES_NOINIT(3); Core::HID::NpadIdType npad_id; u64 applet_resource_user_id; @@ -1364,13 +1364,11 @@ void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( - parameters.unintended_home_button_input_protection, parameters.npad_id); + parameters.is_enabled, parameters.npad_id); - LOG_WARNING(Service_HID, - "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," - "applet_resource_user_id={}", - parameters.unintended_home_button_input_protection, parameters.npad_id, - parameters.applet_resource_user_id); + LOG_DEBUG(Service_HID, + "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}", + parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); @@ -2757,6 +2755,10 @@ public: joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent"); } + ~HidSys() { + service_context.CloseEvent(joy_detach_event); + }; + private: void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { LOG_WARNING(Service_HID, "called"); diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp index ee522c36e..8c44f93e8 100644 --- a/src/core/hle/service/hid/hidbus/hidbus_base.cpp +++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp @@ -13,7 +13,10 @@ HidbusBase::HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& ser : system(system_), service_context(service_context_) { send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent"); } -HidbusBase::~HidbusBase() = default; + +HidbusBase::~HidbusBase() { + service_context.CloseEvent(send_command_async_event); +}; void HidbusBase::ActivateDevice() { if (is_activated) { diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h index 65eb7ea02..0816784e0 100644 --- a/src/core/hle/service/hid/ring_lifo.h +++ b/src/core/hle/service/hid/ring_lifo.h @@ -32,15 +32,15 @@ struct Lifo { } std::size_t GetPreviousEntryIndex() const { - return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count); + return static_cast<size_t>((buffer_tail + max_buffer_size - 1) % max_buffer_size); } std::size_t GetNextEntryIndex() const { - return static_cast<size_t>((buffer_tail + 1) % total_buffer_count); + return static_cast<size_t>((buffer_tail + 1) % max_buffer_size); } void WriteNextEntry(const State& new_state) { - if (buffer_count < total_buffer_count - 1) { + if (buffer_count < static_cast<s64>(max_buffer_size) - 1) { buffer_count++; } buffer_tail = GetNextEntryIndex(); diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index 6a313a03b..f51e63564 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -21,10 +21,8 @@ ServiceContext::ServiceContext(Core::System& system_, std::string name_) // Create the process. process = Kernel::KProcess::Create(kernel); - ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), - Kernel::KProcess::ProcessType::KernelInternal, - kernel.GetSystemResourceLimit()) - .IsSuccess()); + ASSERT(R_SUCCEEDED(process->Initialize(Kernel::Svc::CreateProcessParameter{}, + kernel.GetSystemResourceLimit(), false))); // Register the process. Kernel::KProcess::Register(kernel, process); diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp index 2dbe29616..ed66f6f5b 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp @@ -41,7 +41,7 @@ bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer. if (!use_async_buffer) { - return max_acquired_buffer_count; + return 0; } if (dequeue_buffer_cannot_block || async) { @@ -52,7 +52,7 @@ s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { } s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const { - return GetMinUndequeuedBufferCountLocked(async) + 1; + return GetMinUndequeuedBufferCountLocked(async); } s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const { @@ -61,7 +61,7 @@ s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const { if (override_max_buffer_count != 0) { ASSERT(override_max_buffer_count >= min_buffer_count); - max_buffer_count = override_max_buffer_count; + return override_max_buffer_count; } // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index dc6917d5d..6e7a49658 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -134,7 +134,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); if (async && core->override_max_buffer_count) { if (core->override_max_buffer_count < max_buffer_count) { - LOG_ERROR(Service_Nvnflinger, "async mode is invalid with buffer count override"); + *found = BufferQueueCore::INVALID_BUFFER_SLOT; return Status::BadValue; } } @@ -142,7 +142,8 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St // Free up any buffers that are in slots beyond the max buffer count for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { ASSERT(slots[s].buffer_state == BufferState::Free); - if (slots[s].graphic_buffer != nullptr) { + if (slots[s].graphic_buffer != nullptr && slots[s].buffer_state == BufferState::Free && + !slots[s].is_preallocated) { core->FreeBufferLocked(s); *return_flags |= Status::ReleaseAllBuffers; } diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index a07c621d9..bebb45eae 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -66,7 +66,6 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_ "ScreenComposition", [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - { const auto lock_guard = Lock(); } vsync_signal.Set(); return std::chrono::nanoseconds(GetNextTicks()); }); @@ -99,6 +98,7 @@ Nvnflinger::~Nvnflinger() { } ShutdownLayers(); + vsync_thread = {}; if (nvdrv) { nvdrv->Close(disp_fd); @@ -106,6 +106,7 @@ Nvnflinger::~Nvnflinger() { } void Nvnflinger::ShutdownLayers() { + const auto lock_guard = Lock(); for (auto& display : displays) { for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { display.GetLayer(layer).Core().NotifyShutdown(); @@ -229,16 +230,6 @@ VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) { return display->FindLayer(layer_id); } -const VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) const { - const auto* const display = FindDisplay(display_id); - - if (display == nullptr) { - return nullptr; - } - - return display->FindLayer(layer_id); -} - VI::Layer* Nvnflinger::FindOrCreateLayer(u64 display_id, u64 layer_id) { auto* const display = FindDisplay(display_id); @@ -288,7 +279,6 @@ void Nvnflinger::Compose() { auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); ASSERT(nvdisp); - guard->unlock(); Common::Rectangle<int> crop_rect{ static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; @@ -299,7 +289,6 @@ void Nvnflinger::Compose() { buffer.fence.fences, buffer.fence.num_fences); MicroProfileFlip(); - guard->lock(); swap_interval = buffer.swap_interval; diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index 14c783582..959d8b46b 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -117,9 +117,6 @@ private: /// Finds the layer identified by the specified ID in the desired display. [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); - /// Finds the layer identified by the specified ID in the desired display. - [[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; - /// Finds the layer identified by the specified ID in the desired display, /// or creates the layer if it is not found. /// To be used when the system expects the specified ID to already exist. diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp index 938330dd0..6a7fd72bc 100644 --- a/src/core/hle/service/pctl/pctl_module.cpp +++ b/src/core/hle/service/pctl/pctl_module.cpp @@ -141,6 +141,12 @@ public: service_context.CreateEvent("IParentalControlService::RequestSuspensionEvent"); } + ~IParentalControlService() { + service_context.CloseEvent(synchronization_event); + service_context.CloseEvent(unlinked_event); + service_context.CloseEvent(request_suspension_event); + }; + private: bool CheckFreeCommunicationPermissionImpl() const { if (states.temporary_unlocked) { diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index f9cf2dda3..d92499f05 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -37,7 +37,7 @@ std::optional<Kernel::KProcess*> SearchProcessList( void GetApplicationPidGeneric(HLERequestContext& ctx, const std::vector<Kernel::KProcess*>& process_list) { const auto process = SearchProcessList(process_list, [](const auto& proc) { - return proc->GetProcessId() == Kernel::KProcess::ProcessIDMin; + return proc->GetProcessId() == Kernel::KProcess::ProcessIdMin; }); IPC::ResponseBuilder rb{ctx, 4}; diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 85849d5f3..dd652ca42 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -39,6 +39,18 @@ bool IsConnectionBased(Type type) { } } +template <typename T> +T GetValue(std::span<const u8> buffer) { + T t{}; + std::memcpy(&t, buffer.data(), std::min(sizeof(T), buffer.size())); + return t; +} + +template <typename T> +void PutValue(std::span<u8> buffer, const T& t) { + std::memcpy(buffer.data(), &t, std::min(sizeof(T), buffer.size())); +} + } // Anonymous namespace void BSD::PollWork::Execute(BSD* bsd) { @@ -316,22 +328,12 @@ void BSD::SetSockOpt(HLERequestContext& ctx) { const s32 fd = rp.Pop<s32>(); const u32 level = rp.Pop<u32>(); const OptName optname = static_cast<OptName>(rp.Pop<u32>()); - - const auto buffer = ctx.ReadBuffer(); - const u8* optval = buffer.empty() ? nullptr : buffer.data(); - size_t optlen = buffer.size(); - - std::array<u64, 2> values; - if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) { - std::memcpy(values.data(), buffer.data(), sizeof(values)); - optlen = sizeof(values); - optval = reinterpret_cast<const u8*>(values.data()); - } + const auto optval = ctx.ReadBuffer(); LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level, - static_cast<u32>(optname), optlen); + static_cast<u32>(optname), optval.size()); - BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval)); + BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval)); } void BSD::Shutdown(HLERequestContext& ctx) { @@ -521,18 +523,19 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) { - if (write_buffer.size() < nfds * sizeof(PollFD)) { - return {-1, Errno::INVAL}; - } - - if (nfds == 0) { + if (nfds <= 0) { // When no entries are provided, -1 is returned with errno zero return {-1, Errno::SUCCESS}; } + if (read_buffer.size() < nfds * sizeof(PollFD)) { + return {-1, Errno::INVAL}; + } + if (write_buffer.size() < nfds * sizeof(PollFD)) { + return {-1, Errno::INVAL}; + } - const size_t length = std::min(read_buffer.size(), write_buffer.size()); std::vector<PollFD> fds(nfds); - std::memcpy(fds.data(), read_buffer.data(), length); + std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD)); if (timeout >= 0) { const s64 seconds = timeout / 1000; @@ -580,7 +583,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con for (size_t i = 0; i < num; ++i) { fds[i].revents = Translate(host_pollfds[i].revents); } - std::memcpy(write_buffer.data(), fds.data(), length); + std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD)); return Translate(result); } @@ -608,8 +611,7 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) { new_descriptor.is_connection_based = descriptor.is_connection_based; const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); - const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size()); - std::memcpy(write_buffer.data(), &guest_addr_in, length); + PutValue(write_buffer, guest_addr_in); return {new_fd, Errno::SUCCESS}; } @@ -619,8 +621,7 @@ Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) { return Errno::BADF; } ASSERT(addr.size() == sizeof(SockAddrIn)); - SockAddrIn addr_in; - std::memcpy(&addr_in, addr.data(), sizeof(addr_in)); + auto addr_in = GetValue<SockAddrIn>(addr); return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); } @@ -631,8 +632,7 @@ Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) { } UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); - SockAddrIn addr_in; - std::memcpy(&addr_in, addr.data(), sizeof(addr_in)); + auto addr_in = GetValue<SockAddrIn>(addr); return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); } @@ -650,7 +650,7 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) { ASSERT(write_buffer.size() >= sizeof(guest_addrin)); write_buffer.resize(sizeof(guest_addrin)); - std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); + PutValue(write_buffer, guest_addrin); return Translate(bsd_errno); } @@ -667,7 +667,7 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) { ASSERT(write_buffer.size() >= sizeof(guest_addrin)); write_buffer.resize(sizeof(guest_addrin)); - std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); + PutValue(write_buffer, guest_addrin); return Translate(bsd_errno); } @@ -725,7 +725,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o optval.size() == sizeof(Errno), { return Errno::INVAL; }, "Incorrect getsockopt option size"); optval.resize(sizeof(Errno)); - memcpy(optval.data(), &translated_pending_err, sizeof(Errno)); + PutValue(optval, translated_pending_err); } return Translate(getsockopt_err); } @@ -735,7 +735,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o } } -Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { +Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) { if (!IsFileDescriptorValid(fd)) { return Errno::BADF; } @@ -748,17 +748,15 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); if (optname == OptName::LINGER) { - ASSERT(optlen == sizeof(Linger)); - Linger linger; - std::memcpy(&linger, optval, sizeof(linger)); + ASSERT(optval.size() == sizeof(Linger)); + auto linger = GetValue<Linger>(optval); ASSERT(linger.onoff == 0 || linger.onoff == 1); return Translate(socket->SetLinger(linger.onoff != 0, linger.linger)); } - ASSERT(optlen == sizeof(u32)); - u32 value; - std::memcpy(&value, optval, sizeof(value)); + ASSERT(optval.size() == sizeof(u32)); + auto value = GetValue<u32>(optval); switch (optname) { case OptName::REUSEADDR: @@ -862,7 +860,7 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess } else { ASSERT(addr.size() == sizeof(SockAddrIn)); const SockAddrIn result = Translate(addr_in); - std::memcpy(addr.data(), &result, sizeof(result)); + PutValue(addr, result); } } @@ -886,8 +884,7 @@ std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> mes Network::SockAddrIn* p_addr_in = nullptr; if (!addr.empty()) { ASSERT(addr.size() == sizeof(SockAddrIn)); - SockAddrIn guest_addr_in; - std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in)); + auto guest_addr_in = GetValue<SockAddrIn>(addr); addr_in = Translate(guest_addr_in); p_addr_in = &addr_in; } diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 161f22b9b..4f69d382c 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -163,7 +163,7 @@ private: Errno ListenImpl(s32 fd, s32 backlog); std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval); - Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); + Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval); Errno ShutdownImpl(s32 fd, s32 how); std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index ed875d444..5d168cbc1 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -116,7 +116,7 @@ json GetProcessorStateDataAuto(Core::System& system) { Core::ARM_Interface::ThreadContext64 context{}; arm.SaveContext(context); - return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32", + return GetProcessorStateData(process->Is64Bit() ? "AArch64" : "AArch32", GetInteger(process->GetEntryPoint()), context.sp, context.pc, context.pstate, context.cpu_registers); } diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index cf51f3481..c9f903213 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -139,7 +139,7 @@ void JoyconDriver::InputThread(std::stop_token stop_token) { input_thread_running = true; // Max update rate is 5ms, ensure we are always able to read a bit faster - constexpr int ThreadDelay = 2; + constexpr int ThreadDelay = 3; std::vector<u8> buffer(MaxBufferSize); while (!stop_token.stop_requested()) { @@ -163,6 +163,17 @@ void JoyconDriver::InputThread(std::stop_token stop_token) { OnNewData(buffer); } + if (!vibration_queue.Empty()) { + VibrationValue vibration_value; + vibration_queue.Pop(vibration_value); + last_vibration_result = rumble_protocol->SendVibration(vibration_value); + } + + // We can't keep up with vibrations. Start skipping. + while (vibration_queue.Size() > 6) { + vibration_queue.Pop(); + } + std::this_thread::yield(); } @@ -402,7 +413,8 @@ Common::Input::DriverResult JoyconDriver::SetVibration(const VibrationValue& vib if (disable_input_thread) { return Common::Input::DriverResult::HandleInUse; } - return rumble_protocol->SendVibration(vibration); + vibration_queue.Push(vibration); + return last_vibration_result; } Common::Input::DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index 335e12cc3..5355780fb 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -9,6 +9,7 @@ #include <span> #include <thread> +#include "common/threadsafe_queue.h" #include "input_common/helpers/joycon_protocol/joycon_types.h" namespace Common::Input { @@ -152,6 +153,10 @@ private: SerialNumber handle_serial_number{}; // Serial number type reported by hidapi SupportedFeatures supported_features{}; + /// Queue of vibration request to controllers + Common::Input::DriverResult last_vibration_result{Common::Input::DriverResult::Success}; + Common::SPSCQueue<VibrationValue> vibration_queue; + // Thread related mutable std::mutex mutex; std::jthread input_thread; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 9b2698fad..081a574e8 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1067,8 +1067,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() { template <class P> void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) { - do { - channel_state->has_deleted_buffers = false; + BufferOperations([&]() { if (is_indexed) { UpdateIndexBuffer(); } @@ -1082,14 +1081,16 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) { if (current_draw_indirect) { UpdateDrawIndirect(); } - } while (channel_state->has_deleted_buffers); + }); } template <class P> void BufferCache<P>::DoUpdateComputeBuffers() { - UpdateComputeUniformBuffers(); - UpdateComputeStorageBuffers(); - UpdateComputeTextureBuffers(); + BufferOperations([&]() { + UpdateComputeUniformBuffers(); + UpdateComputeStorageBuffers(); + UpdateComputeTextureBuffers(); + }); } template <class P> diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp index 65cd5aa06..4f1d5b548 100644 --- a/src/video_core/renderer_null/null_rasterizer.cpp +++ b/src/video_core/renderer_null/null_rasterizer.cpp @@ -3,6 +3,7 @@ #include "common/alignment.h" #include "core/memory.h" +#include "video_core/control/channel_state.h" #include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" #include "video_core/renderer_null/null_rasterizer.h" @@ -99,8 +100,14 @@ bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config, } void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, const VideoCore::DiskResourceLoadCallback& callback) {} -void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {} -void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {} -void RasterizerNull::ReleaseChannel(s32 channel_id) {} +void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) { + CreateChannel(channel); +} +void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) { + BindToChannel(channel.bind_id); +} +void RasterizerNull::ReleaseChannel(s32 channel_id) { + EraseChannel(channel_id); +} } // namespace Null diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 804b95989..22bf8cc77 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -358,7 +358,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device .has_broken_spirv_subgroup_mask_vector_extract_dynamic = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, .has_broken_robust = - device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Maxwell, + device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, }; host_info = Shader::HostTranslateInfo{ diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 465eac37e..c0e8431e4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -13,6 +13,7 @@ #include "common/microprofile.h" #include "common/scope_exit.h" #include "common/settings.h" +#include "video_core/buffer_cache/buffer_cache.h" #include "video_core/control/channel_state.h" #include "video_core/engines/draw_manager.h" #include "video_core/engines/kepler_compute.h" @@ -81,7 +82,7 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in } if (y_negate) { - y += height; + y += conv(static_cast<f32>(regs.surface_clip.height)); height = -height; } @@ -285,6 +286,7 @@ void RasterizerVulkan::DrawTexture() { query_cache.NotifySegment(true); + std::scoped_lock l{texture_cache.mutex}; texture_cache.SynchronizeGraphicsDescriptors(); texture_cache.UpdateRenderTargets(false); @@ -921,9 +923,13 @@ void RasterizerVulkan::UpdateDynamicStates() { } void RasterizerVulkan::HandleTransformFeedback() { + static std::once_flag warn_unsupported; + const auto& regs = maxwell3d->regs; if (!device.IsExtTransformFeedbackSupported()) { - LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); + std::call_once(warn_unsupported, [&] { + LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); + }); return; } query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index d56558a83..daaea2979 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -190,7 +190,7 @@ void SetupDirtySpecialOps(Tables& tables) { void SetupDirtyViewportSwizzles(Tables& tables) { static constexpr size_t swizzle_offset = 6; for (size_t index = 0; index < Regs::NumViewports; ++index) { - tables[0][OFF(viewport_transform) + index * NUM(viewport_transform[0]) + swizzle_offset] = + tables[1][OFF(viewport_transform) + index * NUM(viewport_transform[0]) + swizzle_offset] = ViewportSwizzles; } } diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 81ef98f61..821f44f1a 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -147,6 +147,9 @@ bool Swapchain::AcquireNextImage() { case VK_ERROR_OUT_OF_DATE_KHR: is_outdated = true; break; + case VK_ERROR_SURFACE_LOST_KHR: + vk::Check(result); + break; default: LOG_ERROR(Render_Vulkan, "vkAcquireNextImageKHR returned {}", vk::ToString(result)); break; @@ -180,6 +183,9 @@ void Swapchain::Present(VkSemaphore render_semaphore) { case VK_ERROR_OUT_OF_DATE_KHR: is_outdated = true; break; + case VK_ERROR_SURFACE_LOST_KHR: + vk::Check(result); + break; default: LOG_CRITICAL(Render_Vulkan, "Failed to present with error {}", vk::ToString(result)); break; diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 34208ed74..33e1fb663 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -227,14 +227,14 @@ add_executable(yuzu yuzu.rc ) -if (WIN32 AND YUZU_CRASH_DUMPS) +if (YUZU_CRASH_DUMPS) target_sources(yuzu PRIVATE - mini_dump.cpp - mini_dump.h + breakpad.cpp + breakpad.h ) - target_link_libraries(yuzu PRIVATE ${DBGHELP_LIBRARY}) - target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP) + target_link_libraries(yuzu PRIVATE libbreakpad_client) + target_compile_definitions(yuzu PRIVATE YUZU_CRASH_DUMPS) endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index ca0e14fad..515cb7ce6 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -155,18 +155,27 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( UpdateBorderColor(i); connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) { - if (checked) { - // Hide eventual error message about number of controllers - ui->labelError->setVisible(false); - for (std::size_t index = 0; index <= i; ++index) { - connected_controller_checkboxes[index]->setChecked(checked); - } - } else { - for (std::size_t index = i; index < NUM_PLAYERS; ++index) { - connected_controller_checkboxes[index]->setChecked(checked); - } + // Reconnect current controller if it was the last one checked + // (player number was reduced by more than one) + const bool reconnect_first = !checked && i < player_groupboxes.size() - 1 && + player_groupboxes[i + 1]->isChecked(); + + // Ensures that connecting a controller changes the number of players + if (connected_controller_checkboxes[i]->isChecked() != checked) { + // Ensures that the players are always connected in sequential order + PropagatePlayerNumberChanged(i, checked, reconnect_first); } }); + connect(connected_controller_checkboxes[i], &QCheckBox::clicked, [this, i](bool checked) { + // Reconnect current controller if it was the last one checked + // (player number was reduced by more than one) + const bool reconnect_first = !checked && + i < connected_controller_checkboxes.size() - 1 && + connected_controller_checkboxes[i + 1]->isChecked(); + + // Ensures that the players are always connected in sequential order + PropagatePlayerNumberChanged(i, checked, reconnect_first); + }); connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), [this, i](int) { @@ -668,6 +677,29 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) { } } +void QtControllerSelectorDialog::PropagatePlayerNumberChanged(size_t player_index, bool checked, + bool reconnect_current) { + connected_controller_checkboxes[player_index]->setChecked(checked); + // Hide eventual error message about number of controllers + ui->labelError->setVisible(false); + + if (checked) { + // Check all previous buttons when checked + if (player_index > 0) { + PropagatePlayerNumberChanged(player_index - 1, checked); + } + } else { + // Unchecked all following buttons when unchecked + if (player_index < connected_controller_checkboxes.size() - 1) { + PropagatePlayerNumberChanged(player_index + 1, checked); + } + } + + if (reconnect_current) { + connected_controller_checkboxes[player_index]->setCheckState(Qt::Checked); + } +} + void QtControllerSelectorDialog::DisableUnsupportedPlayers() { const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h index 7f0673d06..e5372495d 100644 --- a/src/yuzu/applets/qt_controller.h +++ b/src/yuzu/applets/qt_controller.h @@ -100,6 +100,10 @@ private: // Updates the console mode. void UpdateDockedState(bool is_handheld); + // Enable preceding controllers or disable following ones + void PropagatePlayerNumberChanged(size_t player_index, bool checked, + bool reconnect_current = false); + // Disables and disconnects unsupported players based on the given parameters. void DisableUnsupportedPlayers(); diff --git a/src/yuzu/breakpad.cpp b/src/yuzu/breakpad.cpp new file mode 100644 index 000000000..0f6a71ab0 --- /dev/null +++ b/src/yuzu/breakpad.cpp @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <ranges> + +#if defined(_WIN32) +#include <client/windows/handler/exception_handler.h> +#elif defined(__linux__) +#include <client/linux/handler/exception_handler.h> +#else +#error Minidump creation not supported on this platform +#endif + +#include "common/fs/fs_paths.h" +#include "common/fs/path_util.h" +#include "yuzu/breakpad.h" + +namespace Breakpad { + +static void PruneDumpDirectory(const std::filesystem::path& dump_path) { + // Code in this function should be exception-safe. + struct Entry { + std::filesystem::path path; + std::filesystem::file_time_type last_write_time; + }; + std::vector<Entry> existing_dumps; + + // Get existing entries. + std::error_code ec; + std::filesystem::directory_iterator dir(dump_path, ec); + for (auto& entry : dir) { + if (entry.is_regular_file()) { + existing_dumps.push_back(Entry{ + .path = entry.path(), + .last_write_time = entry.last_write_time(ec), + }); + } + } + + // Sort descending by creation date. + std::ranges::stable_sort(existing_dumps, [](const auto& a, const auto& b) { + return a.last_write_time > b.last_write_time; + }); + + // Delete older dumps. + for (size_t i = 5; i < existing_dumps.size(); i++) { + std::filesystem::remove(existing_dumps[i].path, ec); + } +} + +#if defined(__linux__) +[[noreturn]] bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, + bool succeeded) { + // Prevent time- and space-consuming core dumps from being generated, as we have + // already generated a minidump and a core file will not be useful anyway. + _exit(1); +} +#endif + +void InstallCrashHandler() { + // Write crash dumps to profile directory. + const auto dump_path = GetYuzuPath(Common::FS::YuzuPath::CrashDumpsDir); + PruneDumpDirectory(dump_path); + +#if defined(_WIN32) + // TODO: If we switch to MinGW builds for Windows, this needs to be wrapped in a C API. + static google_breakpad::ExceptionHandler eh{dump_path, nullptr, nullptr, nullptr, + google_breakpad::ExceptionHandler::HANDLER_ALL}; +#elif defined(__linux__) + static google_breakpad::MinidumpDescriptor descriptor{dump_path}; + static google_breakpad::ExceptionHandler eh{descriptor, nullptr, DumpCallback, + nullptr, true, -1}; +#endif +} + +} // namespace Breakpad diff --git a/src/yuzu/breakpad.h b/src/yuzu/breakpad.h new file mode 100644 index 000000000..0f911aa9c --- /dev/null +++ b/src/yuzu/breakpad.h @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Breakpad { + +void InstallCrashHandler(); + +} diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 74ec4f771..1589ba057 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_camera.h b/src/yuzu/configuration/configure_camera.h index 9a90512b3..3d822da7b 100644 --- a/src/yuzu/configuration/configure_camera.h +++ b/src/yuzu/configuration/configure_camera.h @@ -1,4 +1,4 @@ -// Text : Copyright 2022 yuzu Emulator Project +// Text : Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index b22fda746..ef421c754 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -27,16 +27,6 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent) connect(ui->toggle_gdbstub, &QCheckBox::toggled, [&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); }); - - connect(ui->create_crash_dumps, &QCheckBox::stateChanged, [&](int) { - if (crash_dump_warning_shown) { - return; - } - QMessageBox::warning(this, tr("Restart Required"), - tr("yuzu is required to restart in order to apply this setting."), - QMessageBox::Ok, QMessageBox::Ok); - crash_dump_warning_shown = true; - }); } ConfigureDebug::~ConfigureDebug() = default; @@ -89,13 +79,6 @@ void ConfigureDebug::SetConfiguration() { ui->disable_web_applet->setEnabled(false); ui->disable_web_applet->setText(tr("Web applet not compiled")); #endif - -#ifdef YUZU_DBGHELP - ui->create_crash_dumps->setChecked(Settings::values.create_crash_dumps.GetValue()); -#else - ui->create_crash_dumps->setEnabled(false); - ui->create_crash_dumps->setText(tr("MiniDump creation not compiled")); -#endif } void ConfigureDebug::ApplyConfiguration() { @@ -107,7 +90,6 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked(); Settings::values.reporting_services = ui->reporting_services->isChecked(); Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked(); - Settings::values.create_crash_dumps = ui->create_crash_dumps->isChecked(); Settings::values.quest_flag = ui->quest_flag->isChecked(); Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 66b8b7459..76fe98924 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -471,13 +471,6 @@ </property> </widget> </item> - <item row="4" column="0"> - <widget class="QCheckBox" name="create_crash_dumps"> - <property name="text"> - <string>Create Minidump After Crash</string> - </property> - </widget> - </item> <item row="3" column="0"> <widget class="QCheckBox" name="dump_audio_commands"> <property name="toolTip"> diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 5a48e388b..02e23cce6 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -101,13 +101,13 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, }; - player_connected = { + connected_controller_checkboxes = { ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected, ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected, ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, }; - std::array<QLabel*, 8> player_connected_labels = { + std::array<QLabel*, 8> connected_controller_labels = { ui->label, ui->label_3, ui->label_4, ui->label_5, ui->label_6, ui->label_7, ui->label_8, ui->label_9, }; @@ -115,30 +115,44 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, for (std::size_t i = 0; i < player_tabs.size(); ++i) { player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); player_tabs[i]->layout()->addWidget(player_controllers[i]); - connect(player_connected[i], &QCheckBox::clicked, [this, i](int checked) { - // Ensures that the controllers are always connected in sequential order - this->propagateMouseClickOnPlayers(i, checked, true); + connect(player_controllers[i], &ConfigureInputPlayer::Connected, [this, i](bool checked) { + // Ensures that connecting a controller changes the number of players + if (connected_controller_checkboxes[i]->isChecked() != checked) { + // Ensures that the players are always connected in sequential order + PropagatePlayerNumberChanged(i, checked); + } + }); + connect(connected_controller_checkboxes[i], &QCheckBox::clicked, [this, i](bool checked) { + // Reconnect current controller if it was the last one checked + // (player number was reduced by more than one) + const bool reconnect_first = !checked && + i < connected_controller_checkboxes.size() - 1 && + connected_controller_checkboxes[i + 1]->isChecked(); + + // Ensures that the players are always connected in sequential order + PropagatePlayerNumberChanged(i, checked, reconnect_first); }); connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, &ConfigureInput::UpdateAllInputDevices); connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this, &ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection); - connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) { + connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) { + // Keep activated controllers synced with the "Connected Controllers" checkboxes player_controllers[i]->ConnectPlayer(state == Qt::Checked); }); // Remove/hide all the elements that exceed max_players, if applicable. if (i >= max_players) { ui->tabWidget->removeTab(static_cast<int>(max_players)); - player_connected[i]->hide(); - player_connected_labels[i]->hide(); + connected_controller_checkboxes[i]->hide(); + connected_controller_labels[i]->hide(); } } // Only the first player can choose handheld mode so connect the signal just to player 1 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, [this](bool is_handheld) { UpdateDockedState(is_handheld); }); - advanced = new ConfigureInputAdvanced(this); + advanced = new ConfigureInputAdvanced(hid_core, this); ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); ui->tabAdvanced->layout()->addWidget(advanced); @@ -175,28 +189,25 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, LoadConfiguration(); } -void ConfigureInput::propagateMouseClickOnPlayers(size_t player_index, bool checked, bool origin) { - // Origin has already been toggled - if (!origin) { - player_connected[player_index]->setChecked(checked); - } +void ConfigureInput::PropagatePlayerNumberChanged(size_t player_index, bool checked, + bool reconnect_current) { + connected_controller_checkboxes[player_index]->setChecked(checked); if (checked) { // Check all previous buttons when checked if (player_index > 0) { - propagateMouseClickOnPlayers(player_index - 1, checked, false); + PropagatePlayerNumberChanged(player_index - 1, checked); } } else { // Unchecked all following buttons when unchecked - if (player_index < player_tabs.size() - 1) { - // Reconnect current player if it was the last one checked - // (player number was reduced by more than one) - if (origin && player_connected[player_index + 1]->checkState() == Qt::Checked) { - player_connected[player_index]->setCheckState(Qt::Checked); - } - propagateMouseClickOnPlayers(player_index + 1, checked, false); + if (player_index < connected_controller_checkboxes.size() - 1) { + PropagatePlayerNumberChanged(player_index + 1, checked); } } + + if (reconnect_current) { + connected_controller_checkboxes[player_index]->setCheckState(Qt::Checked); + } } QList<QWidget*> ConfigureInput::GetSubTabs() const { @@ -249,17 +260,17 @@ void ConfigureInput::LoadConfiguration() { } void ConfigureInput::LoadPlayerControllerIndices() { - for (std::size_t i = 0; i < player_connected.size(); ++i) { + for (std::size_t i = 0; i < connected_controller_checkboxes.size(); ++i) { if (i == 0) { auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); if (handheld->IsConnected()) { - player_connected[i]->setChecked(true); + connected_controller_checkboxes[i]->setChecked(true); continue; } } const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(i); - player_connected[i]->setChecked(controller->IsConnected()); + connected_controller_checkboxes[i]->setChecked(controller->IsConnected()); } } diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index abb7f7089..beb503dae 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -56,7 +56,9 @@ private: void UpdateDockedState(bool is_handheld); void UpdateAllInputDevices(); void UpdateAllInputProfiles(std::size_t player_index); - void propagateMouseClickOnPlayers(size_t player_index, bool origin, bool checked); + // Enable preceding controllers or disable following ones + void PropagatePlayerNumberChanged(size_t player_index, bool checked, + bool reconnect_current = false); /// Load configuration settings. void LoadConfiguration(); @@ -71,7 +73,8 @@ private: std::array<ConfigureInputPlayer*, 8> player_controllers; std::array<QWidget*, 8> player_tabs; - std::array<QCheckBox*, 8> player_connected; + // Checkboxes representing the "Connected Controllers". + std::array<QCheckBox*, 8> connected_controller_checkboxes; ConfigureInputAdvanced* advanced; Core::System& system; diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 3cfd5d439..441cea3f6 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -4,11 +4,13 @@ #include <QColorDialog> #include "common/settings.h" #include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" #include "ui_configure_input_advanced.h" #include "yuzu/configuration/configure_input_advanced.h" -ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) - : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) { +ConfigureInputAdvanced::ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()), hid_core{hid_core_} { ui->setupUi(this); controllers_color_buttons = {{ @@ -123,6 +125,8 @@ void ConfigureInputAdvanced::ApplyConfiguration() { player.button_color_left = colors[1]; player.body_color_right = colors[2]; player.button_color_right = colors[3]; + + hid_core.GetEmulatedControllerByIndex(player_idx)->ReloadColorsFromSettings(); } Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked(); diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h index fc1230284..41f822c4a 100644 --- a/src/yuzu/configuration/configure_input_advanced.h +++ b/src/yuzu/configuration/configure_input_advanced.h @@ -14,11 +14,15 @@ namespace Ui { class ConfigureInputAdvanced; } +namespace Core::HID { +class HIDCore; +} // namespace Core::HID + class ConfigureInputAdvanced : public QWidget { Q_OBJECT public: - explicit ConfigureInputAdvanced(QWidget* parent = nullptr); + explicit ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent = nullptr); ~ConfigureInputAdvanced() override; void ApplyConfiguration(); @@ -44,4 +48,6 @@ private: std::array<std::array<QColor, 4>, 8> controllers_colors; std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons; + + Core::HID::HIDCore& hid_core; }; diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index d4df43d73..fda09e925 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -75,7 +75,7 @@ public: void ClearAll(); signals: - /// Emitted when this controller is connected by the user. + /// Emitted when this controller is (dis)connected by the user. void Connected(bool connected); /// Emitted when the Handheld mode is selected (undocked with dual joycons attached). void HandheldStateChanged(bool is_handheld); @@ -183,9 +183,6 @@ private: /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum. std::vector<std::pair<int, Core::HID::NpadStyleIndex>> index_controller_type_pairs; - static constexpr int PLAYER_COUNT = 8; - std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox; - /// This will be the the setting function when an input is awaiting configuration. std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index 1a727f32c..cc2513001 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index a47089988..6d2219bf5 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -306,10 +306,10 @@ void ConfigureProfileManager::SetUserImage() { return; } - // Some games crash when the profile image is too big. Resize any image bigger than 256x256 + // Profile image must be 256x256 QImage image(image_path); - if (image.width() > 256 || image.height() > 256) { - image = image.scaled(256, 256, Qt::KeepAspectRatio); + if (image.width() != 256 || image.height() != 256) { + image = image.scaled(256, 256, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); if (!image.save(image_path)) { QMessageBox::warning(this, tr("Error resizing user image"), tr("Unable to resize image")); diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h index b23c27906..6fd95e2b8 100644 --- a/src/yuzu/configuration/configure_ringcon.h +++ b/src/yuzu/configuration/configure_ringcon.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_tas.h b/src/yuzu/configuration/configure_tas.h index 4a6b0ba4e..a91891906 100644 --- a/src/yuzu/configuration/configure_tas.h +++ b/src/yuzu/configuration/configure_tas.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h index 034dc0d46..b6fdffdc8 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.h +++ b/src/yuzu/configuration/configure_touchscreen_advanced.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 3fe448f27..1434b1a56 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -156,7 +156,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { // Ui General INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); - INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 0783a2430..7049c57b6 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -127,7 +127,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons return list; } - if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64BitProcess()) { + if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64Bit()) { return list; } diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 2bb1a0239..7e7d8e252 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -380,7 +380,6 @@ void GameList::UnloadController() { GameList::~GameList() { UnloadController(); - emit ShouldCancelWorker(); } void GameList::SetFilterFocus() { @@ -397,6 +396,10 @@ void GameList::ClearFilter() { search_field->clear(); } +void GameList::WorkerEvent() { + current_worker->ProcessEvents(this); +} + void GameList::AddDirEntry(GameListDir* entry_items) { item_model->invisibleRootItem()->appendRow(entry_items); tree_view->setExpanded( @@ -826,28 +829,21 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) { tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size); tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time); - // Before deleting rows, cancel the worker so that it is not using them - emit ShouldCancelWorker(); + // Cancel any existing worker. + current_worker.reset(); // Delete any rows that might already exist if we're repopulating item_model->removeRows(0, item_model->rowCount()); search_field->clear(); - GameListWorker* worker = - new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system); + current_worker = std::make_unique<GameListWorker>(vfs, provider, game_dirs, compatibility_list, + play_time_manager, system); - connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); - connect(worker, &GameListWorker::DirEntryReady, this, &GameList::AddDirEntry, - Qt::QueuedConnection); - connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, + // Get events from the worker as data becomes available + connect(current_worker.get(), &GameListWorker::DataAvailable, this, &GameList::WorkerEvent, Qt::QueuedConnection); - // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to - // cancel without delay. - connect(this, &GameList::ShouldCancelWorker, worker, &GameListWorker::Cancel, - Qt::DirectConnection); - QThreadPool::globalInstance()->start(worker); - current_worker = std::move(worker); + QThreadPool::globalInstance()->start(current_worker.get()); } void GameList::SaveInterfaceLayout() { diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 712570cea..563a3a35b 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -109,7 +109,6 @@ signals: void BootGame(const QString& game_path, u64 program_id, std::size_t program_index, StartGameType type, AmLaunchType launch_type); void GameChosen(const QString& game_path, const u64 title_id = 0); - void ShouldCancelWorker(); void OpenFolderRequested(u64 program_id, GameListOpenTarget target, const std::string& game_path); void OpenTransferableShaderCacheRequested(u64 program_id); @@ -138,11 +137,16 @@ private slots: void OnUpdateThemedIcons(); private: + friend class GameListWorker; + void WorkerEvent(); + void AddDirEntry(GameListDir* entry_items); void AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent); - void ValidateEntry(const QModelIndex& item); void DonePopulating(const QStringList& watch_list); +private: + void ValidateEntry(const QModelIndex& item); + void RefreshGameDirectory(); void ToggleFavorite(u64 program_id); @@ -165,7 +169,7 @@ private: QVBoxLayout* layout = nullptr; QTreeView* tree_view = nullptr; QStandardItemModel* item_model = nullptr; - GameListWorker* current_worker = nullptr; + std::unique_ptr<GameListWorker> current_worker; QFileSystemWatcher* watcher = nullptr; ControllerNavigation* controller_navigation = nullptr; CompatibilityList compatibility_list; diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 077ced12b..69be21027 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -233,10 +233,53 @@ GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs_, const PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_) : vfs{std::move(vfs_)}, provider{provider_}, game_dirs{game_dirs_}, - compatibility_list{compatibility_list_}, - play_time_manager{play_time_manager_}, system{system_} {} + compatibility_list{compatibility_list_}, play_time_manager{play_time_manager_}, system{ + system_} { + // We want the game list to manage our lifetime. + setAutoDelete(false); +} + +GameListWorker::~GameListWorker() { + this->disconnect(); + stop_requested.store(true); + processing_completed.Wait(); +} + +void GameListWorker::ProcessEvents(GameList* game_list) { + while (true) { + std::function<void(GameList*)> func; + { + // Lock queue to protect concurrent modification. + std::scoped_lock lk(lock); + + // If we can't pop a function, return. + if (queued_events.empty()) { + return; + } + + // Pop a function. + func = std::move(queued_events.back()); + queued_events.pop_back(); + } + + // Run the function. + func(game_list); + } +} + +template <typename F> +void GameListWorker::RecordEvent(F&& func) { + { + // Lock queue to protect concurrent modification. + std::scoped_lock lk(lock); -GameListWorker::~GameListWorker() = default; + // Add the function into the front of the queue. + queued_events.emplace_front(std::move(func)); + } + + // Data now available. + emit DataAvailable(); +} void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { using namespace FileSys; @@ -284,9 +327,9 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { GetMetadataFromControlNCA(patch, *control, icon, name); } - emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader, - program_id, compatibility_list, play_time_manager, patch), - parent_dir); + auto entry = MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader, + program_id, compatibility_list, play_time_manager, patch); + RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); } } @@ -360,11 +403,12 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa const FileSys::PatchManager patch{id, system.GetFileSystemController(), system.GetContentProvider()}; - emit EntryReady(MakeGameListEntry(physical_name, name, - Common::FS::GetSize(physical_name), icon, - *loader, id, compatibility_list, - play_time_manager, patch), - parent_dir); + auto entry = MakeGameListEntry( + physical_name, name, Common::FS::GetSize(physical_name), icon, *loader, + id, compatibility_list, play_time_manager, patch); + + RecordEvent( + [=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); } } else { std::vector<u8> icon; @@ -376,11 +420,12 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), system.GetContentProvider()}; - emit EntryReady(MakeGameListEntry(physical_name, name, - Common::FS::GetSize(physical_name), icon, - *loader, program_id, compatibility_list, - play_time_manager, patch), - parent_dir); + auto entry = MakeGameListEntry( + physical_name, name, Common::FS::GetSize(physical_name), icon, *loader, + program_id, compatibility_list, play_time_manager, patch); + + RecordEvent( + [=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); } } } else if (is_dir) { @@ -399,25 +444,34 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa } void GameListWorker::run() { + watch_list.clear(); provider->ClearAllEntries(); + const auto DirEntryReady = [&](GameListDir* game_list_dir) { + RecordEvent([=](GameList* game_list) { game_list->AddDirEntry(game_list_dir); }); + }; + for (UISettings::GameDir& game_dir : game_dirs) { + if (stop_requested) { + break; + } + if (game_dir.path == QStringLiteral("SDMC")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); - emit DirEntryReady(game_list_dir); + DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else if (game_dir.path == QStringLiteral("UserNAND")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); - emit DirEntryReady(game_list_dir); + DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else if (game_dir.path == QStringLiteral("SysNAND")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); - emit DirEntryReady(game_list_dir); + DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else { watch_list.append(game_dir.path); auto* const game_list_dir = new GameListDir(game_dir); - emit DirEntryReady(game_list_dir); + DirEntryReady(game_list_dir); ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), game_dir.deep_scan, game_list_dir); ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), @@ -425,12 +479,6 @@ void GameListWorker::run() { } } - emit Finished(watch_list); + RecordEvent([=](GameList* game_list) { game_list->DonePopulating(watch_list); }); processing_completed.Set(); } - -void GameListWorker::Cancel() { - this->disconnect(); - stop_requested.store(true); - processing_completed.Wait(); -} diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 54dc05e30..d5990fcde 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h @@ -4,6 +4,7 @@ #pragma once #include <atomic> +#include <deque> #include <memory> #include <string> @@ -20,6 +21,7 @@ namespace Core { class System; } +class GameList; class QStandardItem; namespace FileSys { @@ -46,24 +48,22 @@ public: /// Starts the processing of directory tree information. void run() override; - /// Tells the worker that it should no longer continue processing. Thread-safe. - void Cancel(); - -signals: +public: /** - * The `EntryReady` signal is emitted once an entry has been prepared and is ready - * to be added to the game list. - * @param entry_items a list with `QStandardItem`s that make up the columns of the new - * entry. + * Synchronously processes any events queued by the worker. + * + * AddDirEntry is called on the game list for every discovered directory. + * AddEntry is called on the game list for every discovered program. + * DonePopulating is called on the game list when processing completes. */ - void DirEntryReady(GameListDir* entry_items); - void EntryReady(QList<QStandardItem*> entry_items, GameListDir* parent_dir); + void ProcessEvents(GameList* game_list); - /** - * After the worker has traversed the game directory looking for entries, this signal is - * emitted with a list of folders that should be watched for changes as well. - */ - void Finished(QStringList watch_list); +signals: + void DataAvailable(); + +private: + template <typename F> + void RecordEvent(F&& func); private: void AddTitlesToGameList(GameListDir* parent_dir); @@ -84,8 +84,11 @@ private: QStringList watch_list; - Common::Event processing_completed; + std::mutex lock; + std::condition_variable cv; + std::deque<std::function<void(GameList*)>> queued_events; std::atomic_bool stop_requested = false; + Common::Event processing_completed; Core::System& system; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 1431cf2fe..ce0c71021 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -159,8 +159,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/util/clickable_label.h" #include "yuzu/vk_device_info.h" -#ifdef YUZU_DBGHELP -#include "yuzu/mini_dump.h" +#ifdef YUZU_CRASH_DUMPS +#include "yuzu/breakpad.h" #endif using namespace Common::Literals; @@ -1072,7 +1072,7 @@ void GMainWindow::InitializeWidgets() { }); volume_popup->layout()->addWidget(volume_slider); - volume_button = new QPushButton(); + volume_button = new VolumeButton(); volume_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); volume_button->setFocusPolicy(Qt::NoFocus); volume_button->setCheckable(true); @@ -1103,6 +1103,8 @@ void GMainWindow::InitializeWidgets() { context_menu.exec(volume_button->mapToGlobal(menu_location)); volume_button->repaint(); }); + connect(volume_button, &VolumeButton::VolumeChanged, this, &GMainWindow::UpdateVolumeUI); + statusBar()->insertPermanentWidget(0, volume_button); // setup AA button @@ -1906,7 +1908,10 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, StartGameType type, AmLaunchType launch_type) { LOG_INFO(Frontend, "yuzu starting..."); - StoreRecentFile(filename); // Put the filename on top of the list + + if (program_id > static_cast<u64>(Service::AM::Applets::AppletProgramId::MaxProgramId)) { + StoreRecentFile(filename); // Put the filename on top of the list + } // Save configurations UpdateUISettings(); @@ -2019,7 +2024,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())} .filename()); } - const bool is_64bit = system->Kernel().ApplicationProcess()->Is64BitProcess(); + const bool is_64bit = system->Kernel().ApplicationProcess()->Is64Bit(); const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)"); title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit") .arg(QString::fromStdString(title_name), instruction_set_suffix) @@ -2172,6 +2177,7 @@ void GMainWindow::ShutdownGame() { return; } + play_time_manager->Stop(); OnShutdownBegin(); OnEmulationStopTimeExpired(); OnEmulationStopped(); @@ -2735,7 +2741,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); + const auto extracted = FileSys::ExtractRomFS(romfs); if (extracted == nullptr) { failed(); return; @@ -2906,7 +2912,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga const std::string game_file_name = std::filesystem::path(game_path).filename().string(); // Determine full paths for icon and shortcut -#if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) const char* home = std::getenv("HOME"); const std::filesystem::path home_path = (home == nullptr ? "~" : home); const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); @@ -2963,7 +2969,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga QImage icon_data = QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); -#if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) // Convert and write the icon as a PNG if (!icon_data.save(QString::fromStdString(icon_path.string()))) { LOG_ERROR(Frontend, "Could not write icon as PNG to file"); @@ -3482,7 +3488,7 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) { } void GMainWindow::OnExit() { - OnStopGame(); + ShutdownGame(); } void GMainWindow::OnSaveConfig() { @@ -4002,7 +4008,7 @@ bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::st const std::string& comment, const std::string& icon_path, const std::string& command, const std::string& arguments, const std::string& categories, const std::string& keywords) { -#if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) // This desktop file template was writing referencing // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html std::string shortcut_contents{}; @@ -4270,7 +4276,7 @@ void GMainWindow::OnToggleStatusBar() { } void GMainWindow::OnAlbum() { - constexpr u64 AlbumId = 0x010000000000100Dull; + constexpr u64 AlbumId = static_cast<u64>(Service::AM::Applets::AppletProgramId::PhotoViewer); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), @@ -4293,7 +4299,7 @@ void GMainWindow::OnAlbum() { } void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { - constexpr u64 CabinetId = 0x0100000000001002ull; + constexpr u64 CabinetId = static_cast<u64>(Service::AM::Applets::AppletProgramId::Cabinet); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), @@ -4317,7 +4323,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { } void GMainWindow::OnMiiEdit() { - constexpr u64 MiiEditId = 0x0100000000001009ull; + constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), @@ -4845,7 +4851,12 @@ bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installe } bool GMainWindow::ConfirmClose() { - if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { + if (emu_thread == nullptr || + UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) { + return true; + } + if (!system->GetExitLocked() && + UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) { return true; } const auto text = tr("Are you sure you want to close yuzu?"); @@ -4950,7 +4961,7 @@ bool GMainWindow::ConfirmChangeGame() { } bool GMainWindow::ConfirmForceLockedExit() { - if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { + if (emu_thread == nullptr) { return true; } const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" @@ -5126,6 +5137,32 @@ void GMainWindow::changeEvent(QEvent* event) { QWidget::changeEvent(event); } +void VolumeButton::wheelEvent(QWheelEvent* event) { + + int num_degrees = event->angleDelta().y() / 8; + int num_steps = (num_degrees / 15) * scroll_multiplier; + // Stated in QT docs: Most mouse types work in steps of 15 degrees, in which case the delta + // value is a multiple of 120; i.e., 120 units * 1/8 = 15 degrees. + + if (num_steps > 0) { + Settings::values.volume.SetValue( + std::min(200, Settings::values.volume.GetValue() + num_steps)); + } else { + Settings::values.volume.SetValue( + std::max(0, Settings::values.volume.GetValue() + num_steps)); + } + + scroll_multiplier = std::min(MaxMultiplier, scroll_multiplier * 2); + scroll_timer.start(100); // reset the multiplier if no scroll event occurs within 100 ms + + emit VolumeChanged(); + event->accept(); +} + +void VolumeButton::ResetMultiplier() { + scroll_multiplier = 1; +} + #ifdef main #undef main #endif @@ -5187,22 +5224,15 @@ int main(int argc, char* argv[]) { return 0; } -#ifdef YUZU_DBGHELP - PROCESS_INFORMATION pi; - if (!is_child && Settings::values.create_crash_dumps.GetValue() && - MiniDump::SpawnDebuggee(argv[0], pi)) { - // Delete the config object so that it doesn't save when the program exits - config.reset(nullptr); - MiniDump::DebugDebuggee(pi); - return 0; - } -#endif - if (StartupChecks(argv[0], &has_broken_vulkan, Settings::values.perform_vulkan_check.GetValue())) { return 0; } +#ifdef YUZU_CRASH_DUMPS + Breakpad::InstallCrashHandler(); +#endif + Common::DetachedTasks detached_tasks; MicroProfileOnThreadCreate("Frontend"); SCOPE_EXIT({ MicroProfileShutdown(); }); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 270a40c5f..f9c6efe4f 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -8,6 +8,7 @@ #include <QMainWindow> #include <QMessageBox> +#include <QPushButton> #include <QTimer> #include <QTranslator> @@ -137,6 +138,28 @@ namespace VkDeviceInfo { class Record; } +class VolumeButton : public QPushButton { + Q_OBJECT +public: + explicit VolumeButton(QWidget* parent = nullptr) : QPushButton(parent), scroll_multiplier(1) { + connect(&scroll_timer, &QTimer::timeout, this, &VolumeButton::ResetMultiplier); + } + +signals: + void VolumeChanged(); + +protected: + void wheelEvent(QWheelEvent* event) override; + +private slots: + void ResetMultiplier(); + +private: + int scroll_multiplier; + QTimer scroll_timer; + constexpr static int MaxMultiplier = 8; +}; + class GMainWindow : public QMainWindow { Q_OBJECT @@ -481,7 +504,7 @@ private: QPushButton* dock_status_button = nullptr; QPushButton* filter_status_button = nullptr; QPushButton* aa_status_button = nullptr; - QPushButton* volume_button = nullptr; + VolumeButton* volume_button = nullptr; QWidget* volume_popup = nullptr; QSlider* volume_slider = nullptr; QTimer status_bar_update_timer; diff --git a/src/yuzu/mini_dump.cpp b/src/yuzu/mini_dump.cpp deleted file mode 100644 index a34dc6a9c..000000000 --- a/src/yuzu/mini_dump.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// SPDX-FileCopyrightText: 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <cstdio> -#include <cstring> -#include <ctime> -#include <filesystem> -#include <fmt/format.h> -#include <windows.h> -#include "yuzu/mini_dump.h" -#include "yuzu/startup_checks.h" - -// dbghelp.h must be included after windows.h -#include <dbghelp.h> - -namespace MiniDump { - -void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, - EXCEPTION_POINTERS* pep) { - char file_name[255]; - const std::time_t the_time = std::time(nullptr); - std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time)); - - // Open the file - HANDLE file_handle = CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - - if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) { - fmt::print(stderr, "CreateFileA failed. Error: {}", GetLastError()); - return; - } - - // Create the minidump - const MINIDUMP_TYPE dump_type = MiniDumpNormal; - - const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle, - dump_type, (pep != 0) ? info : 0, 0, 0); - - if (write_dump_status) { - fmt::print(stderr, "MiniDump created: {}", file_name); - } else { - fmt::print(stderr, "MiniDumpWriteDump failed. Error: {}", GetLastError()); - } - - // Close the file - CloseHandle(file_handle); -} - -void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { - EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; - - HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId); - if (thread_handle == nullptr) { - fmt::print(stderr, "OpenThread failed ({})", GetLastError()); - return; - } - - // Get child process context - CONTEXT context = {}; - context.ContextFlags = CONTEXT_ALL; - if (!GetThreadContext(thread_handle, &context)) { - fmt::print(stderr, "GetThreadContext failed ({})", GetLastError()); - return; - } - - // Create exception pointers for minidump - EXCEPTION_POINTERS ep; - ep.ExceptionRecord = &record; - ep.ContextRecord = &context; - - MINIDUMP_EXCEPTION_INFORMATION info; - info.ThreadId = deb_ev.dwThreadId; - info.ExceptionPointers = &ep; - info.ClientPointers = false; - - CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep); - - if (CloseHandle(thread_handle) == 0) { - fmt::print(stderr, "error: CloseHandle(thread_handle) failed ({})", GetLastError()); - } -} - -bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { - std::memset(&pi, 0, sizeof(pi)); - - // Don't debug if we are already being debugged - if (IsDebuggerPresent()) { - return false; - } - - if (!SpawnChild(arg0, &pi, 0)) { - fmt::print(stderr, "warning: continuing without crash dumps"); - return false; - } - - const bool can_debug = DebugActiveProcess(pi.dwProcessId); - if (!can_debug) { - fmt::print(stderr, - "warning: DebugActiveProcess failed ({}), continuing without crash dumps", - GetLastError()); - return false; - } - - return true; -} - -static const char* ExceptionName(DWORD exception) { - switch (exception) { - case EXCEPTION_ACCESS_VIOLATION: - return "EXCEPTION_ACCESS_VIOLATION"; - case EXCEPTION_DATATYPE_MISALIGNMENT: - return "EXCEPTION_DATATYPE_MISALIGNMENT"; - case EXCEPTION_BREAKPOINT: - return "EXCEPTION_BREAKPOINT"; - case EXCEPTION_SINGLE_STEP: - return "EXCEPTION_SINGLE_STEP"; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; - case EXCEPTION_FLT_DENORMAL_OPERAND: - return "EXCEPTION_FLT_DENORMAL_OPERAND"; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; - case EXCEPTION_FLT_INEXACT_RESULT: - return "EXCEPTION_FLT_INEXACT_RESULT"; - case EXCEPTION_FLT_INVALID_OPERATION: - return "EXCEPTION_FLT_INVALID_OPERATION"; - case EXCEPTION_FLT_OVERFLOW: - return "EXCEPTION_FLT_OVERFLOW"; - case EXCEPTION_FLT_STACK_CHECK: - return "EXCEPTION_FLT_STACK_CHECK"; - case EXCEPTION_FLT_UNDERFLOW: - return "EXCEPTION_FLT_UNDERFLOW"; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - return "EXCEPTION_INT_DIVIDE_BY_ZERO"; - case EXCEPTION_INT_OVERFLOW: - return "EXCEPTION_INT_OVERFLOW"; - case EXCEPTION_PRIV_INSTRUCTION: - return "EXCEPTION_PRIV_INSTRUCTION"; - case EXCEPTION_IN_PAGE_ERROR: - return "EXCEPTION_IN_PAGE_ERROR"; - case EXCEPTION_ILLEGAL_INSTRUCTION: - return "EXCEPTION_ILLEGAL_INSTRUCTION"; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; - case EXCEPTION_STACK_OVERFLOW: - return "EXCEPTION_STACK_OVERFLOW"; - case EXCEPTION_INVALID_DISPOSITION: - return "EXCEPTION_INVALID_DISPOSITION"; - case EXCEPTION_GUARD_PAGE: - return "EXCEPTION_GUARD_PAGE"; - case EXCEPTION_INVALID_HANDLE: - return "EXCEPTION_INVALID_HANDLE"; - default: - return "unknown exception type"; - } -} - -void DebugDebuggee(PROCESS_INFORMATION& pi) { - DEBUG_EVENT deb_ev = {}; - - while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) { - const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE); - if (!wait_success) { - fmt::print(stderr, "error: WaitForDebugEvent failed ({})", GetLastError()); - return; - } - - switch (deb_ev.dwDebugEventCode) { - case OUTPUT_DEBUG_STRING_EVENT: - case CREATE_PROCESS_DEBUG_EVENT: - case CREATE_THREAD_DEBUG_EVENT: - case EXIT_PROCESS_DEBUG_EVENT: - case EXIT_THREAD_DEBUG_EVENT: - case LOAD_DLL_DEBUG_EVENT: - case RIP_EVENT: - case UNLOAD_DLL_DEBUG_EVENT: - // Continue on all other debug events - ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); - break; - case EXCEPTION_DEBUG_EVENT: - EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; - - // We want to generate a crash dump if we are seeing the same exception again. - if (!deb_ev.u.Exception.dwFirstChance) { - fmt::print(stderr, "Creating MiniDump on ExceptionCode: 0x{:08x} {}\n", - record.ExceptionCode, ExceptionName(record.ExceptionCode)); - DumpFromDebugEvent(deb_ev, pi); - } - - // Continue without handling the exception. - // Lets the debuggee use its own exception handler. - // - If one does not exist, we will see the exception once more where we make a minidump - // for. Then when it reaches here again, yuzu will probably crash. - // - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an - // infinite loop of exceptions. - ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); - break; - } - } -} - -} // namespace MiniDump diff --git a/src/yuzu/mini_dump.h b/src/yuzu/mini_dump.h deleted file mode 100644 index d6b6cca84..000000000 --- a/src/yuzu/mini_dump.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <windows.h> - -#include <dbghelp.h> - -namespace MiniDump { - -void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, - EXCEPTION_POINTERS* pep); - -void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi); -bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi); -void DebugDebuggee(PROCESS_INFORMATION& pi); - -} // namespace MiniDump diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index b62ff620c..77d992c54 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -93,10 +93,6 @@ struct Values { Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui}; Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui}; - Setting<bool> confirm_before_closing{ - linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default, - true, true}; - SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage, ConfirmStop::Ask_Always, "confirmStop", |