From ae02310bdff82262a1c62bd1ddc457053c1b1a5d Mon Sep 17 00:00:00 2001 From: crueter Date: Sat, 12 Jul 2025 18:05:37 -0400 Subject: [PATCH 01/41] optional multimedia/webengine Signed-off-by: crueter --- .ci/linux/build.sh | 16 ++++++++++++++-- .ci/windows/build.sh | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/.ci/linux/build.sh b/.ci/linux/build.sh index 745f8244b7..aa15333ac2 100755 --- a/.ci/linux/build.sh +++ b/.ci/linux/build.sh @@ -60,6 +60,18 @@ if [ "$DEVEL" != "true" ]; then export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" -DENABLE_QT_UPDATE_CHECKER=ON) fi +if [ "$USE_WEBENGINE" = "true" ]; then + WEBENGINE=ON +else + WEBENGINE=OFF +fi + +if [ "$USE_MULTIMEDIA" = "false" ]; then + MULTIMEDIA=OFF +else + MULTIMEDIA=ON +fi + export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@) mkdir -p build && cd build @@ -74,8 +86,8 @@ cmake .. -G Ninja \ -DYUZU_USE_BUNDLED_SDL2=OFF \ -DYUZU_USE_EXTERNAL_SDL2=ON \ -DYUZU_TESTS=OFF \ - -DYUZU_USE_QT_MULTIMEDIA=ON \ - -DYUZU_USE_QT_WEB_ENGINE=ON \ + -DYUZU_USE_QT_MULTIMEDIA=$MULTIMEDIA \ + -DYUZU_USE_QT_WEB_ENGINE=$WEBENGINE \ -DYUZU_USE_FASTER_LD=ON \ -DYUZU_ENABLE_LTO=ON \ "${EXTRA_CMAKE_FLAGS[@]}" diff --git a/.ci/windows/build.sh b/.ci/windows/build.sh index 4e993b0488..667fd316fa 100644 --- a/.ci/windows/build.sh +++ b/.ci/windows/build.sh @@ -22,6 +22,18 @@ if [ "$WINDEPLOYQT" == "" ]; then exit 1 fi +if [ "$USE_WEBENGINE" = "true" ]; then + WEBENGINE=ON +else + WEBENGINE=OFF +fi + +if [ "$USE_MULTIMEDIA" = "false" ]; then + MULTIMEDIA=OFF +else + MULTIMEDIA=ON +fi + export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@) mkdir -p build && cd build @@ -34,8 +46,8 @@ cmake .. -G Ninja \ -DYUZU_TESTS=OFF \ -DYUZU_CMD=OFF \ -DYUZU_ROOM_STANDALONE=OFF \ - -DYUZU_USE_QT_MULTIMEDIA=ON \ - -DYUZU_USE_QT_WEB_ENGINE=ON \ + -DYUZU_USE_QT_MULTIMEDIA=$MULTIMEDIA \ + -DYUZU_USE_QT_WEB_ENGINE=$WEBENGINE \ -DYUZU_ENABLE_LTO=ON \ "${EXTRA_CMAKE_FLAGS[@]}" From d5c58342e083a275ce4acbfd2b2453c53ab12c3f Mon Sep 17 00:00:00 2001 From: lizzie Date: Sun, 13 Jul 2025 03:25:43 +0200 Subject: [PATCH 02/41] [yuzu_cmd] gdbstub option (#43) Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/43 Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu_cmd/yuzu.cpp | 55 ++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 005f5bf1db..c1d714be77 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -80,6 +83,7 @@ static void PrintHelp(const char* argv0) { " Nickname, password, address and port for multiplayer\n" "-p, --program Pass following string as arguments to executable\n" "-u, --user Select a specific user profile from 0 to 7\n" + "-d, --debug Run the GDB stub on a port from 1 to 65535\n" "-v, --version Output version information and exit\n"; } @@ -162,24 +166,22 @@ static void OnMessageReceived(const Network::ChatEntry& msg) { } static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { - std::string message; - switch (msg.type) { - case Network::IdMemberJoin: - message = fmt::format("{} has joined", msg.nickname); - break; - case Network::IdMemberLeave: - message = fmt::format("{} has left", msg.nickname); - break; - case Network::IdMemberKicked: - message = fmt::format("{} has been kicked", msg.nickname); - break; - case Network::IdMemberBanned: - message = fmt::format("{} has been banned", msg.nickname); - break; - case Network::IdAddressUnbanned: - message = fmt::format("{} has been unbanned", msg.nickname); - break; - } + std::string message = [&]() { + switch (msg.type) { + case Network::IdMemberJoin: + return fmt::format("{} has joined", msg.nickname); + case Network::IdMemberLeave: + return fmt::format("{} has left", msg.nickname); + case Network::IdMemberKicked: + return fmt::format("{} has been kicked", msg.nickname); + case Network::IdMemberBanned: + return fmt::format("{} has been banned", msg.nickname); + case Network::IdAddressUnbanned: + return fmt::format("{} has been unbanned", msg.nickname); + default: + return std::string{}; + } + }(); if (!message.empty()) std::cout << std::endl << "* " << message << std::endl << std::endl; } @@ -209,10 +211,10 @@ int main(int argc, char** argv) { } #endif std::string filepath; - std::optional config_path; + std::optional config_path{}; std::string program_args; - std::optional selected_user; - + std::optional selected_user{}; + std::optional override_gdb_port{}; bool use_multiplayer = false; bool fullscreen = false; std::string nickname{}; @@ -222,6 +224,7 @@ int main(int argc, char** argv) { static struct option long_options[] = { // clang-format off + {"debug", no_argument, 0, 'd'}, {"config", required_argument, 0, 'c'}, {"fullscreen", no_argument, 0, 'f'}, {"help", no_argument, 0, 'h'}, @@ -235,9 +238,12 @@ int main(int argc, char** argv) { }; while (optind < argc) { - int arg = getopt_long(argc, argv, "g:fhvp::c:u:", long_options, &option_index); + int arg = getopt_long(argc, argv, "g:fhvp::c:u:d:", long_options, &option_index); if (arg != -1) { switch (static_cast(arg)) { + case 'd': + override_gdb_port = static_cast(atoi(optarg)); + break; case 'c': config_path = optarg; break; @@ -323,6 +329,11 @@ int main(int argc, char** argv) { Settings::values.current_user = std::clamp(*selected_user, 0, 7); } + if (override_gdb_port.has_value()) { + Settings::values.use_gdbstub = true; + Settings::values.gdbstub_port = *override_gdb_port; + } + #ifdef _WIN32 LocalFree(argv_w); #endif From 55a77973782d1e74c5beec65b3dc5b61ddb37ef4 Mon Sep 17 00:00:00 2001 From: crueter Date: Sun, 13 Jul 2025 03:39:01 +0200 Subject: [PATCH 03/41] [desktop] add options to open root, NAND, SDMC, load, and log dirs (#53) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/53 --- src/yuzu/configuration/configure_debug.ui | 14 ++++---- src/yuzu/main.cpp | 35 +++++++++++++++++-- src/yuzu/main.h | 6 +++- src/yuzu/main.ui | 42 +++++++++++++++++++---- 4 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 155dac412b..5408d485b4 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -157,13 +157,6 @@ Logging - - - - Open Log Location - - - @@ -224,6 +217,13 @@ + + + + Open Log Location + + + diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 13d2373f5d..4ff59291e5 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1674,7 +1674,12 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Configure_Tas, &GMainWindow::OnConfigureTas); // Help - connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); + connect_menu(ui->action_Root_Data_Folder, &GMainWindow::OnOpenRootDataFolder); + connect_menu(ui->action_NAND_Folder, &GMainWindow::OnOpenNANDFolder); + connect_menu(ui->action_SDMC_Folder, &GMainWindow::OnOpenSDMCFolder); + connect_menu(ui->action_Mod_Folder, &GMainWindow::OnOpenModFolder); + connect_menu(ui->action_Log_Folder, &GMainWindow::OnOpenLogFolder); + connect_menu(ui->action_Discord, &GMainWindow::OnOpenDiscord); connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware); @@ -4159,11 +4164,35 @@ void GMainWindow::LoadAmiibo(const QString& filename) { } } -void GMainWindow::OnOpenYuzuFolder() { - QDesktopServices::openUrl(QUrl::fromLocalFile( +void GMainWindow::OnOpenRootDataFolder() { + QDesktopServices::openUrl(QUrl( QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::EdenDir)))); } +void GMainWindow::OnOpenNANDFolder() +{ + QDesktopServices::openUrl(QUrl::fromLocalFile( + QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir)))); +} + +void GMainWindow::OnOpenSDMCFolder() +{ + QDesktopServices::openUrl(QUrl::fromLocalFile( + QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir)))); +} + +void GMainWindow::OnOpenModFolder() +{ + QDesktopServices::openUrl(QUrl::fromLocalFile( + QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LoadDir)))); +} + +void GMainWindow::OnOpenLogFolder() +{ + QDesktopServices::openUrl(QUrl::fromLocalFile( + QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LogDir)))); +} + void GMainWindow::OnVerifyInstalledContents() { // Initialize a progress dialog. QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 84588f641a..1f2582098a 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -387,7 +387,11 @@ private slots: void OnToggleAdaptingFilter(); void OnConfigurePerGame(); void OnLoadAmiibo(); - void OnOpenYuzuFolder(); + void OnOpenRootDataFolder(); + void OnOpenNANDFolder(); + void OnOpenSDMCFolder(); + void OnOpenModFolder(); + void OnOpenLogFolder(); void OnVerifyInstalledContents(); void OnInstallFirmware(); void OnInstallDecryptionKeys(); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 6b19d1f8f5..e35b7afa5a 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -57,6 +57,16 @@ &Recent Files + + + Open &Eden Folders + + + + + + + @@ -66,7 +76,7 @@ - + @@ -388,11 +398,6 @@ &FAQ - - - Open &eden Folder - - false @@ -517,6 +522,31 @@ &Application Menu + + + &Root Data Folder + + + + + &NAND Folder + + + + + &SDMC Folder + + + + + &Mod Folder + + + + + &Log Folder + + From 03351a4f8b463a798d64dcb78899b4cdd9004071 Mon Sep 17 00:00:00 2001 From: crueter Date: Sun, 13 Jul 2025 03:39:19 +0200 Subject: [PATCH 04/41] [frontend] refactor: extract common firmware & key functions (#38) Extracts some firmware version/verification functions into `frontend_common` to reduce duplicate code, especially for the new QML frontend. Additionally adds a check for games that are known to require firmware (e.g. MK8DX) and warns the user if they don't have firmware installed and attempt to run the game. Firmware installation is to be in a separate PR. Signed-off-by: crueter Co-authored-by: Aleksandr Popovich Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/38 --- .../java/org/yuzu/yuzu_emu/NativeLibrary.kt | 48 +- .../org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 72 +- .../fragments/DriverFetcherFragment.kt | 2 - .../fragments/HomeSettingsFragment.kt | 2 +- .../yuzu/yuzu_emu/fragments/SetupFragment.kt | 2 +- .../org/yuzu/yuzu_emu/ui/GamesFragment.kt | 7 +- .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 303 ++--- src/android/app/src/main/jni/native.cpp | 43 +- .../app/src/main/res/values-ar/strings.xml | 8 - .../app/src/main/res/values-ckb/strings.xml | 8 - .../app/src/main/res/values-cs/strings.xml | 6 - .../app/src/main/res/values-de/strings.xml | 8 - .../app/src/main/res/values-es/strings.xml | 8 - .../app/src/main/res/values-fa/strings.xml | 8 - .../app/src/main/res/values-fr/strings.xml | 8 - .../app/src/main/res/values-he/strings.xml | 8 - .../app/src/main/res/values-hu/strings.xml | 8 - .../app/src/main/res/values-id/strings.xml | 8 - .../app/src/main/res/values-it/strings.xml | 8 - .../app/src/main/res/values-ja/strings.xml | 8 - .../app/src/main/res/values-ko/strings.xml | 8 - .../app/src/main/res/values-nb/strings.xml | 8 - .../app/src/main/res/values-pl/strings.xml | 8 - .../src/main/res/values-pt-rBR/strings.xml | 8 - .../src/main/res/values-pt-rPT/strings.xml | 8 - .../app/src/main/res/values-ru/strings.xml | 8 - .../app/src/main/res/values-sr/strings.xml | 8 - .../app/src/main/res/values-uk/strings.xml | 8 - .../app/src/main/res/values-vi/strings.xml | 8 - .../src/main/res/values-zh-rCN/strings.xml | 8 - .../src/main/res/values-zh-rTW/strings.xml | 8 - .../app/src/main/res/values/arrays.xml | 14 + .../app/src/main/res/values/strings.xml | 23 +- src/common/android/id_cache.cpp | 1028 +++++++++-------- src/common/android/id_cache.h | 3 + src/frontend_common/CMakeLists.txt | 2 + src/frontend_common/firmware_manager.cpp | 139 +++ src/frontend_common/firmware_manager.h | 148 +++ src/yuzu/main.cpp | 206 ++-- 39 files changed, 1146 insertions(+), 1078 deletions(-) create mode 100644 src/frontend_common/firmware_manager.cpp create mode 100644 src/frontend_common/firmware_manager.h 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 c7d6d6c33a..c0e5983fc6 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 @@ -15,6 +15,7 @@ import android.view.Surface import android.view.View import android.widget.TextView import androidx.annotation.Keep +import androidx.core.net.toUri import com.google.android.material.dialog.MaterialAlertDialogBuilder import net.swiftzer.semver.SemVer import java.lang.ref.WeakReference @@ -27,6 +28,7 @@ import org.yuzu.yuzu_emu.model.InstallResult import org.yuzu.yuzu_emu.model.Patch import org.yuzu.yuzu_emu.model.GameVerificationResult import org.yuzu.yuzu_emu.network.NetPlayManager +import java.io.File /** * Class which contains methods that interact @@ -102,6 +104,21 @@ object NativeLibrary { FileUtil.getFilename(Uri.parse(path)) } + @Keep + @JvmStatic + fun copyFileToStorage(source: String, destdir: String): Boolean { + return FileUtil.copyUriToInternalStorage( + source.toUri(), + destdir + ) != null + } + + @Keep + @JvmStatic + fun getFileExtension(source: String): String { + return FileUtil.getExtension(source.toUri()) + } + external fun setAppDirectory(directory: String) /** @@ -415,18 +432,29 @@ object NativeLibrary { */ external fun firmwareVersion(): String - fun isFirmwareSupported(): Boolean { - var version: SemVer + /** + * Verifies installed firmware. + * + * @return The result code. + */ + external fun verifyFirmware(): Int - try { - version = SemVer.parse(firmwareVersion()) - } catch (_: Exception) { - return false - } - val max = SemVer(19, 0, 1) + /** + * Check if a game requires firmware to be playable. + * + * @param programId The game's Program ID. + * @return Whether or not the game requires firmware to be playable. + */ + external fun gameRequiresFirmware(programId: String): Boolean - return version <= max - } + /** + * Installs keys from the specified path. + * + * @param path The path to install keys from. + * @param ext What extension the keys should have. + * @return The result code. + */ + external fun installKeys(path: String, ext: String): Int /** * Checks the PatchManager for any addons that are available 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 750e8f4729..c4652f55e1 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 @@ -3,11 +3,16 @@ package org.yuzu.yuzu_emu.adapters +import android.content.DialogInterface import android.net.Uri +import android.text.Html +import android.text.method.LinkMovementMethod import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import android.widget.ImageView +import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.content.pm.ShortcutInfoCompat @@ -33,6 +38,10 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils import org.yuzu.yuzu_emu.utils.ViewUtils.marquee import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder import androidx.recyclerview.widget.RecyclerView +import androidx.core.net.toUri +import androidx.core.content.edit +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.NativeLibrary class GameAdapter(private val activity: AppCompatActivity) : AbstractDiffAdapter(exact = false) { @@ -171,8 +180,9 @@ class GameAdapter(private val activity: AppCompatActivity) : fun onClick(game: Game) { val gameExists = DocumentFile.fromSingleUri( YuzuApplication.appContext, - Uri.parse(game.path) + game.path.toUri() )?.exists() == true + if (!gameExists) { Toast.makeText( YuzuApplication.appContext, @@ -184,29 +194,49 @@ class GameAdapter(private val activity: AppCompatActivity) : return } - val preferences = - PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - preferences.edit() - .putLong( - game.keyLastPlayedTime, - System.currentTimeMillis() - ) - .apply() - - activity.lifecycleScope.launch { - withContext(Dispatchers.IO) { - val shortcut = - ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path) - .setShortLabel(game.title) - .setIcon(GameIconUtils.getShortcutIcon(activity, game)) - .setIntent(game.launchIntent) - .build() - ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) + val launch: () -> Unit = { + val preferences = + PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) + preferences.edit { + putLong( + game.keyLastPlayedTime, + System.currentTimeMillis() + ) } + + activity.lifecycleScope.launch { + withContext(Dispatchers.IO) { + val shortcut = + ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path) + .setShortLabel(game.title) + .setIcon(GameIconUtils.getShortcutIcon(activity, game)) + .setIntent(game.launchIntent) + .build() + ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) + } + } + + val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true) + binding.root.findNavController().navigate(action) } - val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true) - binding.root.findNavController().navigate(action) + if (NativeLibrary.gameRequiresFirmware(game.programId) && !NativeLibrary.isFirmwareAvailable()) { + MaterialAlertDialogBuilder(activity) + .setTitle(R.string.loader_requires_firmware) + .setMessage( + Html.fromHtml( + activity.getString(R.string.loader_requires_firmware_description), + Html.FROM_HTML_MODE_LEGACY + ) + ) + .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> + launch() + } + .setNegativeButton(android.R.string.cancel) { _,_ -> } + .show() + } else { + launch() + } } fun onLongClick(game: Game): Boolean { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt index 0c1e39d095..91670b207d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt @@ -264,8 +264,6 @@ class DriverFetcherFragment : Fragment() { } releases.add(release) - - println(release.publishTime) } } 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 f39d9514b3..5fed99e0b0 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 @@ -137,7 +137,7 @@ class HomeSettingsFragment : Fragment() { binding.root.findNavController() .navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment) }, - { NativeLibrary.isFirmwareAvailable() && NativeLibrary.isFirmwareSupported() }, + { NativeLibrary.isFirmwareAvailable() }, R.string.applets_error_firmware, R.string.applets_error_description ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt index 6443067885..61797f75f5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt @@ -352,7 +352,7 @@ class SetupFragment : Fragment() { val getProdKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> if (result != null) { - mainActivity.processKey(result) + mainActivity.processKey(result, "keys") if (NativeLibrary.areKeysPresent()) { keyCallback.onStepCompleted() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt index 1a74057569..43b9085f50 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt @@ -47,9 +47,6 @@ import info.debatty.java.stringsimilarity.Jaccard import info.debatty.java.stringsimilarity.JaroWinkler import java.util.Locale import androidx.core.content.edit -import androidx.core.view.updateLayoutParams -import org.yuzu.yuzu_emu.features.settings.model.Settings -import android.view.ViewParent import androidx.core.view.doOnNextLayout class GamesFragment : Fragment() { @@ -151,7 +148,7 @@ class GamesFragment : Fragment() { ) } gamesViewModel.games.collect(viewLifecycleOwner) { - if (it.size > 0) { + if (it.isNotEmpty()) { setAdapter(it) } } @@ -361,7 +358,7 @@ class GamesFragment : Fragment() { popup.setOnMenuItemClickListener { item -> currentFilter = item.itemId - preferences.edit().putInt(PREF_SORT_TYPE, currentFilter).apply() + preferences.edit { putInt(PREF_SORT_TYPE, currentFilter) } filterAndSearch() true } 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 f3800f94e9..f8ba35dbd5 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 @@ -6,6 +6,8 @@ package org.yuzu.yuzu_emu.ui.main import android.content.Intent import android.net.Uri import android.os.Bundle +import android.os.ParcelFileDescriptor +import android.provider.OpenableColumns import android.view.View import android.view.ViewGroup.MarginLayoutParams import android.view.WindowManager @@ -47,6 +49,7 @@ import java.io.BufferedOutputStream import java.util.zip.ZipEntry import java.util.zip.ZipInputStream import androidx.core.content.edit +import androidx.core.net.toFile class MainActivity : AppCompatActivity(), ThemeProvider { private lateinit var binding: ActivityMainBinding @@ -70,10 +73,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val granted = permissions.entries.all { it.value } if (granted) { // Permissions were granted. - Toast.makeText(this, R.string.bluetooth_permissions_granted, Toast.LENGTH_SHORT).show() + Toast.makeText(this, R.string.bluetooth_permissions_granted, Toast.LENGTH_SHORT) + .show() } else { // Permissions were denied. - Toast.makeText(this, R.string.bluetooth_permissions_denied, Toast.LENGTH_LONG).show() + Toast.makeText(this, R.string.bluetooth_permissions_denied, Toast.LENGTH_LONG) + .show() } } @@ -94,7 +99,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } } } - + override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } @@ -105,13 +110,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider { NativeLibrary.initMultiplayer() binding = ActivityMainBinding.inflate(layoutInflater) - + setContentView(binding.root) - - + + checkAndRequestBluetoothPermissions() - + if (savedInstanceState != null) { checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION) checkedFirmware = savedInstanceState.getBoolean(CHECKED_FIRMWARE) @@ -146,22 +151,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider { binding.statusBarShade.setBackgroundColor( ThemeHelper.getColorWithOpacity( MaterialColors.getColor( - binding.root, - com.google.android.material.R.attr.colorSurface - ), - ThemeHelper.SYSTEM_BAR_ALPHA + binding.root, com.google.android.material.R.attr.colorSurface + ), ThemeHelper.SYSTEM_BAR_ALPHA ) ) - if (InsetsHelper.getSystemGestureType(applicationContext) != - InsetsHelper.GESTURE_NAVIGATION - ) { + if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) { binding.navigationBarShade.setBackgroundColor( ThemeHelper.getColorWithOpacity( MaterialColors.getColor( - binding.root, - com.google.android.material.R.attr.colorSurface - ), - ThemeHelper.SYSTEM_BAR_ALPHA + binding.root, com.google.android.material.R.attr.colorSurface + ), ThemeHelper.SYSTEM_BAR_ALPHA ) ) } @@ -172,9 +171,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) } homeViewModel.contentToInstall.collect( - this, - resetState = { homeViewModel.setContentToInstall(null) } - ) { + this, resetState = { homeViewModel.setContentToInstall(null) }) { if (it != null) { installContent(it) } @@ -183,7 +180,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider { if (it) checkKeys() } - homeViewModel.checkFirmware.collect(this, resetState = { homeViewModel.setCheckFirmware(false) }) { + homeViewModel.checkFirmware.collect( + this, resetState = { homeViewModel.setCheckFirmware(false) }) { if (it) checkFirmware() } @@ -203,12 +201,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider { negativeButtonTitleId = R.string.close, showNegativeButton = true, positiveAction = { - PreferenceManager.getDefaultSharedPreferences(applicationContext) - .edit() { - putBoolean(Settings.PREF_SHOULD_SHOW_PRE_ALPHA_WARNING, false) - } - } - ).show(supportFragmentManager, MessageDialogFragment.TAG) + PreferenceManager.getDefaultSharedPreferences(applicationContext).edit() { + putBoolean(Settings.PREF_SHOULD_SHOW_PRE_ALPHA_WARNING, false) + } + }).show(supportFragmentManager, MessageDialogFragment.TAG) } } @@ -228,15 +224,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } private fun checkFirmware() { - if (!NativeLibrary.isFirmwareAvailable() || !NativeLibrary.isFirmwareSupported()) { - MessageDialogFragment.newInstance( - titleId = R.string.firmware_missing, - descriptionId = R.string.firmware_missing_description, - helpLinkId = R.string.firmware_missing_help - ).show(supportFragmentManager, MessageDialogFragment.TAG) - } - } + val resultCode: Int = NativeLibrary.verifyFirmware() + if (resultCode == 0) return; + val resultString: String = + resources.getStringArray(R.array.verifyFirmwareResults)[resultCode] + + MessageDialogFragment.newInstance( + titleId = R.string.firmware_invalid, + descriptionString = resultString, + helpLinkId = R.string.firmware_missing_help + ).show(supportFragmentManager, MessageDialogFragment.TAG) + } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) @@ -283,23 +282,22 @@ class MainActivity : AppCompatActivity(), ThemeProvider { super.onResume() } - private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener( - binding.root - ) { _: View, windowInsets: WindowInsetsCompat -> - val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) - val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams - mlpStatusShade.height = insets.top - binding.statusBarShade.layoutParams = mlpStatusShade + private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { _: View, windowInsets: WindowInsetsCompat -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams + mlpStatusShade.height = insets.top + binding.statusBarShade.layoutParams = mlpStatusShade - // The only situation where we care to have a nav bar shade is when it's at the bottom - // of the screen where scrolling list elements can go behind it. - val mlpNavShade = binding.navigationBarShade.layoutParams as MarginLayoutParams - mlpNavShade.height = insets.bottom - binding.navigationBarShade.layoutParams = mlpNavShade + // The only situation where we care to have a nav bar shade is when it's at the bottom + // of the screen where scrolling list elements can go behind it. + val mlpNavShade = binding.navigationBarShade.layoutParams as MarginLayoutParams + mlpNavShade.height = insets.bottom + binding.navigationBarShade.layoutParams = mlpNavShade - windowInsets - } + windowInsets + } override fun setTheme(resId: Int) { super.setTheme(resId) @@ -315,17 +313,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider { fun processGamesDir(result: Uri, calledFromGameFragment: Boolean = false) { contentResolver.takePersistableUriPermission( - result, - Intent.FLAG_GRANT_READ_URI_PERMISSION + result, Intent.FLAG_GRANT_READ_URI_PERMISSION ) val uriString = result.toString() val folder = gamesViewModel.folders.value.firstOrNull { it.uriString == uriString } if (folder != null) { Toast.makeText( - applicationContext, - R.string.folder_already_added, - Toast.LENGTH_SHORT + applicationContext, R.string.folder_already_added, Toast.LENGTH_SHORT ).show() return } @@ -334,72 +329,50 @@ class MainActivity : AppCompatActivity(), ThemeProvider { .show(supportFragmentManager, AddGameFolderDialogFragment.TAG) } - val getProdKey = - registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> - if (result != null) { - processKey(result) - } + val getProdKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> + if (result != null) { + processKey(result, "keys") } - - fun processKey(result: Uri): Boolean { - if (FileUtil.getExtension(result) != "keys") { - MessageDialogFragment.newInstance( - this, - titleId = R.string.reading_keys_failure, - descriptionId = R.string.install_prod_keys_failure_extension_description - ).show(supportFragmentManager, MessageDialogFragment.TAG) - return false - } - - contentResolver.takePersistableUriPermission( - result, - Intent.FLAG_GRANT_READ_URI_PERMISSION - ) - - val dstPath = DirectoryInitialization.userDirectory + "/keys/" - if (FileUtil.copyUriToInternalStorage( - result, - dstPath, - "prod.keys" - ) != null - ) { - if (NativeLibrary.reloadKeys()) { - Toast.makeText( - applicationContext, - R.string.install_keys_success, - Toast.LENGTH_SHORT - ).show() - homeViewModel.setCheckKeys(true) - - val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext) - .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true) - if (!firstTimeSetup) { - homeViewModel.setCheckFirmware(true) - } - - gamesViewModel.reloadGames(true) - return true - } else { - MessageDialogFragment.newInstance( - this, - titleId = R.string.invalid_keys_error, - descriptionId = R.string.install_keys_failure_description, - helpLinkId = R.string.dumping_keys_quickstart_link - ).show(supportFragmentManager, MessageDialogFragment.TAG) - return false - } - } - return false } - val getFirmware = - registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> - if (result == null) { - return@registerForActivityResult - } + val getAmiiboKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> + if (result != null) { + processKey(result, "bin") + } + } + fun processKey(result: Uri, extension: String = "keys") { + contentResolver.takePersistableUriPermission( + result, Intent.FLAG_GRANT_READ_URI_PERMISSION + ) + + val resultCode: Int = NativeLibrary.installKeys(result.toString(), extension); + + if (resultCode == 0) { + Toast.makeText( + applicationContext, R.string.keys_install_success, Toast.LENGTH_SHORT + ).show() + + gamesViewModel.reloadGames(true) + + return + } + + val resultString: String = + resources.getStringArray(R.array.installKeysResults)[resultCode] + + MessageDialogFragment.newInstance( + titleId = R.string.keys_failed, + descriptionString = resultString, + helpLinkId = R.string.keys_missing_help + ).show(supportFragmentManager, MessageDialogFragment.TAG) + } + + val getFirmware = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> + if (result != null) { processFirmware(result) } + } fun processFirmware(result: Uri, onComplete: (() -> Unit)? = null) { val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") } @@ -409,15 +382,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val cacheFirmwareDir = File("${cacheDir.path}/registered/") ProgressDialogFragment.newInstance( - this, - R.string.firmware_installing + this, R.string.firmware_installing ) { progressCallback, _ -> var messageToShow: Any try { FileUtil.unzipToInternalStorage( - result.toString(), - cacheFirmwareDir, - progressCallback + result.toString(), cacheFirmwareDir, progressCallback ) val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 @@ -448,10 +418,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } fun uninstallFirmware() { - val firmwarePath = File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/") + val firmwarePath = + File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/") ProgressDialogFragment.newInstance( - this, - R.string.firmware_uninstalling + this, R.string.firmware_uninstalling ) { progressCallback, _ -> var messageToShow: Any try { @@ -473,49 +443,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { messageToShow }.show(supportFragmentManager, ProgressDialogFragment.TAG) } - val getAmiiboKey = - registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> - if (result == null) { - return@registerForActivityResult - } - - if (FileUtil.getExtension(result) != "bin") { - MessageDialogFragment.newInstance( - this, - titleId = R.string.reading_keys_failure, - descriptionId = R.string.install_amiibo_keys_failure_extension_description - ).show(supportFragmentManager, MessageDialogFragment.TAG) - return@registerForActivityResult - } - - contentResolver.takePersistableUriPermission( - result, - Intent.FLAG_GRANT_READ_URI_PERMISSION - ) - - val dstPath = DirectoryInitialization.userDirectory + "/keys/" - if (FileUtil.copyUriToInternalStorage( - result, - dstPath, - "key_retail.bin" - ) != null - ) { - if (NativeLibrary.reloadKeys()) { - Toast.makeText( - applicationContext, - R.string.install_keys_success, - Toast.LENGTH_SHORT - ).show() - } else { - MessageDialogFragment.newInstance( - this, - titleId = R.string.invalid_keys_error, - descriptionId = R.string.install_keys_failure_description, - helpLinkId = R.string.dumping_keys_quickstart_link - ).show(supportFragmentManager, MessageDialogFragment.TAG) - } - } - } val installGameUpdate = registerForActivityResult( ActivityResultContracts.OpenMultipleDocuments() @@ -530,15 +457,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } ProgressDialogFragment.newInstance( - this@MainActivity, - R.string.verifying_content, - false + this@MainActivity, R.string.verifying_content, false ) { _, _ -> var updatesMatchProgram = true for (document in documents) { val valid = NativeLibrary.doesUpdateMatchProgram( - addonViewModel.game!!.programId, - document.toString() + addonViewModel.game!!.programId, document.toString() ) if (!valid) { updatesMatchProgram = false @@ -554,16 +478,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider { titleId = R.string.content_install_notice, descriptionId = R.string.content_install_notice_description, positiveAction = { homeViewModel.setContentToInstall(documents) }, - negativeAction = {} - ) + negativeAction = {}) } }.show(supportFragmentManager, ProgressDialogFragment.TAG) } private fun installContent(documents: List) { ProgressDialogFragment.newInstance( - this@MainActivity, - R.string.installing_game_content + this@MainActivity, R.string.installing_game_content ) { progressCallback, messageCallback -> var installSuccess = 0 var installOverwrite = 0 @@ -571,14 +493,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider { var error = 0 documents.forEach { messageCallback.invoke(FileUtil.getFilename(it)) - when ( - InstallResult.from( - NativeLibrary.installFileToNand( - it.toString(), - progressCallback - ) + when (InstallResult.from( + NativeLibrary.installFileToNand( + it.toString(), progressCallback ) - ) { + )) { InstallResult.Success -> { installSuccess += 1 } @@ -599,13 +518,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider { addonViewModel.refreshAddons() - val separator = System.getProperty("line.separator") ?: "\n" + val separator = System.lineSeparator() ?: "\n" val installResult = StringBuilder() if (installSuccess > 0) { installResult.append( getString( - R.string.install_game_content_success_install, - installSuccess + R.string.install_game_content_success_install, installSuccess ) ) installResult.append(separator) @@ -613,8 +531,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { if (installOverwrite > 0) { installResult.append( getString( - R.string.install_game_content_success_overwrite, - installOverwrite + R.string.install_game_content_success_overwrite, installOverwrite ) ) installResult.append(separator) @@ -624,8 +541,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { installResult.append(separator) installResult.append( getString( - R.string.install_game_content_failed_count, - errorTotal + R.string.install_game_content_failed_count, errorTotal ) ) installResult.append(separator) @@ -666,9 +582,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } ProgressDialogFragment.newInstance( - this, - R.string.exporting_user_data, - true + this, R.string.exporting_user_data, true ) { progressCallback, _ -> val zipResult = FileUtil.zipFromInternalStorage( File(DirectoryInitialization.userDirectory!!), @@ -692,8 +606,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } ProgressDialogFragment.newInstance( - this, - R.string.importing_user_data + this, R.string.importing_user_data ) { progressCallback, _ -> val checkStream = ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result))) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index fe7c0658d4..9fed0b1449 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -60,6 +60,7 @@ #include "core/hle/service/set/system_settings_server.h" #include "core/loader/loader.h" #include "frontend_common/config.h" +#include "frontend_common/firmware_manager.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" #include "hid_core/hid_types.h" @@ -283,6 +284,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string : Service::AM::LaunchType::ApplicationInitiated, .program_index = static_cast(program_index), }; + m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params); if (m_load_result != Core::SystemResultStatus::Success) { return m_load_result; @@ -764,35 +766,19 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass cl } bool isFirmwarePresent() { - 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; + return FirmwareManager::CheckFirmwarePresence(EmulationSession::GetInstance().System()); } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env, jclass clazz) { return isFirmwarePresent(); } -// TODO(crueter): This check is nonfunctional... jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_firmwareVersion(JNIEnv* env, jclass clazz) { - Service::Set::FirmwareVersionFormat firmware_data{}; - const auto result = Service::Set::GetFirmwareVersionImpl( - firmware_data, EmulationSession::GetInstance().System(), - Service::Set::GetFirmwareVersionType::Version2); + const auto pair = FirmwareManager::GetFirmwareVersion(EmulationSession::GetInstance().System()); + const auto firmware_data = pair.first; + const auto result = pair.second; if (result.IsError() || !isFirmwarePresent()) { - LOG_INFO(Frontend, "Installed firmware: No firmware available"); - return Common::Android::ToJString(env, "N/A"); } @@ -804,6 +790,23 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_firmwareVersion(JNIEnv* env, jclas return Common::Android::ToJString(env, display_version); } +jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyFirmware(JNIEnv* env, jclass clazz) { + return static_cast(FirmwareManager::VerifyFirmware(EmulationSession::GetInstance().System())); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_gameRequiresFirmware(JNIEnv* env, jclass clazz, jstring jprogramId) { + auto program_id = EmulationSession::GetProgramId(env, jprogramId); + + return FirmwareManager::GameRequiresFirmware(program_id); +} + +jint Java_org_yuzu_yuzu_1emu_NativeLibrary_installKeys(JNIEnv* env, jclass clazz, jstring jpath, jstring jext) { + const auto path = Common::Android::GetJString(env, jpath); + const auto ext = Common::Android::GetJString(env, jext); + + return static_cast(FirmwareManager::InstallKeys(path, ext)); +} + jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj, jstring jpath, jstring jprogramId) { diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index 4ea8b30c1e..e100bafe7e 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -349,13 +349,7 @@ إلغاء تثبيت مفاتيح أميبو مطلوب لاستخدام أميبو في اللعبة - تم تحديد ملف مفاتيح غير صالح - تم تثبيت المفاتيح بنجاح - خطأ في قراءة مفاتيح التشفير - وحاول مرة أخر keys تحقق من أن ملف المفاتيح له امتداد وحاول مرة أخر bin تحقق من أن ملف المفاتيح له امتداد - مفاتيح التشفير غير صالحة - الملف المحدد غير صحيح أو تالف. يرجى إعادة المفاتيح الخاصة بك GPU مدير برنامج تشغيل GPU تثبيت برنامج تشغيل قم بتثبيت برامج تشغيل بديلة للحصول على أداء أو دقة أفضل @@ -422,8 +416,6 @@ فحص المحتوى المثبت بحثًا عن تلف مفاتيح التشفير مفقودة لا يمكن فك تشفير البرنامج الثابت والألعاب - البرنامج الثابت مفقود أو جديد جدًا - بعض الألعاب قد لا تعمل بشكل صحيح. يتطلب إيدن البرنامج الثابت 19.0.1 أو أقل. Qlaunch diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml index af4f2056b3..d13f24d173 100644 --- a/src/android/app/src/main/res/values-ckb/strings.xml +++ b/src/android/app/src/main/res/values-ckb/strings.xml @@ -344,13 +344,7 @@ ڕەتکردنەوە دامەزراندنی کلیلی Amiibo پێویستە بۆ بەکارهێنانی Amiibo لە یاریدا - فایلی کلیلێکی نادروست هەڵبژێردرا - کلیلەکان بە سەرکەوتوویی دامەزران - هەڵە لە خوێندنەوەی کۆدکردنی کلیل - دڵنیابەوە کە فایلی کلیلەکانت درێژکراوەی .keys ی هەیە و دووبارە هەوڵبدەرەوە. دڵنیابە کە فایلی کلیلەکانت درێژکراوەی .bin ی هەیە و دووبارە هەوڵبدەرەوە. - کلیلی کۆدکردنی نادروستە - فایلە هەڵبژێردراوەکە هەڵەیە یان تێکچووە. تکایە دووبارە کلیلەکانت دەربێنەوە. دامەزراندنی وەگەڕخەری GPU دامەزراندنی وەگەڕخەری بەدیل بۆ ئەوەی بە ئەگەرێکی زۆرەوە کارایی باشتر یان وردبینی هەبێت ڕێکخستنە پێشکەوتووەکان @@ -416,8 +410,6 @@ هەموو ناوەڕۆکی دامەزراو پشکنین دەکات بۆ تێکچوون کلیلە کۆدکراوەکان دیار نییە پتەوواڵا و یارییە تاکەکەسییەکان ناتوانرێت کۆد بکرێنەوە - فریموێر بوونی نییە یان زۆر نوێە - هەندێ یاری لەوانەیە باش کار نەکەن. ئێدەن پێویستی بە فریموێری ١٩.٠.١ یان کەمترە. Qlaunch diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml index c71ea42f75..fe8575512e 100644 --- a/src/android/app/src/main/res/values-cs/strings.xml +++ b/src/android/app/src/main/res/values-cs/strings.xml @@ -333,10 +333,6 @@ Zrušit Instalovat Amiibo klíče Povinné použití Amiibo ve hře - Vybrané klíče jsou neplatné - Klíče úspěšně nainstalovány - Chyba při čtení šifrovacích klíčů - Neplatné šifrovací klíče Správce ovladače GPU Instalovat GPU ovladač Pokročilé nastavení @@ -380,8 +376,6 @@ Kontrola poškození obsahu Chybí šifrovací klíče Firmware a retail hry nelze dešifrovat - Firmware chybí nebo je příliš nový - Některé hry nemusí fungovat správně. Eden vyžaduje firmware 19.0.1 nebo nižší. Qlaunch 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 06b14f7c15..93881eabb5 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -346,13 +346,7 @@ Abbrechen Amiibo-Schlüssel installieren Benötigt um Amiibos im Spiel zu verwenden - Ungültige Schlüsseldatei ausgewählt - Schlüssel erfolgreich installiert - Fehler beim Lesen der Schlüssel - Überprüfen Sie, ob Ihre Schlüsseldatei die Erweiterung \".keys\" hat, und versuchen Sie es erneut. Überprüfen Sie, ob Ihre Schlüsseldatei die Erweiterung \".bin\" hat, und versuchen Sie es erneut. - Ungültige Schlüssel - Die ausgewählte Datei ist falsch oder beschädigt. Bitte kopieren Sie Ihre Schlüssel erneut. GPU-Treiber Verwaltung GPU-Treiber installieren Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren @@ -415,8 +409,6 @@ Wirklich fortfahren? Überprüft installierte Inhalte auf Fehler Schlüssel fehlen Firmware und Spiele können nicht entschlüsselt werden - Firmware fehlt oder ist zu neu - Einige Spiele funktionieren möglicherweise nicht richtig. Eden erfordert Firmware 19.0.1 oder älter. Qlaunch 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 9cac800975..a55bf4d935 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -347,13 +347,7 @@ Cancelar Instalar claves de Amiibo Necesario para usar Amiibos en el juego - Archivo de claves seleccionado no válido - Claves instaladas correctamente - Error al leer las claves de cifrado - Compruebe que el archivo de claves tenga una extensión .keys y pruebe otra vez. Compruebe que el archivo de claves tenga una extensión .bin y pruebe otra vez. - Claves de cifrado no válidas - El archivo seleccionado es incorrecto o está corrupto. Vuelva a redumpear sus claves. Explorador de drivers de GPU Instalar driver de GPU Instale drivers alternativos para obtener un rendimiento o una precisión potencialmente mejores @@ -428,8 +422,6 @@ Comprueba todo el contenido instalado por si hubiese alguno corrupto Faltan las claves de encriptación El firmware y los juegos no se pueden desencriptar - Falta el firmware o es demasiado nuevo - Algunos juegos pueden no funcionar correctamente. Eden requiere firmware 19.0.1 o inferior. Qlaunch diff --git a/src/android/app/src/main/res/values-fa/strings.xml b/src/android/app/src/main/res/values-fa/strings.xml index 48f024322b..99f48c1c9d 100644 --- a/src/android/app/src/main/res/values-fa/strings.xml +++ b/src/android/app/src/main/res/values-fa/strings.xml @@ -347,13 +347,7 @@ لغو کلیدهای Amiibo را نصب کنید برای استفاده از Amiibo در بازی لازم است - فایل کلیدهای نامعتبر انتخاب شد - کلیدها با موفقیت نصب شدند - خطا در خواندن کلیدهای رمزگذاری - بررسی کنید که فایل کلیدهای شما دارای پسوند keys. باشد و دوباره امتحان کنید. بررسی کنید که فایل کلیدهای شما دارای پسوند bin. باشد و دوباره امتحان کنید. - کلیدهای رمزگذاری نامعتبر - فایل انتخابی نادرست یا خراب است. لطفا کلیدهای خود را دوباره استخراج کنید. مدیریت درایور پردازنده گرافیکی نصب درایور پردازنده گرافیکی درایورهای جایگزین را برای عملکرد یا دقت بهتر نصب کنید @@ -426,8 +420,6 @@ تمام محتوای نصب شده را از نظر خرابی بررسی می‌کند کلیدهای رمزگذاری وجود ندارند ثابت‌افزار و بازی‌های فروشگاهی قابل رمزگشایی نیستند - فریمور وجود ندارد یا خیلی جدید است - برخی بازی‌ها ممکن است به درستی کار نکنند. ایدن به فریمور نسخه 19.0.1 یا پایین‌تر نیاز دارد. Qlaunch 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 f8ace84f1f..d9783766fe 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -347,13 +347,7 @@ Annuler Installer les clés Amiibo Nécessaire pour utiliser les Amiibo en jeu - Fichier de clés sélectionné invalide - Clés installées avec succès - Erreur lors de la lecture des clés de chiffrement - Vérifiez que votre fichier de clés a une extension .keys et réessayez. Vérifiez que votre fichier de clés a une extension .bin et réessayez. - Clés de chiffrement invalides - Le fichier sélectionné est incorrect ou corrompu. Veuillez dumper à nouveau vos clés. Gestionnaire de pilotes du GPU Installer le pilote du GPU Installer des pilotes alternatifs pour des performances ou une précision potentiellement meilleures @@ -428,8 +422,6 @@ Vérifie l\'intégrité des contenus installés Les clés de chiffrement sont manquantes. Le firmware et les jeux commerciaux ne peuvent pas être déchiffrés - Firmware manquant ou trop récent - Certains jeux peuvent ne pas fonctionner correctement. Eden nécessite le firmware 19.0.1 ou antérieur. Qlaunch diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml index 711b71d3ed..47a01d7dbd 100644 --- a/src/android/app/src/main/res/values-he/strings.xml +++ b/src/android/app/src/main/res/values-he/strings.xml @@ -348,13 +348,7 @@ ביטול התקן מפתחות Amiibo נחוץ כדי להשתמש ב Amiibo במשחק - קובץ מפתחות לא חוקי נבחר - מפתחות הותקנו בהצלחה - שגיאה בקריאת מפתחות ההצפנה - ודא שלקובץ המפתחות שלך יש סיומת של key. ונסה/י שוב. ודא/י שלקובץ המפתחות שלך יש סיומת של bin. ונסה/י שוב. - מפתחות הצפנה לא חוקיים - קבוץ שנבחר מושחת או לא נכון. בבקשה הוצא מחדש את המפתחות שלך. מנהל הדרייברים של המעבד הגרפי התקן דרייבר למעבד הגרפי התקן דרייברים אחרים בשביל סיכוי לביצועים או דיוק גבוההים יותר @@ -427,8 +421,6 @@ בודק תוכן מותקן לשגיאות מפתחות הצפנה חסרים לא ניתן לפענח firmware ומשחקים - קושחה חסרה או חדשה מדי - חלק מהמשחקים עשויים לא לפעול כראוי. Eden דורש קושחה בגרסה 19.0.1 או נמוכה יותר. Qlaunch diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml index 0bedc72e29..e14af511c6 100644 --- a/src/android/app/src/main/res/values-hu/strings.xml +++ b/src/android/app/src/main/res/values-hu/strings.xml @@ -346,13 +346,7 @@ Mégse Amiibo kulcsok telepítése Amiibo használata szükséges a játékhoz - Érvénytelen titkosítófájlok kiválasztva - Kulcsok sikeresen telepítve - Hiba történt a titkosítókulcsok olvasása során - Győződj meg róla, hogy a titkosító fájlod .keys kiterjesztéssel rendelkezik, majd próbáld újra. Győződj meg róla, hogy a titkosító fájlod .bin kiterjesztéssel rendelkezik, majd próbáld újra. - Érvénytelen titkosítókulcsok - A kiválasztott fájl helytelen, vagy sérült. Állíts össze egy új kulcsot. GPU illesztőprogram-kezelő GPU illesztőprogram telepítése Alternatív illesztőprogramok telepítése az esetlegesen elérhető teljesítmény és pontosság érdekében @@ -424,8 +418,6 @@ A telepített tartalom épségét ellenőrzi Hiányzó titkosítókulcsok A Firmware és a kiskereskedelmi (retail) játékok nem dekódolhatók - Hiányzó vagy túl új firmware - Néhány játék nem fog megfelelően működni. Az Eden 19.0.1 vagy régebbi firmware-t igényel. Qlaunch diff --git a/src/android/app/src/main/res/values-id/strings.xml b/src/android/app/src/main/res/values-id/strings.xml index 542734e451..2d931d2c5d 100644 --- a/src/android/app/src/main/res/values-id/strings.xml +++ b/src/android/app/src/main/res/values-id/strings.xml @@ -347,13 +347,7 @@ Batalkan Install Amiibo keys Diperlukan untuk menggunakan Amiibo di dalam game - Keys yang dipilih invalid - Keys berhasil diinstal - Error saat mengecek enkripsi keys - Pastikan file keys anda memiliki format .keys dan coba lagi. Pastikan file keys anda memiliki format .bin dan coba lagi. - Keys enkripsi tidak valid - File yang dipilih salah atau rusak. Silakan masukkan kembali kunci Anda. Manajer driver GPU Install driver GPU Instal driver lain untuk kinerja atau akurasi yang berpotensi lebih baik @@ -424,8 +418,6 @@ Memeriksa semua konten yang terinstal dari kerusakan Kunci enkripsi hilang Firmware dan game retail tidak dapat didekripsi - Firmware hilang atau terlalu baru - Beberapa game mungkin tidak berfungsi dengan baik. Eden memerlukan firmware 19.0.1 atau lebih rendah. Qlaunch 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 ad5b9f62cf..33a937ca93 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml @@ -348,13 +348,7 @@ Annulla Installa le chiavi degli Amiibo Necessario per usare gli Amiibo in gioco - Selezionate chiavi non valide - Chiavi installate correttamente - Errore durante la lettura delle chiavi di crittografia - Controlla che le tue chiavi abbiano l\'estensione .keys e prova di nuovo. Controlla che le tue chiavi abbiano l\'estensione .bin e prova di nuovo - Chiavi di crittografia non valide - Il file selezionato è incorretto o corrotto. Per favore riesegui il dump delle tue chiavi. Gestore driver GPU Installa i driver GPU Installa driver alternativi per potenziali prestazioni migliori o accuratezza. @@ -427,8 +421,6 @@ Verifica l\'integrità di tutti i contenuti installati. Chiavi di crittografia mancanti Impossibile decifrare firmware e giochi retail - Firmware mancante o troppo recente - Alcuni giochi potrebbero non funzionare correttamente. Eden richiede il firmware 19.0.1 o precedente. Qlaunch 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 b209943135..c8d23cb6b9 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml @@ -346,13 +346,7 @@ キャンセル Amiibo ゲーム内での Amiibo の使用に必要です - 無効なキーファイルです - 正常にインストールされました - 暗号化キーの読み込み失敗 - キーの拡張子が.keysであることを確認し、再度お試しください。 キーの拡張子が.binであることを確認し、再度お試しください。 - 暗号化キーが無効 - ファイルが間違っているか破損しています。キーを再ダンプしてください。 GPUドライバーの管理 GPUドライバー 代替ドライバーをインストールしてパフォーマンスや精度を向上させます @@ -417,8 +411,6 @@ すべてのインストール済みコンテンツの整合性を確認 暗号化キーが不足 ファームウェアと製品版ゲームを復号化できません - ファームウェアがないか、バージョンが新しすぎます - 一部のゲームが正常に動作しない可能性があります。Edenは19.0.1以下のファームウェアが必要です。 Qlaunch 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 595795570f..deb901fc3f 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -346,13 +346,7 @@ 취소 amiibo 키 설치 게임에서 amiibo 사용 시 필요 - 잘못된 키 파일이 선택됨 - 키 값을 설치했습니다. - 암호화 키 읽기 오류 - 키 파일의 확장자가 .keys인지 확인하고 다시 시도하세요. 키 파일의 확장자가 .bin인지 확인하고 다시 시도하세요. - 암호화 키가 올바르지 않음 - 선택한 파일이 잘못되었거나 손상되었습니다. 키를 다시 덤프하세요. GPU 드라이버 관리자 GPU 드라이버 설치 잠재적인 성능 또는 정확도 개선을 위해 대체 드라이버 설치 @@ -423,8 +417,6 @@ 전체 설치된 콘텐츠의 손상을 확인합니다. 암호화 키를 찾을 수 없음 펌웨어 및 패키지 게임을 해독할 수 없음 - 펌웨어가 없거나 버전이 너무 높습니다 - 일부 게임이 제대로 작동하지 않을 수 있습니다. Eden은 19.0.1 이하 버전의 펌웨어가 필요합니다. Qlaunch 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 82a4601e65..80173a357d 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml @@ -344,13 +344,7 @@ Avbryt Installer Amiibo-nøkler Kreves for å bruke Amiibo i spillet - Ugyldig nøkkelfil valgt - Nøkler vellykket installert - Feil ved lesing av krypteringsnøkler - Kontroller at nøkkelfilen har filtypen .keys, og prøv igjen. Kontroller at nøkkelfilen har filtypen .bin, og prøv igjen. - Ugyldige krypteringsnøkler - Den valgte filen er feil eller ødelagt. Vennligst dump nøklene på nytt. Installer GPU-driver Installer alternative drivere for potensielt bedre ytelse eller nøyaktighet. Avanserte innstillinger @@ -416,8 +410,6 @@ Sjekk for korrupsjon Nøkler mangler Kan ikke dekryptere firmware/spill - Firmware mangler eller er for ny - Noen spill fungerer kanskje ikke skikkelig. Eden krever firmware 19.0.1 eller lavere. Qlaunch 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 394de5fb16..4742f795c8 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -344,13 +344,7 @@ Anuluj Zainstaluj klucze Amiibo Wymagane aby korzystać z Amiibo w grze - Wybrano niepoprawne klucze - Klucze zainstalowane pomyślnie - Błąd podczas odczytu kluczy - Upewnij się że twoje klucze mają rozszerzenie .keys i spróbuj ponownie. Upewnij się że twoje klucze mają rozszerzenie .bin i spróbuj ponownie. - Niepoprawne klucze - Wybrany plik jest niepoprawny lub uszkodzony. Zrzuć ponownie swoje klucze. Zainstaluj sterownik GPU Użyj alternatywnych sterowników aby potencjalnie zwiększyć wydajność i naprawić błędy Ustawienia zaawansowane @@ -416,8 +410,6 @@ Sprawdza integralność zainstalowanych plików. Brak kluczy Firmware i gry nie mogą być odszyfrowane. - Brak firmware lub zbyt nowa wersja - Niektóre gry mogą nie działać poprawnie. Eden wymaga firmware w wersji 19.0.1 lub starszej. Qlaunch 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 46778754ee..a886450e47 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 @@ -347,13 +347,7 @@ Cancelar Instalar chaves Amiibo Necessárias para usar Amiibos em um jogo - Arquivo de chaves selecionado inválido - Chaves instaladas com sucesso - Erro ao ler chaves de encriptação - Verifique se seu arquivo de chaves possui a extensão .keys e tente novamente. Verifique se seu arquivo de chaves possui a extensão .bin e tente novamente. - Chaves de encriptação inválidas - O arquivo selecionado está incorreto ou corrompido. Por favor extraia suas chaves novamente. Gerenciador de driver de GPU Instalar driver para GPU Instale drivers alternativos para desempenho ou precisão potencialmente melhores @@ -428,8 +422,6 @@ Verifica todo o conteúdo instalado em busca de dados corrompidos Faltando chaves de encriptação O firmware e jogos comerciais não poderão ser decriptados - Firmware ausente ou muito recente - Alguns jogos podem não funcionar corretamente. O Eden requer firmware 19.0.1 ou inferior. Qlaunch 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 2da6518d48..caf7090993 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 @@ -347,13 +347,7 @@ Cancelar Instala chaves Amiibo Necessário para usares Amiibo no jogo - Ficheiro de chaves inválido - Chaves instaladas com sucesso - Erro ao ler chaves de encriptação - Verifique se seu arquivo keys possui a extensão .keys e tente novamente. Verifique se seu arquivo keys possui a extensão .bin e tente novamente. - Chaves de encriptação inválidas - O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves. Gerenciador de driver de GPU Instala driver para GPU Instala drivers alternativos para desempenho ou precisão potencialmente melhores @@ -428,8 +422,6 @@ Verifica todo o conteúdo instalado em busca de dados corrompidos Faltando chaves de encriptação O firmware e jogos comerciais não poderão ser decriptados - Firmware em falta ou demasiado recente - Alguns jogos podem não funcionar corretamente. O Eden requer firmware versão 19.0.1 ou inferior. Qlaunch 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 b9f146aa84..3d8371324f 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -346,13 +346,7 @@ Отмена Установить ключи Amiibo Необходимо для использования Amiibo в играх - Выбран неверный файл ключей - Ключи успешно установлены - Ошибка при чтении ключей шифрования - Убедитесь, что файл ключей имеет расширение .keys, и повторите попытку. Убедитесь, что файл ключей имеет расширение .bin, и повторите попытку. - Неверные ключи шифрования - Выбранный файл неверен или поврежден. Пожалуйста, пере-дампите ваши ключи. Менеджер драйверов ГП Установить драйвер ГП Установите альтернативные драйверы для потенциально лучшей производительности и/или точности @@ -430,8 +424,6 @@ Проверяет весь установленный контент на наличие повреждений Отсутствуют ключи шифрования Прошивка и розничные игры не могут быть расшифрованы - Прошивка отсутствует или слишком новая - Некоторые игры могут работать некорректно. Eden требует прошивку версии 19.0.1 или ниже. Qlaunch diff --git a/src/android/app/src/main/res/values-sr/strings.xml b/src/android/app/src/main/res/values-sr/strings.xml index 0217819b68..d133a470b2 100644 --- a/src/android/app/src/main/res/values-sr/strings.xml +++ b/src/android/app/src/main/res/values-sr/strings.xml @@ -298,13 +298,7 @@ Отказати Инсталирајте Амиибо Кеис Потребно је користити Амиибо у игри - Изабрана је неважећа датотека тастера - Кључеви су успешно инсталирани - Грешка приликом чишћења кључева за шифровање - Проверите да датотека кључева има. Екеис Ектенсион и покушајте поново. Проверите да датотека кључева има .бин екстензију и покушајте поново. - Неважеће кључеве за шифровање - Изабрана датотека је нетачна или оштећена. Молим вас да вам умањите кључеве. ГПУ возач фетцхер ГПУ управљач возача Инсталирајте ГПУ драјвер @@ -380,8 +374,6 @@ Шифра о шифрирањима недостају Фирмваре и малопродајне игре не могу се дешифровати - Фирмвер недостаје или је превише нов - Неке игре можда неће радити исправно. Eden захтева фирмвер верзије 19.0.1 или старији. Клаунцх 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 c2ab22b0d4..91b600eded 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -344,12 +344,6 @@ Відміна Встановити ключі Amiibo Необхідно для використання Amiibo в іграх - Вибрано неправильний файл ключів - Ключі успішно встановлено - Помилка під час зчитування ключів шифрування - Переконайтеся, що файл ключів має розширення .keys, і повторіть спробу. - Невірні ключі шифрування - Обраний файл невірний або пошкоджений. Будь ласка, зробіть повторний дамп ваших ключів. Встановити драйвер GPU Встановіть альтернативні драйвери для потенційно кращої продуктивності та/або точності Розширені налаштування @@ -415,8 +409,6 @@ Перевіряє встановлений вміст на наявність помилок. Відсутні ключі Прошивку та роздрібні ігри не вдасться розшифрувати. - Прошивка відсутня або занадто нова - Деякі ігри можуть працювати неправильно. Eden вимагає прошивку версії 19.0.1 або нижче. Qlaunch diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml index 75a797a386..87de8e80dc 100644 --- a/src/android/app/src/main/res/values-vi/strings.xml +++ b/src/android/app/src/main/res/values-vi/strings.xml @@ -344,13 +344,7 @@ Huỷ Cài đặt Amiibo Cần thiết để dùng Amiibo trong trò chơi - Chìa khóa không hợp lệ - Cài đặt chìa khóa thành công - Lỗi đọc keys mã hóa - Xác minh rằng tệp keys của bạn có đuôi .keys và thử lại. Xác minh rằng tệp keys của bạn có đuôi .bin và thử lại. - Keys mã hoá không hợp lệ - Chọn file sai hoặc bị hỏng. Hãy xuất chìa khóa khác Cài đặt driver GPU Cài đặt driver thay thế để có thể có hiệu suất tốt và chính xác hơn Cài đặt nâng cao @@ -416,8 +410,6 @@ Kiểm tra lỗi nội dung đã cài Thiếu keys mã hóa Không thể giải mã firmware và game - Thiếu firmware hoặc phiên bản quá mới - Một số trò chơi có thể không hoạt động bình thường. Eden yêu cầu firmware phiên bản 19.0.1 hoặc thấp hơn. Qlaunch 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 534b2a322d..20f9bdea90 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 @@ -346,13 +346,7 @@ 取消 安装 Amiibo 密钥文件 在遊戏中使用 Amiibo 时必需 - 选择的密钥文件无效 - 密钥文件已成功安装 - 读取加密密钥时出错 - 请确保您的密钥文件扩展名为 .keys 并重试。 请确保您的密钥文件扩展名为 .bin 并重试。 - 无效的加密密钥 - 选择的密钥文件不正确或已损坏。请重新转储密钥文件。 GPU 驱动管理器 安装 GPU 驱动 安装替代的驱动程序以获得更好的性能和精度 @@ -423,8 +417,6 @@ 检查所有安装的内容是否有损坏 密钥缺失 无法解密固件和商业游戏 - 固件缺失或版本过新 - 某些游戏可能无法正常运行。Eden需要19.0.1或更低版本的固件。 Qlaunch 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 5c4494c9aa..dcda2d8260 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 @@ -350,13 +350,7 @@ 取消 安裝 Amiibo 金鑰 需要在遊戲中使用 Amiibo - 無效的金鑰檔案已選取 - 金鑰已成功安裝 - 讀取加密金鑰時發生錯誤 - 驗證您的金鑰檔案是否具有 .keys 副檔名並再試一次。 驗證您的金鑰檔案是否具有 .bin 副檔名並再試一次。 - 無效的加密金鑰 - 選取的檔案不正確或已損毀,請重新傾印您的金鑰。 GPU 驅動程式管理員 安裝 GPU 驅動程式 安裝替代驅動程式以取得潛在的更佳效能或準確度 @@ -427,8 +421,6 @@ 检查所有安装的内容是否有损坏 密钥缺失 无法解密固件和商业游戏 - 韌體缺失或版本過新 - 某些遊戲可能無法正常運作。Eden需要19.0.1或更低版本的韌體。 Qlaunch diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index d037d2680a..855fd6e769 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -496,4 +496,18 @@ 3 + + "" + @string/error_firmware_missing + @string/error_firmware_corrupted + @string/error_firmware_too_new + + + + "" + "" + @string/error_keys_copy_failed + @string/error_keys_invalid_filename + @string/error_keys_failed_init + diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 602be31027..e611e66c1f 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -304,14 +304,8 @@ Cancel Install Amiibo keys Required to use Amiibo in game - Invalid keys file selected - Keys successfully installed - Error reading encryption keys - Verify your keys file has a .keys extension and try again. Verify your keys file has a .bin extension and try again. - Invalid encryption keys https://yuzu-mirror.github.io/help/quickstart/#dumping-decryption-keys - The selected file is incorrect or corrupt. Please redump your keys. GPU driver fetcher GPU driver manager Install GPU driver @@ -390,10 +384,18 @@ Firmware and retail games cannot be decrypted https://yuzu-mirror.github.io/help/quickstart/#dumping-decryption-keys - Firmware is missing or too new - Some games may not function properly. Eden requires firmware 19.0.1 or below. + Firmware Invalid + Firmware is required to run certain games and use the Home Menu. Eden only works with firmware 19.0.1 and earlier. + Firmware reported as present, but was unable to be read. Check for decryption keys and redump firmware if necessary. + Firmware is too new or could not be read. Eden only works with firmware 19.0.1 and earlier. https://yuzu-mirror.github.io/help/quickstart/#dumping-system-firmware + Failed to Install Keys + Keys successfully installed + One or more keys failed to copy. + Verify your keys file has a .keys extension and try again. + Keys failed to initialize. Check that your dumping tools are up to date and re-dump keys. + Qlaunch Launch applications from the system home screen @@ -754,13 +756,16 @@ Your ROM is encrypted - game cartidges or installed titles.]]> + game cartridges or installed titles.]]> prod.keys file is installed so that games can be decrypted.]]> An error occurred initializing the video core This is usually caused by an incompatible GPU driver. Installing a custom GPU driver may resolve this problem. Unable to load ROM ROM file does not exist + Game Requires Firmware + dump and install firmware, or press "OK" to launch anyways.]]> + Exit emulation Done diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp index 2625e55c35..e0edd006a5 100644 --- a/src/common/android/id_cache.cpp +++ b/src/common/android/id_cache.cpp @@ -15,7 +15,7 @@ #include -static JavaVM* s_java_vm; +static JavaVM *s_java_vm; static jclass s_native_library_class; static jclass s_disk_cache_progress_class; static jclass s_load_callback_stage_class; @@ -26,6 +26,9 @@ static jmethodID s_disk_cache_load_progress; static jmethodID s_on_emulation_started; static jmethodID s_on_emulation_stopped; static jmethodID s_on_program_changed; +static jmethodID s_copy_to_storage; +static jmethodID s_file_exists; +static jmethodID s_file_extension; static jclass s_game_class; static jmethodID s_game_constructor; @@ -102,513 +105,534 @@ static constexpr jint JNI_VERSION = JNI_VERSION_1_6; namespace Common::Android { -JNIEnv* GetEnvForThread() { - thread_local static struct OwnedEnv { - OwnedEnv() { - status = s_java_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); - if (status == JNI_EDETACHED) - s_java_vm->AttachCurrentThread(&env, nullptr); - } - - ~OwnedEnv() { - if (status == JNI_EDETACHED) - s_java_vm->DetachCurrentThread(); - } - - int status; - JNIEnv* env = nullptr; - } owned; - return owned.env; -} - -jclass GetNativeLibraryClass() { - return s_native_library_class; -} - -jclass GetDiskCacheProgressClass() { - return s_disk_cache_progress_class; -} - -jclass GetDiskCacheLoadCallbackStageClass() { - return s_load_callback_stage_class; -} - -jclass GetGameDirClass() { - return s_game_dir_class; -} - -jmethodID GetGameDirConstructor() { - return s_game_dir_constructor; -} - -jmethodID GetExitEmulationActivity() { - return s_exit_emulation_activity; -} - -jmethodID GetDiskCacheLoadProgress() { - return s_disk_cache_load_progress; -} - -jmethodID GetOnEmulationStarted() { - return s_on_emulation_started; -} - -jmethodID GetOnEmulationStopped() { - return s_on_emulation_stopped; -} - -jmethodID GetOnProgramChanged() { - return s_on_program_changed; -} - -jclass GetGameClass() { - return s_game_class; -} - -jmethodID GetGameConstructor() { - return s_game_constructor; -} - -jfieldID GetGameTitleField() { - return s_game_title_field; -} - -jfieldID GetGamePathField() { - return s_game_path_field; -} - -jfieldID GetGameProgramIdField() { - return s_game_program_id_field; -} - -jfieldID GetGameDeveloperField() { - return s_game_developer_field; -} - -jfieldID GetGameVersionField() { - return s_game_version_field; -} - -jfieldID GetGameIsHomebrewField() { - return s_game_is_homebrew_field; -} - -jclass GetStringClass() { - return s_string_class; -} - -jclass GetPairClass() { - return s_pair_class; -} - -jmethodID GetPairConstructor() { - return s_pair_constructor; -} - -jfieldID GetPairFirstField() { - return s_pair_first_field; -} - -jfieldID GetPairSecondField() { - return s_pair_second_field; -} - -jclass GetOverlayControlDataClass() { - return s_overlay_control_data_class; -} - -jmethodID GetOverlayControlDataConstructor() { - return s_overlay_control_data_constructor; -} - -jfieldID GetOverlayControlDataIdField() { - return s_overlay_control_data_id_field; -} - -jfieldID GetOverlayControlDataEnabledField() { - return s_overlay_control_data_enabled_field; -} - -jfieldID GetOverlayControlDataLandscapePositionField() { - return s_overlay_control_data_landscape_position_field; -} - -jfieldID GetOverlayControlDataPortraitPositionField() { - return s_overlay_control_data_portrait_position_field; -} - -jfieldID GetOverlayControlDataFoldablePositionField() { - return s_overlay_control_data_foldable_position_field; -} - -jclass GetPatchClass() { - return s_patch_class; -} - -jmethodID GetPatchConstructor() { - return s_patch_constructor; -} - -jfieldID GetPatchEnabledField() { - return s_patch_enabled_field; -} - -jfieldID GetPatchNameField() { - return s_patch_name_field; -} - -jfieldID GetPatchVersionField() { - return s_patch_version_field; -} - -jfieldID GetPatchTypeField() { - return s_patch_type_field; -} - -jfieldID GetPatchProgramIdField() { - return s_patch_program_id_field; -} - -jfieldID GetPatchTitleIdField() { - return s_patch_title_id_field; -} - -jclass GetDoubleClass() { - return s_double_class; -} - -jmethodID GetDoubleConstructor() { - return s_double_constructor; -} - -jfieldID GetDoubleValueField() { - return s_double_value_field; -} - -jclass GetIntegerClass() { - return s_integer_class; -} - -jmethodID GetIntegerConstructor() { - return s_integer_constructor; -} - -jfieldID GetIntegerValueField() { - return s_integer_value_field; -} - -jclass GetBooleanClass() { - return s_boolean_class; -} - -jmethodID GetBooleanConstructor() { - return s_boolean_constructor; -} - -jfieldID GetBooleanValueField() { - return s_boolean_value_field; -} - -jclass GetPlayerInputClass() { - return s_player_input_class; -} - -jmethodID GetPlayerInputConstructor() { - return s_player_input_constructor; -} - -jfieldID GetPlayerInputConnectedField() { - return s_player_input_connected_field; -} - -jfieldID GetPlayerInputButtonsField() { - return s_player_input_buttons_field; -} - -jfieldID GetPlayerInputAnalogsField() { - return s_player_input_analogs_field; -} - -jfieldID GetPlayerInputMotionsField() { - return s_player_input_motions_field; -} - -jfieldID GetPlayerInputVibrationEnabledField() { - return s_player_input_vibration_enabled_field; -} - -jfieldID GetPlayerInputVibrationStrengthField() { - return s_player_input_vibration_strength_field; -} - -jfieldID GetPlayerInputBodyColorLeftField() { - return s_player_input_body_color_left_field; -} - -jfieldID GetPlayerInputBodyColorRightField() { - return s_player_input_body_color_right_field; -} - -jfieldID GetPlayerInputButtonColorLeftField() { - return s_player_input_button_color_left_field; -} - -jfieldID GetPlayerInputButtonColorRightField() { - return s_player_input_button_color_right_field; -} - -jfieldID GetPlayerInputProfileNameField() { - return s_player_input_profile_name_field; -} - -jfieldID GetPlayerInputUseSystemVibratorField() { - return s_player_input_use_system_vibrator_field; -} - -jclass GetYuzuInputDeviceInterface() { - return s_yuzu_input_device_interface; -} - -jmethodID GetYuzuDeviceGetName() { - return s_yuzu_input_device_get_name; -} - -jmethodID GetYuzuDeviceGetGUID() { - return s_yuzu_input_device_get_guid; -} - -jmethodID GetYuzuDeviceGetPort() { - return s_yuzu_input_device_get_port; -} - -jmethodID GetYuzuDeviceGetSupportsVibration() { - return s_yuzu_input_device_get_supports_vibration; -} - -jmethodID GetYuzuDeviceVibrate() { - return s_yuzu_input_device_vibrate; -} - -jmethodID GetYuzuDeviceGetAxes() { - return s_yuzu_input_device_get_axes; -} - -jmethodID GetYuzuDeviceHasKeys() { - return s_yuzu_input_device_has_keys; -} - -jmethodID GetAddNetPlayMessage() { - return s_add_netplay_message; -} - -jmethodID ClearChat() { - return s_clear_chat; -} - -#ifdef __cplusplus -extern "C" { -#endif - -jint JNI_OnLoad(JavaVM* vm, void* reserved) { - s_java_vm = vm; - - JNIEnv* env; - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION) != JNI_OK) - return JNI_ERR; - - // Initialize Java classes - const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary"); - s_native_library_class = reinterpret_cast(env->NewGlobalRef(native_library_class)); - s_disk_cache_progress_class = reinterpret_cast(env->NewGlobalRef( - env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress"))); - s_load_callback_stage_class = reinterpret_cast(env->NewGlobalRef(env->FindClass( - "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); - - const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir"); - s_game_dir_class = reinterpret_cast(env->NewGlobalRef(game_dir_class)); - s_game_dir_constructor = env->GetMethodID(game_dir_class, "", "(Ljava/lang/String;Z)V"); - env->DeleteLocalRef(game_dir_class); - - // Initialize methods - s_exit_emulation_activity = - env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); - s_disk_cache_load_progress = - env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); - s_on_emulation_started = - env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); - s_on_emulation_stopped = - env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); - s_on_program_changed = - env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V"); - - const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); - s_game_class = reinterpret_cast(env->NewGlobalRef(game_class)); - s_game_constructor = env->GetMethodID(game_class, "", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/" - "String;Ljava/lang/String;Ljava/lang/String;Z)V"); - s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;"); - s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;"); - s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;"); - s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;"); - s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;"); - s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z"); - env->DeleteLocalRef(game_class); - - const jclass string_class = env->FindClass("java/lang/String"); - s_string_class = reinterpret_cast(env->NewGlobalRef(string_class)); - env->DeleteLocalRef(string_class); - - const jclass pair_class = env->FindClass("kotlin/Pair"); - s_pair_class = reinterpret_cast(env->NewGlobalRef(pair_class)); - s_pair_constructor = - env->GetMethodID(pair_class, "", "(Ljava/lang/Object;Ljava/lang/Object;)V"); - s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;"); - s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;"); - env->DeleteLocalRef(pair_class); - - const jclass overlay_control_data_class = - env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData"); - s_overlay_control_data_class = - reinterpret_cast(env->NewGlobalRef(overlay_control_data_class)); - s_overlay_control_data_constructor = - env->GetMethodID(overlay_control_data_class, "", - "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V"); - s_overlay_control_data_id_field = - env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;"); - s_overlay_control_data_enabled_field = - env->GetFieldID(overlay_control_data_class, "enabled", "Z"); - s_overlay_control_data_landscape_position_field = - env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;"); - s_overlay_control_data_portrait_position_field = - env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;"); - s_overlay_control_data_foldable_position_field = - env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;"); - env->DeleteLocalRef(overlay_control_data_class); - - const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch"); - s_patch_class = reinterpret_cast(env->NewGlobalRef(patch_class)); - s_patch_constructor = env->GetMethodID( - patch_class, "", - "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"); - s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z"); - s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;"); - s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;"); - s_patch_type_field = env->GetFieldID(patch_class, "type", "I"); - s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;"); - s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;"); - env->DeleteLocalRef(patch_class); - - const jclass double_class = env->FindClass("java/lang/Double"); - s_double_class = reinterpret_cast(env->NewGlobalRef(double_class)); - s_double_constructor = env->GetMethodID(double_class, "", "(D)V"); - s_double_value_field = env->GetFieldID(double_class, "value", "D"); - env->DeleteLocalRef(double_class); - - const jclass int_class = env->FindClass("java/lang/Integer"); - s_integer_class = reinterpret_cast(env->NewGlobalRef(int_class)); - s_integer_constructor = env->GetMethodID(int_class, "", "(I)V"); - s_integer_value_field = env->GetFieldID(int_class, "value", "I"); - env->DeleteLocalRef(int_class); - - const jclass boolean_class = env->FindClass("java/lang/Boolean"); - s_boolean_class = reinterpret_cast(env->NewGlobalRef(boolean_class)); - s_boolean_constructor = env->GetMethodID(boolean_class, "", "(Z)V"); - s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z"); - env->DeleteLocalRef(boolean_class); - - const jclass player_input_class = - env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput"); - s_player_input_class = reinterpret_cast(env->NewGlobalRef(player_input_class)); - s_player_input_constructor = env->GetMethodID( - player_input_class, "", - "(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V"); - s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z"); - s_player_input_buttons_field = - env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;"); - s_player_input_analogs_field = - env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;"); - s_player_input_motions_field = - env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;"); - s_player_input_vibration_enabled_field = - env->GetFieldID(player_input_class, "vibrationEnabled", "Z"); - s_player_input_vibration_strength_field = - env->GetFieldID(player_input_class, "vibrationStrength", "I"); - s_player_input_body_color_left_field = - env->GetFieldID(player_input_class, "bodyColorLeft", "J"); - s_player_input_body_color_right_field = - env->GetFieldID(player_input_class, "bodyColorRight", "J"); - s_player_input_button_color_left_field = - env->GetFieldID(player_input_class, "buttonColorLeft", "J"); - s_player_input_button_color_right_field = - env->GetFieldID(player_input_class, "buttonColorRight", "J"); - s_player_input_profile_name_field = - env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;"); - s_player_input_use_system_vibrator_field = - env->GetFieldID(player_input_class, "useSystemVibrator", "Z"); - env->DeleteLocalRef(player_input_class); - - const jclass yuzu_input_device_interface = - env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice"); - s_yuzu_input_device_interface = - reinterpret_cast(env->NewGlobalRef(yuzu_input_device_interface)); - s_yuzu_input_device_get_name = - env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;"); - s_yuzu_input_device_get_guid = - env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;"); - s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort", "()I"); - s_yuzu_input_device_get_supports_vibration = - env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z"); - s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate", "(F)V"); - s_yuzu_input_device_get_axes = - env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;"); - s_yuzu_input_device_has_keys = - env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z"); - env->DeleteLocalRef(yuzu_input_device_interface); - s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage", - "(ILjava/lang/String;)V"); - s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V"); - - - // Initialize Android Storage - Common::FS::Android::RegisterCallbacks(env, s_native_library_class); - - // Initialize applets - Common::Android::SoftwareKeyboard::InitJNI(env); - - return JNI_VERSION; -} - -void JNI_OnUnload(JavaVM* vm, void* reserved) { - JNIEnv* env; - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION) != JNI_OK) { - return; + JNIEnv *GetEnvForThread() { + thread_local static struct OwnedEnv { + OwnedEnv() { + status = s_java_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + if (status == JNI_EDETACHED) + s_java_vm->AttachCurrentThread(&env, nullptr); + } + + ~OwnedEnv() { + if (status == JNI_EDETACHED) + s_java_vm->DetachCurrentThread(); + } + + int status; + JNIEnv *env = nullptr; + } owned; + return owned.env; } - // UnInitialize Android Storage - Common::FS::Android::UnRegisterCallbacks(); - env->DeleteGlobalRef(s_native_library_class); - env->DeleteGlobalRef(s_disk_cache_progress_class); - env->DeleteGlobalRef(s_load_callback_stage_class); - env->DeleteGlobalRef(s_game_dir_class); - env->DeleteGlobalRef(s_game_class); - env->DeleteGlobalRef(s_string_class); - env->DeleteGlobalRef(s_pair_class); - env->DeleteGlobalRef(s_overlay_control_data_class); - env->DeleteGlobalRef(s_patch_class); - env->DeleteGlobalRef(s_double_class); - env->DeleteGlobalRef(s_integer_class); - env->DeleteGlobalRef(s_boolean_class); - env->DeleteGlobalRef(s_player_input_class); - env->DeleteGlobalRef(s_yuzu_input_device_interface); + jclass GetNativeLibraryClass() { + return s_native_library_class; + } - // UnInitialize applets - SoftwareKeyboard::CleanupJNI(env); + jclass GetDiskCacheProgressClass() { + return s_disk_cache_progress_class; + } - AndroidMultiplayer::NetworkShutdown(); -} + jclass GetDiskCacheLoadCallbackStageClass() { + return s_load_callback_stage_class; + } + + jclass GetGameDirClass() { + return s_game_dir_class; + } + + jmethodID GetGameDirConstructor() { + return s_game_dir_constructor; + } + + jmethodID GetExitEmulationActivity() { + return s_exit_emulation_activity; + } + + jmethodID GetDiskCacheLoadProgress() { + return s_disk_cache_load_progress; + } + + jmethodID GetCopyToStorage() { + return s_copy_to_storage; + } + + jmethodID GetFileExists() { + return s_file_exists; + } + + jmethodID GetFileExtension() { + return s_file_extension; + } + + jmethodID GetOnEmulationStarted() { + return s_on_emulation_started; + } + + jmethodID GetOnEmulationStopped() { + return s_on_emulation_stopped; + } + + jmethodID GetOnProgramChanged() { + return s_on_program_changed; + } + + jclass GetGameClass() { + return s_game_class; + } + + jmethodID GetGameConstructor() { + return s_game_constructor; + } + + jfieldID GetGameTitleField() { + return s_game_title_field; + } + + jfieldID GetGamePathField() { + return s_game_path_field; + } + + jfieldID GetGameProgramIdField() { + return s_game_program_id_field; + } + + jfieldID GetGameDeveloperField() { + return s_game_developer_field; + } + + jfieldID GetGameVersionField() { + return s_game_version_field; + } + + jfieldID GetGameIsHomebrewField() { + return s_game_is_homebrew_field; + } + + jclass GetStringClass() { + return s_string_class; + } + + jclass GetPairClass() { + return s_pair_class; + } + + jmethodID GetPairConstructor() { + return s_pair_constructor; + } + + jfieldID GetPairFirstField() { + return s_pair_first_field; + } + + jfieldID GetPairSecondField() { + return s_pair_second_field; + } + + jclass GetOverlayControlDataClass() { + return s_overlay_control_data_class; + } + + jmethodID GetOverlayControlDataConstructor() { + return s_overlay_control_data_constructor; + } + + jfieldID GetOverlayControlDataIdField() { + return s_overlay_control_data_id_field; + } + + jfieldID GetOverlayControlDataEnabledField() { + return s_overlay_control_data_enabled_field; + } + + jfieldID GetOverlayControlDataLandscapePositionField() { + return s_overlay_control_data_landscape_position_field; + } + + jfieldID GetOverlayControlDataPortraitPositionField() { + return s_overlay_control_data_portrait_position_field; + } + + jfieldID GetOverlayControlDataFoldablePositionField() { + return s_overlay_control_data_foldable_position_field; + } + + jclass GetPatchClass() { + return s_patch_class; + } + + jmethodID GetPatchConstructor() { + return s_patch_constructor; + } + + jfieldID GetPatchEnabledField() { + return s_patch_enabled_field; + } + + jfieldID GetPatchNameField() { + return s_patch_name_field; + } + + jfieldID GetPatchVersionField() { + return s_patch_version_field; + } + + jfieldID GetPatchTypeField() { + return s_patch_type_field; + } + + jfieldID GetPatchProgramIdField() { + return s_patch_program_id_field; + } + + jfieldID GetPatchTitleIdField() { + return s_patch_title_id_field; + } + + jclass GetDoubleClass() { + return s_double_class; + } + + jmethodID GetDoubleConstructor() { + return s_double_constructor; + } + + jfieldID GetDoubleValueField() { + return s_double_value_field; + } + + jclass GetIntegerClass() { + return s_integer_class; + } + + jmethodID GetIntegerConstructor() { + return s_integer_constructor; + } + + jfieldID GetIntegerValueField() { + return s_integer_value_field; + } + + jclass GetBooleanClass() { + return s_boolean_class; + } + + jmethodID GetBooleanConstructor() { + return s_boolean_constructor; + } + + jfieldID GetBooleanValueField() { + return s_boolean_value_field; + } + + jclass GetPlayerInputClass() { + return s_player_input_class; + } + + jmethodID GetPlayerInputConstructor() { + return s_player_input_constructor; + } + + jfieldID GetPlayerInputConnectedField() { + return s_player_input_connected_field; + } + + jfieldID GetPlayerInputButtonsField() { + return s_player_input_buttons_field; + } + + jfieldID GetPlayerInputAnalogsField() { + return s_player_input_analogs_field; + } + + jfieldID GetPlayerInputMotionsField() { + return s_player_input_motions_field; + } + + jfieldID GetPlayerInputVibrationEnabledField() { + return s_player_input_vibration_enabled_field; + } + + jfieldID GetPlayerInputVibrationStrengthField() { + return s_player_input_vibration_strength_field; + } + + jfieldID GetPlayerInputBodyColorLeftField() { + return s_player_input_body_color_left_field; + } + + jfieldID GetPlayerInputBodyColorRightField() { + return s_player_input_body_color_right_field; + } + + jfieldID GetPlayerInputButtonColorLeftField() { + return s_player_input_button_color_left_field; + } + + jfieldID GetPlayerInputButtonColorRightField() { + return s_player_input_button_color_right_field; + } + + jfieldID GetPlayerInputProfileNameField() { + return s_player_input_profile_name_field; + } + + jfieldID GetPlayerInputUseSystemVibratorField() { + return s_player_input_use_system_vibrator_field; + } + + jclass GetYuzuInputDeviceInterface() { + return s_yuzu_input_device_interface; + } + + jmethodID GetYuzuDeviceGetName() { + return s_yuzu_input_device_get_name; + } + + jmethodID GetYuzuDeviceGetGUID() { + return s_yuzu_input_device_get_guid; + } + + jmethodID GetYuzuDeviceGetPort() { + return s_yuzu_input_device_get_port; + } + + jmethodID GetYuzuDeviceGetSupportsVibration() { + return s_yuzu_input_device_get_supports_vibration; + } + + jmethodID GetYuzuDeviceVibrate() { + return s_yuzu_input_device_vibrate; + } + + jmethodID GetYuzuDeviceGetAxes() { + return s_yuzu_input_device_get_axes; + } + + jmethodID GetYuzuDeviceHasKeys() { + return s_yuzu_input_device_has_keys; + } + + jmethodID GetAddNetPlayMessage() { + return s_add_netplay_message; + } + + jmethodID ClearChat() { + return s_clear_chat; + } #ifdef __cplusplus -} + extern "C" { +#endif + + jint JNI_OnLoad(JavaVM *vm, void *reserved) { + s_java_vm = vm; + + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION) != JNI_OK) + return JNI_ERR; + + // Initialize Java classes + const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary"); + s_native_library_class = reinterpret_cast(env->NewGlobalRef(native_library_class)); + s_disk_cache_progress_class = reinterpret_cast(env->NewGlobalRef( + env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress"))); + s_load_callback_stage_class = reinterpret_cast(env->NewGlobalRef(env->FindClass( + "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); + + const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir"); + s_game_dir_class = reinterpret_cast(env->NewGlobalRef(game_dir_class)); + s_game_dir_constructor = env->GetMethodID(game_dir_class, "", + "(Ljava/lang/String;Z)V"); + env->DeleteLocalRef(game_dir_class); + + // Initialize methods + s_exit_emulation_activity = + env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); + s_disk_cache_load_progress = + env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); + s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage", + "(Ljava/lang/String;Ljava/lang/String;)Z"); + s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists", + "(Ljava/lang/String;)Z"); + s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension", + "(Ljava/lang/String;)Ljava/lang/String;"); + s_on_emulation_started = + env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); + s_on_emulation_stopped = + env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); + s_on_program_changed = + env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V"); + + const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); + s_game_class = reinterpret_cast(env->NewGlobalRef(game_class)); + s_game_constructor = env->GetMethodID(game_class, "", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/" + "String;Ljava/lang/String;Ljava/lang/String;Z)V"); + s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;"); + s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;"); + s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;"); + s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;"); + s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;"); + s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z"); + env->DeleteLocalRef(game_class); + + const jclass string_class = env->FindClass("java/lang/String"); + s_string_class = reinterpret_cast(env->NewGlobalRef(string_class)); + env->DeleteLocalRef(string_class); + + const jclass pair_class = env->FindClass("kotlin/Pair"); + s_pair_class = reinterpret_cast(env->NewGlobalRef(pair_class)); + s_pair_constructor = + env->GetMethodID(pair_class, "", "(Ljava/lang/Object;Ljava/lang/Object;)V"); + s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;"); + s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;"); + env->DeleteLocalRef(pair_class); + + const jclass overlay_control_data_class = + env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData"); + s_overlay_control_data_class = + reinterpret_cast(env->NewGlobalRef(overlay_control_data_class)); + s_overlay_control_data_constructor = + env->GetMethodID(overlay_control_data_class, "", + "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V"); + s_overlay_control_data_id_field = + env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;"); + s_overlay_control_data_enabled_field = + env->GetFieldID(overlay_control_data_class, "enabled", "Z"); + s_overlay_control_data_landscape_position_field = + env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;"); + s_overlay_control_data_portrait_position_field = + env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;"); + s_overlay_control_data_foldable_position_field = + env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;"); + env->DeleteLocalRef(overlay_control_data_class); + + const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch"); + s_patch_class = reinterpret_cast(env->NewGlobalRef(patch_class)); + s_patch_constructor = env->GetMethodID( + patch_class, "", + "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"); + s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z"); + s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;"); + s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;"); + s_patch_type_field = env->GetFieldID(patch_class, "type", "I"); + s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;"); + s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;"); + env->DeleteLocalRef(patch_class); + + const jclass double_class = env->FindClass("java/lang/Double"); + s_double_class = reinterpret_cast(env->NewGlobalRef(double_class)); + s_double_constructor = env->GetMethodID(double_class, "", "(D)V"); + s_double_value_field = env->GetFieldID(double_class, "value", "D"); + env->DeleteLocalRef(double_class); + + const jclass int_class = env->FindClass("java/lang/Integer"); + s_integer_class = reinterpret_cast(env->NewGlobalRef(int_class)); + s_integer_constructor = env->GetMethodID(int_class, "", "(I)V"); + s_integer_value_field = env->GetFieldID(int_class, "value", "I"); + env->DeleteLocalRef(int_class); + + const jclass boolean_class = env->FindClass("java/lang/Boolean"); + s_boolean_class = reinterpret_cast(env->NewGlobalRef(boolean_class)); + s_boolean_constructor = env->GetMethodID(boolean_class, "", "(Z)V"); + s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z"); + env->DeleteLocalRef(boolean_class); + + const jclass player_input_class = + env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput"); + s_player_input_class = reinterpret_cast(env->NewGlobalRef(player_input_class)); + s_player_input_constructor = env->GetMethodID( + player_input_class, "", + "(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V"); + s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z"); + s_player_input_buttons_field = + env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;"); + s_player_input_analogs_field = + env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;"); + s_player_input_motions_field = + env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;"); + s_player_input_vibration_enabled_field = + env->GetFieldID(player_input_class, "vibrationEnabled", "Z"); + s_player_input_vibration_strength_field = + env->GetFieldID(player_input_class, "vibrationStrength", "I"); + s_player_input_body_color_left_field = + env->GetFieldID(player_input_class, "bodyColorLeft", "J"); + s_player_input_body_color_right_field = + env->GetFieldID(player_input_class, "bodyColorRight", "J"); + s_player_input_button_color_left_field = + env->GetFieldID(player_input_class, "buttonColorLeft", "J"); + s_player_input_button_color_right_field = + env->GetFieldID(player_input_class, "buttonColorRight", "J"); + s_player_input_profile_name_field = + env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;"); + s_player_input_use_system_vibrator_field = + env->GetFieldID(player_input_class, "useSystemVibrator", "Z"); + env->DeleteLocalRef(player_input_class); + + const jclass yuzu_input_device_interface = + env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice"); + s_yuzu_input_device_interface = + reinterpret_cast(env->NewGlobalRef(yuzu_input_device_interface)); + s_yuzu_input_device_get_name = + env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;"); + s_yuzu_input_device_get_guid = + env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;"); + s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort", + "()I"); + s_yuzu_input_device_get_supports_vibration = + env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z"); + s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate", + "(F)V"); + s_yuzu_input_device_get_axes = + env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;"); + s_yuzu_input_device_has_keys = + env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z"); + env->DeleteLocalRef(yuzu_input_device_interface); + s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage", + "(ILjava/lang/String;)V"); + s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V"); + + + // Initialize Android Storage + Common::FS::Android::RegisterCallbacks(env, s_native_library_class); + + // Initialize applets + Common::Android::SoftwareKeyboard::InitJNI(env); + + return JNI_VERSION; + } + + void JNI_OnUnload(JavaVM *vm, void *reserved) { + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION) != JNI_OK) { + return; + } + + // UnInitialize Android Storage + Common::FS::Android::UnRegisterCallbacks(); + env->DeleteGlobalRef(s_native_library_class); + env->DeleteGlobalRef(s_disk_cache_progress_class); + env->DeleteGlobalRef(s_load_callback_stage_class); + env->DeleteGlobalRef(s_game_dir_class); + env->DeleteGlobalRef(s_game_class); + env->DeleteGlobalRef(s_string_class); + env->DeleteGlobalRef(s_pair_class); + env->DeleteGlobalRef(s_overlay_control_data_class); + env->DeleteGlobalRef(s_patch_class); + env->DeleteGlobalRef(s_double_class); + env->DeleteGlobalRef(s_integer_class); + env->DeleteGlobalRef(s_boolean_class); + env->DeleteGlobalRef(s_player_input_class); + env->DeleteGlobalRef(s_yuzu_input_device_interface); + + // UnInitialize applets + SoftwareKeyboard::CleanupJNI(env); + + AndroidMultiplayer::NetworkShutdown(); + } + +#ifdef __cplusplus + } #endif } // namespace Common::Android diff --git a/src/common/android/id_cache.h b/src/common/android/id_cache.h index cbfbf36be8..c56ffcf5c6 100644 --- a/src/common/android/id_cache.h +++ b/src/common/android/id_cache.h @@ -39,6 +39,9 @@ jclass GetDiskCacheLoadCallbackStageClass(); jclass GetGameDirClass(); jmethodID GetGameDirConstructor(); jmethodID GetDiskCacheLoadProgress(); +jmethodID GetCopyToStorage(); +jmethodID GetFileExists(); +jmethodID GetFileExtension(); jmethodID GetExitEmulationActivity(); jmethodID GetOnEmulationStarted(); diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt index 94d8cc4c3b..70e142bb0c 100644 --- a/src/frontend_common/CMakeLists.txt +++ b/src/frontend_common/CMakeLists.txt @@ -5,6 +5,8 @@ add_library(frontend_common STATIC config.cpp config.h content_manager.h + firmware_manager.h + firmware_manager.cpp ) create_target_directory_groups(frontend_common) diff --git a/src/frontend_common/firmware_manager.cpp b/src/frontend_common/firmware_manager.cpp new file mode 100644 index 0000000000..f1022579be --- /dev/null +++ b/src/frontend_common/firmware_manager.cpp @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "firmware_manager.h" +#include +#include + +#include "common/fs/fs.h" +#include "common/fs/path_util.h" + +#include "common/logging/backend.h" + +#include "core/crypto/key_manager.h" +#include "frontend_common/content_manager.h" + +#ifdef ANDROID +#include +#include +#include +#endif + +FirmwareManager::KeyInstallResult +FirmwareManager::InstallKeys(std::string location, std::string extension) { + LOG_INFO(Frontend, "Installing key files from {}", location); + + const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir); + +#ifdef ANDROID + JNIEnv *env = Common::Android::GetEnvForThread(); + + jstring jsrc = Common::Android::ToJString(env, location); + + jclass native = Common::Android::GetNativeLibraryClass(); + jmethodID getExtension = Common::Android::GetFileExtension(); + + jstring jext = static_cast(env->CallStaticObjectMethod( + native, + getExtension, + jsrc + )); + + std::string ext = Common::Android::GetJString(env, jext); + + if (ext != extension) { + return ErrorWrongFilename; + } + + jmethodID copyToStorage = Common::Android::GetCopyToStorage(); + jstring jdest = Common::Android::ToJString(env, keys_dir.string()); + + jboolean copyResult = env->CallStaticBooleanMethod( + native, + copyToStorage, + jsrc, + jdest + ); + + if (!copyResult) { + return ErrorFailedCopy; + } +#else + if (!location.ends_with(extension)) { + return ErrorWrongFilename; + } + + bool prod_keys_found = false; + + const std::filesystem::path prod_key_path = location; + const std::filesystem::path key_source_path = prod_key_path.parent_path(); + + if (!Common::FS::IsDir(key_source_path)) { + return InvalidDir; + } + + std::vector source_key_files; + + if (Common::FS::Exists(prod_key_path)) { + prod_keys_found = true; + source_key_files.emplace_back(prod_key_path); + } + + if (Common::FS::Exists(key_source_path / "title.keys")) { + source_key_files.emplace_back(key_source_path / "title.keys"); + } + + if (Common::FS::Exists(key_source_path / "key_retail.bin")) { + source_key_files.emplace_back(key_source_path / "key_retail.bin"); + } + + if (source_key_files.empty() || !prod_keys_found) { + return ErrorWrongFilename; + } + + for (const auto &key_file : source_key_files) { + std::filesystem::path destination_key_file = keys_dir / key_file.filename(); + if (!std::filesystem::copy_file(key_file, + destination_key_file, + std::filesystem::copy_options::overwrite_existing)) { + LOG_ERROR(Frontend, + "Failed to copy file {} to {}", + key_file.string(), + destination_key_file.string()); + return ErrorFailedCopy; + } + } +#endif + + // Reinitialize the key manager + Core::Crypto::KeyManager::Instance().ReloadKeys(); + + if (ContentManager::AreKeysPresent()) { + return Success; + } + + // Let the frontend handle everything else + return ErrorFailedInit; +} + +FirmwareManager::FirmwareCheckResult FirmwareManager::VerifyFirmware(Core::System &system) { + if (!CheckFirmwarePresence(system)) { + return ErrorFirmwareMissing; + } else { + const auto pair = GetFirmwareVersion(system); + const auto firmware_data = pair.first; + const auto result = pair.second; + + if (result.IsError()) { + LOG_INFO(Frontend, "Unable to read firmware"); + return ErrorFirmwareCorrupted; + } + + // TODO: update this whenever newer firmware is properly supported + if (firmware_data.major > 19) { + return ErrorFirmwareTooNew; + } + } + + return FirmwareGood; +} diff --git a/src/frontend_common/firmware_manager.h b/src/frontend_common/firmware_manager.h new file mode 100644 index 0000000000..20f3b41478 --- /dev/null +++ b/src/frontend_common/firmware_manager.h @@ -0,0 +1,148 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef FIRMWARE_MANAGER_H +#define FIRMWARE_MANAGER_H + +#include "common/common_types.h" +#include "core/core.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/service/filesystem/filesystem.h" +#include +#include +#include +#include + +#include "core/hle/service/set/settings_types.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/result.h" + +namespace FirmwareManager { + +static constexpr std::array KEY_INSTALL_RESULT_STRINGS = { + "Decryption Keys were successfully installed", + "Unable to read key directory, aborting", + "One or more keys failed to copy.", + "Verify your keys file has a .keys extension and try again.", + "Decryption Keys failed to initialize. Check that your dumping tools are up to date and " + "re-dump keys.", +}; + +static constexpr std::array FIRMWARE_REQUIRED_GAMES = { + 0x0100152000022000ULL, // MK8DX +}; + +enum KeyInstallResult { + Success, + InvalidDir, + ErrorFailedCopy, + ErrorWrongFilename, + ErrorFailedInit, +}; + +/** + * @brief Installs any arbitrary set of keys for the emulator. + * @param location Where the keys are located. + * @param expected_extension What extension the file should have. + * @return A result code for the operation. + */ +KeyInstallResult InstallKeys(std::string location, std::string expected_extension); + +/** + * \brief Get a string representation of a result from InstallKeys. + * \param result The result code. + * \return A string representation of the passed result code. + */ +inline constexpr const char *GetKeyInstallResultString(KeyInstallResult result) +{ + return KEY_INSTALL_RESULT_STRINGS.at(static_cast(result)); +} + +/** + * \brief Check if the specified program requires firmware to run properly. + * It is the responsibility of the frontend to properly expose this to the user. + * \param program_id The program ID to check. + * \return Whether or not the program requires firmware to run properly. + */ +inline constexpr bool GameRequiresFirmware(u64 program_id) +{ + return std::find(FIRMWARE_REQUIRED_GAMES.begin(), FIRMWARE_REQUIRED_GAMES.end(), program_id) + != FIRMWARE_REQUIRED_GAMES.end(); +} + + +enum FirmwareCheckResult { + FirmwareGood, + ErrorFirmwareMissing, + ErrorFirmwareCorrupted, + ErrorFirmwareTooNew, +}; + +static constexpr std::array FIRMWARE_CHECK_STRINGS = { + "", + "Firmware missing. Firmware is required to run certain games and use the Home Menu. " + "Eden only works with firmware 19.0.1 and earlier.", + "Firmware reported as present, but was unable to be read. Check for decryption keys and " + "redump firmware if necessary.", + "Firmware is too new or could not be read. Eden only works with firmware 19.0.1 and earlier.", +}; + +/** + * \brief Checks for installed firmware within the system. + * \param system The system to check for firmware. + * \return Whether or not the system has installed firmware. + */ +inline bool CheckFirmwarePresence(Core::System &system) +{ + constexpr u64 MiiEditId = static_cast(Service::AM::AppletProgramId::MiiEdit); + + auto bis_system = system.GetFileSystemController().GetSystemNANDContents(); + if (!bis_system) { + return false; + } + + auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program); + + if (!mii_applet_nca) { + return false; + } + + return true; +} + +/** + * \brief Verifies if firmware is properly installed and is in the correct version range. + * \param system The system to check firmware on. + * \return A result code defining the status of the system's firmware. + */ +FirmwareCheckResult VerifyFirmware(Core::System &system); + +/** + * \brief Get a string representation of a result from CheckFirmwareVersion. + * \param result The result code. + * \return A string representation of the passed result code. + */ +inline constexpr const char *GetFirmwareCheckString(FirmwareCheckResult result) +{ + return FIRMWARE_CHECK_STRINGS.at(static_cast(result)); +} + +/** + * @brief Get the currently installed firmware version. + * @param system The system to check firmware on. + * @return A pair of the firmware version format and result code. + */ +inline std::pair GetFirmwareVersion(Core::System &system) +{ + Service::Set::FirmwareVersionFormat firmware_data{}; + const auto result + = Service::Set::GetFirmwareVersionImpl(firmware_data, + system, + Service::Set::GetFirmwareVersionType::Version2); + + return {firmware_data, result}; +} +} + +#endif // FIRMWARE_MANAGER_H diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4ff59291e5..081f08cf52 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -10,6 +10,7 @@ #include "core/hle/service/am/applet_manager.h" #include "core/loader/nca.h" #include "core/tools/renderdoc.h" +#include "frontend_common/firmware_manager.h" #ifdef __APPLE__ #include // for chdir @@ -211,7 +212,7 @@ enum class CalloutFlag : uint32_t { * Some games perform worse or straight-up don't work with updates, * so this tracks which games are bad in this regard. */ -static const QList bad_update_games{ +constexpr std::array bad_update_games{ 0x0100F2C0115B6000 // Tears of the Kingdom }; @@ -1889,46 +1890,61 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa QSettings settings; QStringList currentIgnored = settings.value("ignoredBadUpdates", {}).toStringList(); - for (const u64 id : bad_update_games) { - const bool ignored = currentIgnored.contains(QString::number(id)); + if (std::find(bad_update_games.begin(), bad_update_games.end(), params.program_id) != bad_update_games.end() + && !currentIgnored.contains(QString::number(params.program_id))) { + QMessageBox *msg = new QMessageBox(this); + msg->setWindowTitle(tr("Game Updates Warning")); + msg->setIcon(QMessageBox::Warning); + msg->setText(tr("The game you are trying to launch is known to have performance or booting " + "issues when updates are applied. Please try increasing the memory layout to " + "6GB or 8GB if any issues occur.

Press \"OK\" to continue launching, or " + "\"Cancel\" to cancel the launch.")); - if (params.program_id == id && !ignored) { - QMessageBox *msg = new QMessageBox(this); - msg->setWindowTitle(tr("Game Updates Warning")); - msg->setIcon(QMessageBox::Warning); - msg->setText(tr("The game you are trying to launch is known to have performance or booting " - "issues when updates are applied. Please try increasing the memory layout to " - "6GB or 8GB if any issues occur.

Press \"OK\" to continue launching, or " - "\"Cancel\" to cancel the launch.")); + msg->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - // TODO: TMP: Recommends more memory for TotK. + QCheckBox *dontShowAgain = new QCheckBox(msg); + dontShowAgain->setText(tr("Don't show again for this game")); + msg->setCheckBox(dontShowAgain); - msg->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + int result = msg->exec(); - QCheckBox *dontShowAgain = new QCheckBox(msg); - dontShowAgain->setText(tr("Don't show again for this game")); - msg->setCheckBox(dontShowAgain); + // wtf + QMessageBox::ButtonRole role = msg->buttonRole(msg->button((QMessageBox::StandardButton) result)); - int result = msg->exec(); - - // wtf - QMessageBox::ButtonRole role = msg->buttonRole(msg->button((QMessageBox::StandardButton) result)); - - switch (role) { - - case QMessageBox::RejectRole: - return false; - - case QMessageBox::AcceptRole: - default: - if (dontShowAgain->isChecked()) { - currentIgnored << QString::number(params.program_id); - - settings.setValue("ignoredBadUpdates", currentIgnored); - settings.sync(); - } - break; + switch (role) { + case QMessageBox::RejectRole: + return false; + case QMessageBox::AcceptRole: + default: + if (dontShowAgain->isChecked()) { + currentIgnored << QString::number(params.program_id); + settings.setValue("ignoredBadUpdates", currentIgnored); + settings.sync(); } + break; + } + } + + if (FirmwareManager::GameRequiresFirmware(params.program_id) && !FirmwareManager::CheckFirmwarePresence(*system)) { + QMessageBox *msg = new QMessageBox(this); + msg->setWindowTitle(tr("Game Requires Firmware")); + msg->setIcon(QMessageBox::Warning); + msg->setText(tr("The game you are trying to launch requires firmware to boot or to get past the " + "opening menu. Please " + "dump and install firmware, or press \"OK\" to launch anyways.")); + + msg->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + + int exec_result = msg->exec(); + + QMessageBox::ButtonRole role = msg->buttonRole(msg->button((QMessageBox::StandardButton) exec_result)); + + switch (role) { + case QMessageBox::RejectRole: + return false; + case QMessageBox::AcceptRole: + default: + break; } } @@ -1945,7 +1961,7 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa UISettings::values.callout_flags = UISettings::values.callout_flags.GetValue() | static_cast(CalloutFlag::DRDDeprecation); QMessageBox::warning( - this, tr("Warning Outdated Game Format"), + this, tr("Warning: Outdated Game Format"), tr("You are using the deconstructed ROM directory format for this game, which is an " "outdated format that has been superseded by others such as NCA, NAX, XCI, or " "NSP. Deconstructed ROM directories lack icons, metadata, and update " @@ -1968,7 +1984,7 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa "This is usually caused by outdated GPU drivers, including integrated ones. " "Please see the log for more details. " "For more information on accessing the log, please see the following page: " - "" + "" "How to Upload the Log File. ")); break; default: @@ -4322,8 +4338,8 @@ void GMainWindow::OnInstallFirmware() { progress.close(); QMessageBox::warning( this, tr("Firmware install failed"), - tr("Firmware installation cancelled, firmware may be in bad state, " - "restart eden or re-install firmware.")); + tr("Firmware installation cancelled, firmware may be in a bad state or corrupted. " + "Restart Eden or re-install firmware.")); return; } } @@ -4368,72 +4384,27 @@ void GMainWindow::OnInstallDecryptionKeys() { } const QString key_source_location = QFileDialog::getOpenFileName( - this, tr("Select Dumped Keys Location"), {}, QStringLiteral("prod.keys (prod.keys)"), {}, + this, tr("Select Dumped Keys Location"), {}, QStringLiteral("Decryption Keys (*.keys)"), {}, QFileDialog::ReadOnly); if (key_source_location.isEmpty()) { return; } - // Verify that it contains prod.keys, title.keys and optionally, key_retail.bin - LOG_INFO(Frontend, "Installing key files from {}", key_source_location.toStdString()); + FirmwareManager::KeyInstallResult result = FirmwareManager::InstallKeys(key_source_location.toStdString(), "keys"); - const std::filesystem::path prod_key_path = key_source_location.toStdString(); - const std::filesystem::path key_source_path = prod_key_path.parent_path(); - if (!Common::FS::IsDir(key_source_path)) { - return; - } - - bool prod_keys_found = false; - std::vector source_key_files; - - if (Common::FS::Exists(prod_key_path)) { - prod_keys_found = true; - source_key_files.emplace_back(prod_key_path); - } - - if (Common::FS::Exists(key_source_path / "title.keys")) { - source_key_files.emplace_back(key_source_path / "title.keys"); - } - - if (Common::FS::Exists(key_source_path / "key_retail.bin")) { - source_key_files.emplace_back(key_source_path / "key_retail.bin"); - } - - // There should be at least prod.keys. - if (source_key_files.empty() || !prod_keys_found) { - QMessageBox::warning(this, tr("Decryption Keys install failed"), - tr("prod.keys is a required decryption key file.")); - return; - } - - const auto yuzu_keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir); - for (auto key_file : source_key_files) { - std::filesystem::path destination_key_file = yuzu_keys_dir / key_file.filename(); - if (!std::filesystem::copy_file(key_file, destination_key_file, - std::filesystem::copy_options::overwrite_existing)) { - LOG_ERROR(Frontend, "Failed to copy file {} to {}", key_file.string(), - destination_key_file.string()); - QMessageBox::critical(this, tr("Decryption Keys install failed"), - tr("One or more keys failed to copy.")); - return; - } - } - - // Reinitialize the key manager, re-read the vfs (for update/dlc files), - // and re-populate the game list in the UI if the user has already added - // game folders. - Core::Crypto::KeyManager::Instance().ReloadKeys(); system->GetFileSystemController().CreateFactories(*vfs); game_list->PopulateAsync(UISettings::values.game_dirs); - if (ContentManager::AreKeysPresent()) { + switch (result) { + case FirmwareManager::KeyInstallResult::Success: QMessageBox::information(this, tr("Decryption Keys install succeeded"), tr("Decryption Keys were successfully installed")); - } else { + break; + default: QMessageBox::critical( this, tr("Decryption Keys install failed"), - tr("Decryption Keys failed to initialize. Check that your dumping tools are " - "up to date and re-dump keys.")); + tr(FirmwareManager::GetKeyInstallResultString(result))); + break; } OnCheckFirmwareDecryption(); @@ -5103,52 +5074,27 @@ void GMainWindow::OnCheckFirmwareDecryption() { void GMainWindow::OnCheckFirmware() { - if (!CheckFirmwarePresence()) { + auto result = FirmwareManager::VerifyFirmware(*system.get()); + + switch (result) { + case FirmwareManager::FirmwareGood: + break; + default: QMessageBox::warning( - this, tr("Firmware Missing"), - tr("Firmware missing. Firmware is required to run certain games and use the Home Menu.\n" - "Eden only works with firmware 19.0.1 and earlier.")); - } else { - Service::Set::FirmwareVersionFormat firmware_data{}; - const auto result = Service::Set::GetFirmwareVersionImpl( - firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2); - - if (result.IsError()) { - LOG_INFO(Frontend, "Unable to read firmware"); - QMessageBox::warning( - this, tr("Firmware Corrupted"), - tr("Firmware reported as present, but was unable to be read. Check for decryption keys and redump firmware if necessary.")); - return; - } - - if (firmware_data.major > 19) { - QMessageBox::warning( - this, tr("Firmware Too New"), - tr("Firmware is too new. Eden only works with firmware 19.0.1 and earlier.")); - } + this, tr("Firmware Read Error"), + tr(FirmwareManager::GetFirmwareCheckString(result))); + break; } } bool GMainWindow::CheckFirmwarePresence() { - constexpr u64 MiiEditId = static_cast(Service::AM::AppletProgramId::MiiEdit); - - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); - if (!bis_system) { - return false; - } - - auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program); - if (!mii_applet_nca) { - return false; - } - - return true; + return FirmwareManager::CheckFirmwarePresence(*system.get()); } void GMainWindow::SetFirmwareVersion() { - Service::Set::FirmwareVersionFormat firmware_data{}; - const auto result = Service::Set::GetFirmwareVersionImpl( - firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2); + const auto pair = FirmwareManager::GetFirmwareVersion(*system.get()); + const auto firmware_data = pair.first; + const auto result = pair.second; if (result.IsError() || !CheckFirmwarePresence()) { LOG_INFO(Frontend, "Installed firmware: No firmware available"); From 5091759a47182fd85e12ca58abcb57d6f4ca1ea1 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sun, 13 Jul 2025 03:40:48 +0200 Subject: [PATCH 05/41] [vk, opengl] defer checks to topmost call (avoid unnecessary call) (#40) Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/40 Co-authored-by: lizzie Co-committed-by: lizzie --- src/core/memory.cpp | 69 +++++++++++-------- src/video_core/gpu.h | 5 +- .../renderer_opengl/gl_rasterizer.cpp | 46 ++++++------- .../renderer_vulkan/vk_rasterizer.cpp | 7 +- 4 files changed, 66 insertions(+), 61 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index b033858bf8..34539cc650 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-FileCopyrightText: 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -868,35 +871,35 @@ struct Memory::Impl { [[nodiscard]] u8* GetPointerImpl(u64 vaddr, auto on_unmapped, auto on_rasterizer) const { // AARCH64 masks the upper 16 bit of all memory accesses vaddr = vaddr & 0xffffffffffffULL; - if (!AddressSpaceContains(*current_page_table, vaddr, 1)) [[unlikely]] { on_unmapped(); return nullptr; + } else { + // Avoid adding any extra logic to this fast-path block + const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw(); + if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { + return reinterpret_cast(pointer + vaddr); + } else { + switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { + case Common::PageType::Unmapped: + on_unmapped(); + return nullptr; + case Common::PageType::Memory: + ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr); + return nullptr; + case Common::PageType::DebugMemory: + return GetPointerFromDebugMemory(vaddr); + case Common::PageType::RasterizerCachedMemory: { + u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; + on_rasterizer(); + return host_ptr; + } + default: + UNREACHABLE(); + } + return nullptr; + } } - - // Avoid adding any extra logic to this fast-path block - const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw(); - if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { - return reinterpret_cast(pointer + vaddr); - } - switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { - case Common::PageType::Unmapped: - on_unmapped(); - return nullptr; - case Common::PageType::Memory: - ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr); - return nullptr; - case Common::PageType::DebugMemory: - return GetPointerFromDebugMemory(vaddr); - case Common::PageType::RasterizerCachedMemory: { - u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; - on_rasterizer(); - return host_ptr; - } - default: - UNREACHABLE(); - } - return nullptr; } [[nodiscard]] u8* GetPointer(const Common::ProcessAddress vaddr) const { @@ -1149,13 +1152,19 @@ struct Memory::Impl { gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) { auto& current_area = rasterizer_write_areas[core]; PAddr subaddress = address >> YUZU_PAGEBITS; - bool do_collection = current_area.last_address == subaddress; - if (!do_collection) [[unlikely]] { - do_collection = system.GPU().OnCPUWrite(address, size); - if (!do_collection) { + // Performance note: + // It may not be a good idea to assume accesses are within the same subaddress (i.e same page) + // It is often the case the games like to access wildly different addresses. Hence why I propose + // we should let the compiler just do it's thing... + if (current_area.last_address != subaddress) { + // Short circuit the need to check for address/size + auto const do_collection = (address != 0 && size != 0) + && system.GPU().OnCPUWrite(address, size); + if (do_collection) { + current_area.last_address = subaddress; + } else { return; } - current_area.last_address = subaddress; } gpu_dirty_managers[core].Collect(address, size); }); diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 8a06adad79..538c4da85a 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -244,7 +247,7 @@ public: void InvalidateRegion(DAddr addr, u64 size); /// Notify rasterizer that CPU is trying to write this area. It returns true if the area is - /// sensible, false otherwise + /// sensible, false otherwise, addr and size must be a valid combination bool OnCPUWrite(DAddr addr, u64 size); /// Notify rasterizer that any caches of the specified region should be flushed and invalidated diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 19f586518f..4fb193293b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -565,22 +568,17 @@ void RasterizerOpenGL::InvalidateRegion(DAddr addr, u64 size, VideoCommon::Cache bool RasterizerOpenGL::OnCPUWrite(DAddr addr, u64 size) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); - if (addr == 0 || size == 0) { - return false; - } - + DEBUG_ASSERT(addr != 0 || size != 0); { std::scoped_lock lock{buffer_cache.mutex}; if (buffer_cache.OnCPUWrite(addr, size)) { return true; } } - { std::scoped_lock lock{texture_cache.mutex}; texture_cache.WriteMemory(addr, size); } - shader_cache.InvalidateRegion(addr, size); return false; } @@ -1204,24 +1202,24 @@ void RasterizerOpenGL::SyncLogicOpState() { } flags[Dirty::LogicOp] = false; - auto& regs = maxwell3d->regs; - - if (device.IsAmd()) { - using namespace Tegra::Engines; - struct In { - const Maxwell3D::Regs::VertexAttribute::Type d; - In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {} - bool operator()(Maxwell3D::Regs::VertexAttribute n) const { - return n.type == d; - } - }; - - bool has_float = - std::any_of(regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), - In(Maxwell3D::Regs::VertexAttribute::Type::Float)); - regs.logic_op.enable = static_cast(!has_float); - } - + auto& regs = maxwell3d->regs; + + if (device.IsAmd()) { + using namespace Tegra::Engines; + struct In { + const Maxwell3D::Regs::VertexAttribute::Type d; + In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {} + bool operator()(Maxwell3D::Regs::VertexAttribute n) const { + return n.type == d; + } + }; + + bool has_float = + std::any_of(regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), + In(Maxwell3D::Regs::VertexAttribute::Type::Float)); + regs.logic_op.enable = static_cast(!has_float); + } + if (regs.logic_op.enable) { glEnable(GL_COLOR_LOGIC_OP); glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.op)); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 66f4668026..a2492f1d74 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -636,22 +636,17 @@ void RasterizerVulkan::InnerInvalidation(std::span Date: Sun, 13 Jul 2025 01:14:09 -0400 Subject: [PATCH 06/41] [android] Fix key install and revert to old icon_bg `312b3d4743ad734ac3234385f1edaa94d5b69440` Signed-off-by: crueter --- .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 2 + .../app/src/main/res/drawable/ic_icon_bg.xml | 1675 ++++++++--------- src/frontend_common/firmware_manager.cpp | 2 +- 3 files changed, 740 insertions(+), 939 deletions(-) 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 f8ba35dbd5..6d9a2002f5 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 @@ -349,6 +349,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val resultCode: Int = NativeLibrary.installKeys(result.toString(), extension); if (resultCode == 0) { + // TODO(crueter): It may be worth it to switch some of these Toasts to snackbars, + // since most of it is foreground-only anyways. Toast.makeText( applicationContext, R.string.keys_install_success, Toast.LENGTH_SHORT ).show() diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg.xml b/src/android/app/src/main/res/drawable/ic_icon_bg.xml index b409a61ea2..df62dde92e 100644 --- a/src/android/app/src/main/res/drawable/ic_icon_bg.xml +++ b/src/android/app/src/main/res/drawable/ic_icon_bg.xml @@ -1,952 +1,751 @@ - - - - - - - - - + + + + android:pathData="M0,0h512v512h-512z" + android:fillColor="#ffffff"/> + android:pathData="M0,0h512v512h-512z" + android:fillColor="#1C1C1C"/> + android:pathData="M208.16,7H159.88C155.54,7 152,10.54 152,14.88V92.16C152,96.54 155.54,100.04 159.88,100.04H208.12C212.5,100.04 216,96.5 216,92.16V14.88C216.04,10.54 212.5,7 208.16,7Z" + android:strokeAlpha="0.7" + android:fillColor="#282828" + android:fillAlpha="0.7"/> + android:pathData="M208.8,89.73H158.44C156.65,89.73 155.18,88.26 155.18,86.47V17.02C155.18,15.23 156.65,13.76 158.44,13.76H208.84C210.63,13.76 212.1,15.23 212.1,17.02V86.51C212.06,88.26 210.59,89.73 208.8,89.73Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> + android:pathData="M194.16,14.16H173.08V12.93C173.08,12.29 173.6,11.77 174.24,11.77H193.01C193.65,11.77 194.16,12.29 194.16,12.93V14.16Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:pathData="M183.86,97.29L177.93,92.92H189.79L183.86,97.29Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> - - + android:pathData="M424.16,7H375.88C371.54,7 368,10.54 368,14.88V92.16C368,96.54 371.54,100.04 375.88,100.04H424.12C428.5,100.04 432,96.5 432,92.16V14.88C432.04,10.54 428.5,7 424.16,7Z" + android:strokeAlpha="0.7" + android:fillColor="#282828" + android:fillAlpha="0.7"/> - - + android:pathData="M424.8,89.73H374.44C372.65,89.73 371.18,88.26 371.18,86.47V17.02C371.18,15.23 372.65,13.76 374.44,13.76H424.84C426.63,13.76 428.1,15.23 428.1,17.02V86.51C428.06,88.26 426.59,89.73 424.8,89.73Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:pathData="M410.16,14.16H389.08V12.93C389.08,12.29 389.6,11.77 390.23,11.77H409.01C409.65,11.77 410.16,12.29 410.16,12.93V14.16Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> + android:pathData="M399.86,97.29L393.93,92.92H405.79L399.86,97.29Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> - - - - + android:pathData="M352.16,109H303.88C299.54,109 296,112.54 296,116.88V194.16C296,198.54 299.54,202.04 303.88,202.04H352.12C356.5,202.04 360,198.5 360,194.16V116.88C360.04,112.54 356.5,109 352.16,109Z" + android:strokeAlpha="0.7" + android:fillColor="#282828" + android:fillAlpha="0.7"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/src/frontend_common/firmware_manager.cpp b/src/frontend_common/firmware_manager.cpp index f1022579be..a180693996 100644 --- a/src/frontend_common/firmware_manager.cpp +++ b/src/frontend_common/firmware_manager.cpp @@ -46,7 +46,7 @@ FirmwareManager::InstallKeys(std::string location, std::string extension) { } jmethodID copyToStorage = Common::Android::GetCopyToStorage(); - jstring jdest = Common::Android::ToJString(env, keys_dir.string()); + jstring jdest = Common::Android::ToJString(env, keys_dir.string() + "/"); jboolean copyResult = env->CallStaticBooleanMethod( native, From c47f6615d3bda7b3c7d305701ec8af38333aeebf Mon Sep 17 00:00:00 2001 From: Ghost <> Date: Sun, 13 Jul 2025 19:27:39 +0200 Subject: [PATCH 07/41] [vk, opengl] Prevent GPU draw call if CBUF binding fails (cbuf0 error handling) (#2) Add defensive checks to cancel draw calls early if any graphics storage buffer (CBUF) fails to bind properly. - Modified BindGraphicsStorageBuffer to return false on invalid buffer ID., - ConfigureImpl (both OpenGL and Vulkan) now propagates binding failure., - Pipeline::Configure returns false if CBUF binding fails., - PrepareDraw cancels rendering if pipeline configuration fails., This avoids undefined GPU behavior, draw corruption, or crashes caused by uninitialized or invalid constant buffer (CBUF0) access, particularly in games with faulty or missing shader bindings. Eden Collaborator: Authored-by: CamilleLaVey Signed-off-by: Bix Co-authored-by: Bix <114880614+Bixbr@users.noreply.github.com> Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2 Co-authored-by: Ghost <> Co-committed-by: Ghost <> --- src/video_core/buffer_cache/buffer_cache.h | 3 ++- .../buffer_cache/buffer_cache_base.h | 2 +- .../renderer_opengl/gl_graphics_pipeline.cpp | 9 +++++++-- .../renderer_opengl/gl_graphics_pipeline.h | 13 ++++++++----- .../renderer_opengl/gl_rasterizer.cpp | 3 ++- .../renderer_vulkan/vk_graphics_pipeline.cpp | 19 ++++++++++++++----- .../renderer_vulkan/vk_graphics_pipeline.h | 13 ++++++++----- .../renderer_vulkan/vk_rasterizer.cpp | 3 ++- 8 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 0ce2abc627..a6e87a3583 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -416,7 +416,7 @@ void BufferCache

::UnbindGraphicsStorageBuffers(size_t stage) { } template -void BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, +bool BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, bool is_written) { channel_state->enabled_storage_buffers[stage] |= 1U << ssbo_index; channel_state->written_storage_buffers[stage] |= (is_written ? 1U : 0U) << ssbo_index; @@ -425,6 +425,7 @@ void BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset; channel_state->storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr, cbuf_index, is_written); + return (channel_state->storage_buffers[stage][ssbo_index].buffer_id != NULL_BUFFER_ID); } template diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index d45f595ea8..1c7320dcc1 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -235,7 +235,7 @@ public: void UnbindGraphicsStorageBuffers(size_t stage); - void BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, + bool BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, bool is_written); void UnbindGraphicsTextureBuffers(size_t stage); diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index af0a453ee7..b4417de703 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -128,7 +131,7 @@ bool Passes(const std::array& stage_infos, u32 enabled_mask) { return true; } -using ConfigureFuncPtr = void (*)(GraphicsPipeline*, bool); +using ConfigureFuncPtr = bool (*)(GraphicsPipeline*, bool); template ConfigureFuncPtr FindSpec(const std::array& stage_infos, u32 enabled_mask) { @@ -275,7 +278,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c } template -void GraphicsPipeline::ConfigureImpl(bool is_indexed) { +bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { std::array views; std::array samplers; size_t views_index{}; @@ -556,6 +559,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { if (image_binding != 0) { glBindImageTextures(0, image_binding, images.data()); } + + return true; } void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 2f70c1ae9c..66ab677919 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -80,8 +83,8 @@ public: const std::array& infos, const GraphicsPipelineKey& key_, bool force_context_flush = false); - void Configure(bool is_indexed) { - configure_func(this, is_indexed); + bool Configure(bool is_indexed) { + return configure_func(this, is_indexed); } void ConfigureTransformFeedback() const { @@ -107,7 +110,7 @@ public: template static auto MakeConfigureSpecFunc() { return [](GraphicsPipeline* pipeline, bool is_indexed) { - pipeline->ConfigureImpl(is_indexed); + return pipeline->ConfigureImpl(is_indexed); }; } @@ -118,7 +121,7 @@ public: private: template - void ConfigureImpl(bool is_indexed); + bool ConfigureImpl(bool is_indexed); void ConfigureTransformFeedbackImpl() const; @@ -134,7 +137,7 @@ private: StateTracker& state_tracker; const GraphicsPipelineKey key; - void (*configure_func)(GraphicsPipeline*, bool){}; + bool (*configure_func)(GraphicsPipeline*, bool){}; std::array source_programs; std::array assembly_programs; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4fb193293b..131b7463e0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -250,7 +250,8 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) { program_manager.LocalMemoryWarmup(); } pipeline->SetEngine(maxwell3d, gpu_memory); - pipeline->Configure(is_indexed); + if (!pipeline->Configure(is_indexed)) + return; SyncState(); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index e7cec364b6..eb757d68f5 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -175,7 +178,7 @@ bool Passes(const std::array& modules, return true; } -using ConfigureFuncPtr = void (*)(GraphicsPipeline*, bool); +using ConfigureFuncPtr = bool (*)(GraphicsPipeline*, bool); template ConfigureFuncPtr FindSpec(const std::array& modules, @@ -302,7 +305,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) { } template -void GraphicsPipeline::ConfigureImpl(bool is_indexed) { +bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { std::array views; std::array samplers; size_t sampler_index{}; @@ -321,8 +324,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { size_t ssbo_index{}; for (const auto& desc : info.storage_buffers_descriptors) { ASSERT(desc.count == 1); - buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index, - desc.cbuf_offset, desc.is_written); + if (!buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index, + desc.cbuf_offset, desc.is_written)) + return false; ++ssbo_index; } } @@ -382,6 +386,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { add_image(desc, desc.is_written); } } + + return true; }}; if constexpr (Spec::enabled_stages[0]) { config_stage(0); @@ -396,7 +402,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { config_stage(3); } if constexpr (Spec::enabled_stages[4]) { - config_stage(4); + if (!config_stage(4)) + return false; } texture_cache.FillGraphicsImageViews(std::span(views.data(), view_index)); @@ -490,6 +497,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { texture_cache.UpdateRenderTargets(false); texture_cache.CheckFeedbackLoop(views); ConfigureDraw(rescaling, render_area); + + return true; } void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 99e56e9ad8..7e9dbb583a 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -86,8 +89,8 @@ public: void AddTransition(GraphicsPipeline* transition); - void Configure(bool is_indexed) { - configure_func(this, is_indexed); + bool Configure(bool is_indexed) { + return configure_func(this, is_indexed); } [[nodiscard]] GraphicsPipeline* Next(const GraphicsPipelineCacheKey& current_key) noexcept { @@ -105,7 +108,7 @@ public: template static auto MakeConfigureSpecFunc() { - return [](GraphicsPipeline* pl, bool is_indexed) { pl->ConfigureImpl(is_indexed); }; + return [](GraphicsPipeline* pl, bool is_indexed) { return pl->ConfigureImpl(is_indexed); }; } void SetEngine(Tegra::Engines::Maxwell3D* maxwell3d_, Tegra::MemoryManager* gpu_memory_) { @@ -115,7 +118,7 @@ public: private: template - void ConfigureImpl(bool is_indexed); + bool ConfigureImpl(bool is_indexed); void ConfigureDraw(const RescalingPushConstant& rescaling, const RenderAreaPushConstant& render_are); @@ -134,7 +137,7 @@ private: Scheduler& scheduler; GuestDescriptorQueue& guest_descriptor_queue; - void (*configure_func)(GraphicsPipeline*, bool){}; + bool (*configure_func)(GraphicsPipeline*, bool){}; std::vector transition_keys; std::vector transitions; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a2492f1d74..0243693049 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -226,7 +226,8 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; // update engine as channel may be different. pipeline->SetEngine(maxwell3d, gpu_memory); - pipeline->Configure(is_indexed); + if (!pipeline->Configure(is_indexed)) + return; UpdateDynamicStates(); From a0a208db578b8d77579573d437ad251029db1635 Mon Sep 17 00:00:00 2001 From: SDK Chan Date: Sun, 13 Jul 2025 20:58:04 +0200 Subject: [PATCH 08/41] [cmake] Fix misplaced comment (#54) A member of the community notified me that I misplaced a comment so, I corrected it. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/54 Co-authored-by: SDK Chan Co-committed-by: SDK Chan --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1efa94e45a..1b8f60089d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,10 +29,10 @@ elseif (TARGET SDL2::SDL2-shared) endif() # Set bundled sdl2/qt as dependent options. +# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON "ENABLE_SDL2;MSVC" OFF) if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") - # On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" OFF "ENABLE_SDL2;NOT MSVC" OFF) else() CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF) From fe4f5a3860fcb2941f2dabe1327c7bd2eb229ca6 Mon Sep 17 00:00:00 2001 From: Ghost <> Date: Sun, 13 Jul 2025 23:03:26 +0200 Subject: [PATCH 09/41] [dynarmic] lea over mov and other stuff (#24) Co-authored-by: Esther1024 Co-authored-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/24 Co-authored-by: Ghost <> Co-committed-by: Ghost <> --- .gitignore | 1 + externals/dynarmic/CMakeLists.txt | 6 +- .../dynarmic/src/dynarmic/CMakeLists.txt | 105 - .../dynarmic/src/dynarmic/backend/arm64/abi.h | 3 +- .../src/dynarmic/backend/riscv64/code_block.h | 13 +- .../src/dynarmic/backend/x64/a32_emit_x64.cpp | 47 +- .../src/dynarmic/backend/x64/a64_emit_x64.cpp | 7 +- .../backend/x64/a64_emit_x64_memory.cpp | 16 +- .../dynarmic/backend/x64/block_of_code.cpp | 9 +- .../src/dynarmic/backend/x64/emit_x64.cpp | 9 +- .../backend/x64/emit_x64_data_processing.cpp | 1 - .../backend/x64/emit_x64_floating_point.cpp | 80 +- .../dynarmic/backend/x64/emit_x64_memory.h | 7 +- .../x64/emit_x64_vector_floating_point.cpp | 39 +- .../x64/emit_x64_vector_saturation.cpp | 3 + .../backend/x64/exception_handler_windows.cpp | 2 +- .../dynarmic/frontend/A32/a32_ir_emitter.h | 12 +- .../src/dynarmic/frontend/A32/decoder/arm.h | 11 +- .../frontend/A32/translate/a32_translate.cpp | 48 + .../A32/translate/impl/a32_translate_impl.cpp | 4 + .../A32/translate/impl/a32_translate_impl.h | 2 +- .../A32/translate/impl/asimd_three_regs.cpp | 6 +- .../translate/impl/asimd_two_regs_misc.cpp | 30 +- .../translate/impl/asimd_two_regs_shift.cpp | 36 +- .../frontend/A32/translate/impl/common.h | 31 + .../frontend/A32/translate/impl/extension.cpp | 6 +- .../frontend/A32/translate/impl/saturated.cpp | 9 +- ...data_processing_plain_binary_immediate.cpp | 8 +- .../impl/thumb32_data_processing_register.cpp | 5 +- .../A32/translate/impl/thumb32_load_byte.cpp | 8 +- .../translate/impl/thumb32_load_halfword.cpp | 9 +- .../impl/thumb32_load_store_dual.cpp | 4 +- .../impl/thumb32_load_store_multiple.cpp | 4 +- .../A32/translate/impl/thumb32_load_word.cpp | 4 +- .../A32/translate/impl/thumb32_parallel.cpp | 8 +- .../dynarmic/frontend/A64/a64_ir_emitter.cpp | 254 -- .../dynarmic/frontend/A64/a64_ir_emitter.h | 313 +- .../src/dynarmic/frontend/A64/decoder/a64.h | 16 +- .../frontend/A64/translate/a64_translate.cpp | 61 + .../translate/impl/simd_scalar_pairwise.cpp | 20 +- .../impl/simd_scalar_shift_by_immediate.cpp | 58 +- .../translate/impl/simd_scalar_three_same.cpp | 10 +- .../impl/simd_scalar_two_register_misc.cpp | 52 +- .../impl/simd_scalar_x_indexed_element.cpp | 8 +- .../impl/simd_shift_by_immediate.cpp | 98 +- .../translate/impl/simd_three_different.cpp | 54 +- .../A64/translate/impl/simd_three_same.cpp | 140 +- .../translate/impl/simd_two_register_misc.cpp | 92 +- .../impl/simd_vector_x_indexed_element.cpp | 80 +- .../src/dynarmic/interface/A32/arch_version.h | 4 +- .../src/dynarmic/interface/A32/config.h | 85 +- .../src/dynarmic/interface/A64/config.h | 137 +- .../dynarmic/src/dynarmic/ir/ir_emitter.cpp | 2872 --------------- .../dynarmic/src/dynarmic/ir/ir_emitter.h | 3176 +++++++++++++++-- 54 files changed, 3874 insertions(+), 4249 deletions(-) create mode 100644 externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/common.h diff --git a/.gitignore b/.gitignore index 7484a368e6..9aaf549512 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ CMakeLists.txt.user* # Visual Studio CMake settings CMakeSettings.json +.cache/ # OSX global filetypes # Created by Finder or Spotlight in directories for various OS functionality (indexing, etc) diff --git a/externals/dynarmic/CMakeLists.txt b/externals/dynarmic/CMakeLists.txt index efb5edf165..3db8d8077b 100644 --- a/externals/dynarmic/CMakeLists.txt +++ b/externals/dynarmic/CMakeLists.txt @@ -98,8 +98,7 @@ else() -Wextra -Wcast-qual -pedantic - -Wno-missing-braces - -Wstack-usage=4096) + -Wno-missing-braces) if (ARCHITECTURE STREQUAL "x86_64") list(APPEND DYNARMIC_CXX_FLAGS -mtune=core2) @@ -123,12 +122,15 @@ else() # GCC knows that the variable is actually a Reg64. isMEM() will never return true for a # Reg64, but GCC doesn't know that. list(APPEND DYNARMIC_CXX_FLAGS -Wno-array-bounds) + list(APPEND DYNARMIC_CXX_FLAGS -Wstack-usage=4096) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang") # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. # And this in turns limits the size of a std::array. list(APPEND DYNARMIC_CXX_FLAGS -fbracket-depth=1024) + # Clang mistakenly blames CMake for using unused arguments during compilation + list(APPEND DYNARMIC_CXX_FLAGS -Wno-unused-command-line-argument) endif() endif() diff --git a/externals/dynarmic/src/dynarmic/CMakeLists.txt b/externals/dynarmic/src/dynarmic/CMakeLists.txt index 9227951fcc..a43c9eae10 100644 --- a/externals/dynarmic/src/dynarmic/CMakeLists.txt +++ b/externals/dynarmic/src/dynarmic/CMakeLists.txt @@ -125,52 +125,6 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS) frontend/A32/translate/a32_translate.h frontend/A32/translate/conditional_state.cpp frontend/A32/translate/conditional_state.h - frontend/A32/translate/impl/a32_branch.cpp - frontend/A32/translate/impl/a32_crc32.cpp - frontend/A32/translate/impl/a32_exception_generating.cpp - frontend/A32/translate/impl/a32_translate_impl.cpp - frontend/A32/translate/impl/a32_translate_impl.h - frontend/A32/translate/impl/asimd_load_store_structures.cpp - frontend/A32/translate/impl/asimd_misc.cpp - frontend/A32/translate/impl/asimd_one_reg_modified_immediate.cpp - frontend/A32/translate/impl/asimd_three_regs.cpp - frontend/A32/translate/impl/asimd_two_regs_misc.cpp - frontend/A32/translate/impl/asimd_two_regs_scalar.cpp - frontend/A32/translate/impl/asimd_two_regs_shift.cpp - frontend/A32/translate/impl/barrier.cpp - frontend/A32/translate/impl/coprocessor.cpp - frontend/A32/translate/impl/data_processing.cpp - frontend/A32/translate/impl/divide.cpp - frontend/A32/translate/impl/extension.cpp - frontend/A32/translate/impl/hint.cpp - frontend/A32/translate/impl/load_store.cpp - frontend/A32/translate/impl/misc.cpp - frontend/A32/translate/impl/multiply.cpp - frontend/A32/translate/impl/packing.cpp - frontend/A32/translate/impl/parallel.cpp - frontend/A32/translate/impl/reversal.cpp - frontend/A32/translate/impl/saturated.cpp - frontend/A32/translate/impl/status_register_access.cpp - frontend/A32/translate/impl/synchronization.cpp - frontend/A32/translate/impl/thumb16.cpp - frontend/A32/translate/impl/thumb32_branch.cpp - frontend/A32/translate/impl/thumb32_control.cpp - frontend/A32/translate/impl/thumb32_coprocessor.cpp - frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp - frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp - frontend/A32/translate/impl/thumb32_data_processing_register.cpp - frontend/A32/translate/impl/thumb32_data_processing_shifted_register.cpp - frontend/A32/translate/impl/thumb32_load_byte.cpp - frontend/A32/translate/impl/thumb32_load_halfword.cpp - frontend/A32/translate/impl/thumb32_load_store_dual.cpp - frontend/A32/translate/impl/thumb32_load_store_multiple.cpp - frontend/A32/translate/impl/thumb32_load_word.cpp - frontend/A32/translate/impl/thumb32_long_multiply.cpp - frontend/A32/translate/impl/thumb32_misc.cpp - frontend/A32/translate/impl/thumb32_multiply.cpp - frontend/A32/translate/impl/thumb32_parallel.cpp - frontend/A32/translate/impl/thumb32_store_single_data_item.cpp - frontend/A32/translate/impl/vfp.cpp frontend/A32/translate/translate_arm.cpp frontend/A32/translate/translate_thumb.cpp interface/A32/a32.h @@ -194,65 +148,6 @@ if ("A64" IN_LIST DYNARMIC_FRONTENDS) frontend/A64/decoder/a64.inc frontend/A64/translate/a64_translate.cpp frontend/A64/translate/a64_translate.h - frontend/A64/translate/impl/a64_branch.cpp - frontend/A64/translate/impl/a64_exception_generating.cpp - frontend/A64/translate/impl/data_processing_addsub.cpp - frontend/A64/translate/impl/data_processing_bitfield.cpp - frontend/A64/translate/impl/data_processing_conditional_compare.cpp - frontend/A64/translate/impl/data_processing_conditional_select.cpp - frontend/A64/translate/impl/data_processing_crc32.cpp - frontend/A64/translate/impl/data_processing_logical.cpp - frontend/A64/translate/impl/data_processing_multiply.cpp - frontend/A64/translate/impl/data_processing_pcrel.cpp - frontend/A64/translate/impl/data_processing_register.cpp - frontend/A64/translate/impl/data_processing_shift.cpp - frontend/A64/translate/impl/floating_point_compare.cpp - frontend/A64/translate/impl/floating_point_conditional_compare.cpp - frontend/A64/translate/impl/floating_point_conditional_select.cpp - frontend/A64/translate/impl/floating_point_conversion_fixed_point.cpp - frontend/A64/translate/impl/floating_point_conversion_integer.cpp - frontend/A64/translate/impl/floating_point_data_processing_one_register.cpp - frontend/A64/translate/impl/floating_point_data_processing_three_register.cpp - frontend/A64/translate/impl/floating_point_data_processing_two_register.cpp - frontend/A64/translate/impl/impl.cpp - frontend/A64/translate/impl/impl.h - frontend/A64/translate/impl/load_store_exclusive.cpp - frontend/A64/translate/impl/load_store_load_literal.cpp - frontend/A64/translate/impl/load_store_multiple_structures.cpp - frontend/A64/translate/impl/load_store_no_allocate_pair.cpp - frontend/A64/translate/impl/load_store_register_immediate.cpp - frontend/A64/translate/impl/load_store_register_pair.cpp - frontend/A64/translate/impl/load_store_register_register_offset.cpp - frontend/A64/translate/impl/load_store_register_unprivileged.cpp - frontend/A64/translate/impl/load_store_single_structure.cpp - frontend/A64/translate/impl/move_wide.cpp - frontend/A64/translate/impl/simd_across_lanes.cpp - frontend/A64/translate/impl/simd_aes.cpp - frontend/A64/translate/impl/simd_copy.cpp - frontend/A64/translate/impl/simd_crypto_four_register.cpp - frontend/A64/translate/impl/simd_crypto_three_register.cpp - frontend/A64/translate/impl/simd_extract.cpp - frontend/A64/translate/impl/simd_modified_immediate.cpp - frontend/A64/translate/impl/simd_permute.cpp - frontend/A64/translate/impl/simd_scalar_pairwise.cpp - frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp - frontend/A64/translate/impl/simd_scalar_three_same.cpp - frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp - frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp - frontend/A64/translate/impl/simd_sha.cpp - frontend/A64/translate/impl/simd_sha512.cpp - frontend/A64/translate/impl/simd_shift_by_immediate.cpp - frontend/A64/translate/impl/simd_table_lookup.cpp - frontend/A64/translate/impl/simd_three_different.cpp - frontend/A64/translate/impl/simd_three_same.cpp - frontend/A64/translate/impl/simd_three_same_extra.cpp - frontend/A64/translate/impl/simd_two_register_misc.cpp - frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp - frontend/A64/translate/impl/sys_dc.cpp - frontend/A64/translate/impl/sys_ic.cpp - frontend/A64/translate/impl/system.cpp - frontend/A64/translate/impl/system_flag_format.cpp - frontend/A64/translate/impl/system_flag_manipulation.cpp interface/A64/a64.h interface/A64/config.h ir/opt/a64_callback_config_pass.cpp diff --git a/externals/dynarmic/src/dynarmic/backend/arm64/abi.h b/externals/dynarmic/src/dynarmic/backend/arm64/abi.h index 609b06cd22..d3d5de963a 100644 --- a/externals/dynarmic/src/dynarmic/backend/arm64/abi.h +++ b/externals/dynarmic/src/dynarmic/backend/arm64/abi.h @@ -59,13 +59,12 @@ constexpr RegisterList ToRegList(oaknut::Reg reg) { } if (reg.index() == 31) { - throw std::out_of_range("ZR not allowed in reg list"); + ASSERT_FALSE("ZR not allowed in reg list"); } if (reg.index() == -1) { return RegisterList{1} << 31; } - return RegisterList{1} << reg.index(); } diff --git a/externals/dynarmic/src/dynarmic/backend/riscv64/code_block.h b/externals/dynarmic/src/dynarmic/backend/riscv64/code_block.h index 6ac014a51a..8f98fdf01f 100644 --- a/externals/dynarmic/src/dynarmic/backend/riscv64/code_block.h +++ b/externals/dynarmic/src/dynarmic/backend/riscv64/code_block.h @@ -14,29 +14,26 @@ namespace Dynarmic::Backend::RV64 { class CodeBlock { public: - explicit CodeBlock(std::size_t size) - : memsize(size) { + explicit CodeBlock(std::size_t size) noexcept : memsize(size) { mem = (u8*)mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); - if (mem == nullptr) - throw std::bad_alloc{}; + ASSERT_FALSE("out of memory"); } - ~CodeBlock() { + ~CodeBlock() noexcept { if (mem == nullptr) return; - munmap(mem, memsize); } template - T ptr() const { + T ptr() const noexcept { static_assert(std::is_pointer_v || std::is_same_v || std::is_same_v); return reinterpret_cast(mem); } protected: - u8* mem; + u8* mem = nullptr; size_t memsize = 0; }; } // namespace Dynarmic::Backend::RV64 diff --git a/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp b/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp index ce38a52c73..740625c982 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -124,35 +124,36 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { EmitCondPrelude(ctx); - for (auto iter = block.begin(); iter != block.end(); ++iter) { - IR::Inst* inst = &*iter; - - // Call the relevant Emit* member function. - switch (inst->GetOpcode()) { -#define OPCODE(name, type, ...) \ - case IR::Opcode::name: \ - A32EmitX64::Emit##name(ctx, inst); \ - break; -#define A32OPC(name, type, ...) \ - case IR::Opcode::A32##name: \ - A32EmitX64::EmitA32##name(ctx, inst); \ - break; + auto const loop_all_inst = [this, &block, &ctx](auto const func) { + for (auto iter = block.begin(); iter != block.end(); ++iter) [[likely]] { + auto* inst = &*iter; + // Call the relevant Emit* member function. + switch (inst->GetOpcode()) { +#define OPCODE(name, type, ...) \ + case IR::Opcode::name: \ + A32EmitX64::Emit##name(ctx, inst); \ + break; +#define A32OPC(name, type, ...) \ + case IR::Opcode::A32##name: \ + A32EmitX64::EmitA32##name(ctx, inst);\ + break; #define A64OPC(...) #include "dynarmic/ir/opcodes.inc" #undef OPCODE #undef A32OPC #undef A64OPC - - default: - ASSERT_FALSE("Invalid opcode: {}", inst->GetOpcode()); - break; + default: [[unlikely]] ASSERT_FALSE("Invalid opcode: {}", inst->GetOpcode()); + } + reg_alloc.EndOfAllocScope(); + func(reg_alloc); } - - reg_alloc.EndOfAllocScope(); - - if (conf.very_verbose_debugging_output) { + }; + if (!conf.very_verbose_debugging_output) [[likely]] { + loop_all_inst([](auto&) { /*noop*/ }); + } else [[unlikely]] { + loop_all_inst([this](auto& reg_alloc) { EmitVerboseDebuggingOutput(reg_alloc); - } + }); } reg_alloc.AssertNoMoreUses(); @@ -229,7 +230,7 @@ void A32EmitX64::GenTerminalHandlers() { terminal_handler_pop_rsb_hint = code.getCurr(); calculate_location_descriptor(); code.mov(eax, dword[r15 + offsetof(A32JitState, rsb_ptr)]); - code.sub(eax, 1); + code.dec(eax); code.and_(eax, u32(A32JitState::RSBPtrMask)); code.mov(dword[r15 + offsetof(A32JitState, rsb_ptr)], eax); code.cmp(rbx, qword[r15 + offsetof(A32JitState, rsb_location_descriptors) + rax * sizeof(u64)]); diff --git a/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp b/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp index ad84e0ecc0..4d7bb0d7b1 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp @@ -198,18 +198,19 @@ void A64EmitX64::GenTerminalHandlers() { code.or_(rbx, rcx); }; - Xbyak::Label fast_dispatch_cache_miss, rsb_cache_miss; + Xbyak::Label fast_dispatch_cache_miss; + Xbyak::Label rsb_cache_miss; code.align(); terminal_handler_pop_rsb_hint = code.getCurr(); calculate_location_descriptor(); code.mov(eax, dword[r15 + offsetof(A64JitState, rsb_ptr)]); - code.sub(eax, 1); + code.dec(eax); code.and_(eax, u32(A64JitState::RSBPtrMask)); code.mov(dword[r15 + offsetof(A64JitState, rsb_ptr)], eax); code.cmp(rbx, qword[r15 + offsetof(A64JitState, rsb_location_descriptors) + rax * sizeof(u64)]); if (conf.HasOptimization(OptimizationFlag::FastDispatch)) { - code.jne(rsb_cache_miss); + code.jne(rsb_cache_miss, code.T_NEAR); } else { code.jne(code.GetReturnFromRunCodeAddress()); } diff --git a/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp b/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp index 450b16d000..fe7dfa011f 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp @@ -33,13 +33,13 @@ void A64EmitX64::GenMemory128Accessors() { #ifdef _WIN32 Devirtualize<&A64::UserCallbacks::MemoryRead128>(conf.callbacks).EmitCallWithReturnPointer(code, [&](Xbyak::Reg64 return_value_ptr, [[maybe_unused]] RegList args) { code.mov(code.ABI_PARAM3, code.ABI_PARAM2); - code.sub(rsp, 8 + 16 + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (8 + 16 + ABI_SHADOW_SPACE)]); code.lea(return_value_ptr, ptr[rsp + ABI_SHADOW_SPACE]); }); code.movups(xmm1, xword[code.ABI_RETURN]); code.add(rsp, 8 + 16 + ABI_SHADOW_SPACE); #else - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); Devirtualize<&A64::UserCallbacks::MemoryRead128>(conf.callbacks).EmitCall(code); if (code.HasHostFeature(HostFeature::SSE41)) { code.movq(xmm1, code.ABI_RETURN); @@ -57,13 +57,13 @@ void A64EmitX64::GenMemory128Accessors() { code.align(); memory_write_128 = code.getCurr(); #ifdef _WIN32 - code.sub(rsp, 8 + 16 + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (8 + 16 + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE]); code.movaps(xword[code.ABI_PARAM3], xmm1); Devirtualize<&A64::UserCallbacks::MemoryWrite128>(conf.callbacks).EmitCall(code); code.add(rsp, 8 + 16 + ABI_SHADOW_SPACE); #else - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); if (code.HasHostFeature(HostFeature::SSE41)) { code.movq(code.ABI_PARAM3, xmm1); code.pextrq(code.ABI_PARAM4, xmm1, 1); @@ -81,7 +81,7 @@ void A64EmitX64::GenMemory128Accessors() { code.align(); memory_exclusive_write_128 = code.getCurr(); #ifdef _WIN32 - code.sub(rsp, 8 + 32 + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (8 + 32 + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE]); code.lea(code.ABI_PARAM4, ptr[rsp + ABI_SHADOW_SPACE + 16]); code.movaps(xword[code.ABI_PARAM3], xmm1); @@ -89,7 +89,7 @@ void A64EmitX64::GenMemory128Accessors() { Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive128>(conf.callbacks).EmitCall(code); code.add(rsp, 8 + 32 + ABI_SHADOW_SPACE); #else - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); if (code.HasHostFeature(HostFeature::SSE41)) { code.movq(code.ABI_PARAM3, xmm1); code.pextrq(code.ABI_PARAM4, xmm1, 1); @@ -131,8 +131,8 @@ void A64EmitX64::GenFastmemFallbacks() { {64, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive64>(conf.callbacks)}, }}; - for (bool ordered : {false, true}) { - for (int vaddr_idx : idxes) { + for (auto const ordered : {false, true}) { + for (auto const vaddr_idx : idxes) { if (vaddr_idx == 4 || vaddr_idx == 15) { continue; } diff --git a/externals/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp b/externals/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp index 22d9868fc5..e5fb25573b 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp @@ -63,7 +63,8 @@ public: uint8_t* alloc(size_t size) override { void* p = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE); if (p == nullptr) { - throw Xbyak::Error(Xbyak::ERR_CANT_ALLOC); + using Xbyak::Error; + XBYAK_THROW(Xbyak::ERR_CANT_ALLOC); } return static_cast(p); } @@ -95,7 +96,8 @@ public: void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, mode, -1, 0); if (p == MAP_FAILED) { - throw Xbyak::Error(Xbyak::ERR_CANT_ALLOC); + using Xbyak::Error; + XBYAK_THROW(Xbyak::ERR_CANT_ALLOC); } std::memcpy(p, &size, sizeof(size_t)); return static_cast(p) + DYNARMIC_PAGE_SIZE; @@ -514,7 +516,8 @@ size_t BlockOfCode::GetTotalCodeSize() const { void* BlockOfCode::AllocateFromCodeSpace(size_t alloc_size) { if (size_ + alloc_size >= maxSize_) { - throw Xbyak::Error(Xbyak::ERR_CODE_IS_TOO_BIG); + using Xbyak::Error; + XBYAK_THROW(Xbyak::ERR_CODE_IS_TOO_BIG); } EnsureMemoryCommitted(alloc_size); diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp index 8bd9102d0d..b9a705813f 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp @@ -104,7 +104,7 @@ void EmitX64::PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, I } void EmitX64::EmitVerboseDebuggingOutput(RegAlloc& reg_alloc) { - code.sub(rsp, sizeof(RegisterData)); + code.lea(rsp, ptr[rsp - sizeof(RegisterData)]); code.stmxcsr(dword[rsp + offsetof(RegisterData, mxcsr)]); for (int i = 0; i < 16; i++) { if (rsp.getIdx() == i) { @@ -223,7 +223,7 @@ void EmitX64::EmitGetNZCVFromOp(EmitContext& ctx, IR::Inst* inst) { const Xbyak::Reg value = ctx.reg_alloc.UseGpr(args[0]).changeBit(bitsize); code.test(value, value); code.lahf(); - code.mov(al, 0); + code.xor_(al, al); ctx.reg_alloc.DefineValue(inst, nzcv); } @@ -270,7 +270,6 @@ void EmitX64::EmitNZCVFromPackedFlags(EmitContext& ctx, IR::Inst* inst) { code.shr(nzcv, 28); code.imul(nzcv, nzcv, NZCV::to_x64_multiplier); code.and_(nzcv, NZCV::x64_mask); - ctx.reg_alloc.DefineValue(inst, nzcv); } } @@ -331,10 +330,8 @@ Xbyak::Label EmitX64::EmitCond(IR::Cond cond) { code.jle(pass); break; default: - ASSERT_MSG(false, "Unknown cond {}", static_cast(cond)); - break; + UNREACHABLE(); } - return pass; } diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp index 98197c2db3..cb1afdec9e 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp @@ -992,7 +992,6 @@ static void EmitAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, int bit code.seto(overflow); ctx.reg_alloc.DefineValue(overflow_inst, overflow); } - ctx.reg_alloc.DefineValue(inst, result); } diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp index 182c887538..aeb4ceac3c 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp @@ -33,6 +33,23 @@ #include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/microinstruction.h" +#define FCODE(NAME) \ + [&code](auto... args) { \ + if constexpr (fsize == 32) { \ + code.NAME##s(args...); \ + } else { \ + code.NAME##d(args...); \ + } \ + } +#define ICODE(NAME) \ + [&code](auto... args) { \ + if constexpr (fsize == 32) { \ + code.NAME##d(args...); \ + } else { \ + code.NAME##q(args...); \ + } \ + } + namespace Dynarmic::Backend::X64 { using namespace Xbyak::util; @@ -60,23 +77,6 @@ constexpr u64 f64_max_s32 = 0x41dfffffffc00000u; // 2147483647 as a double constexpr u64 f64_max_u32 = 0x41efffffffe00000u; // 4294967295 as a double constexpr u64 f64_max_s64_lim = 0x43e0000000000000u; // 2^63 as a double (actual maximum unrepresentable) -#define FCODE(NAME) \ - [&code](auto... args) { \ - if constexpr (fsize == 32) { \ - code.NAME##s(args...); \ - } else { \ - code.NAME##d(args...); \ - } \ - } -#define ICODE(NAME) \ - [&code](auto... args) { \ - if constexpr (fsize == 32) { \ - code.NAME##d(args...); \ - } else { \ - code.NAME##q(args...); \ - } \ - } - template void ForceDenormalsToZero(BlockOfCode& code, std::initializer_list to_daz) { if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) { @@ -473,7 +473,7 @@ static void EmitFPMinMax(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { } template -static void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { +static inline void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) noexcept { using FPT = mcl::unsigned_integer_of_size; constexpr FPT default_nan = FP::FPInfo::DefaultNaN(); @@ -701,15 +701,14 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { // x64 rounds before flushing to zero // AArch64 rounds after flushing to zero // This difference of behaviour is noticable if something would round to a smallest normalized number - - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); code.movq(code.ABI_PARAM1, operand1); code.movq(code.ABI_PARAM2, operand2); code.movq(code.ABI_PARAM3, operand3); code.mov(code.ABI_PARAM4.cvt32(), ctx.FPCR().Value()); #ifdef _WIN32 - code.sub(rsp, 16 + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (16 + ABI_SHADOW_SPACE)]); code.lea(rax, code.ptr[code.r15 + code.GetJitStateInfo().offsetof_fpsr_exc]); code.mov(qword[rsp + ABI_SHADOW_SPACE], rax); code.CallFunction(fallback_fn); @@ -735,13 +734,13 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { code.vmovaps(xmm0, code.Const(xword, FP::FPInfo::mantissa_msb)); FCODE(ucomis)(operand2, operand3); - code.jp(has_nan); + code.jp(has_nan, code.T_NEAR); FCODE(ucomis)(operand1, operand1); - code.jnp(indeterminate); + code.jnp(indeterminate, code.T_NEAR); // AArch64 specifically emits a default NaN for the case when the addend is a QNaN and the two other arguments are {inf, zero} code.ptest(operand1, xmm0); - code.jz(op1_snan); + code.jz(op1_snan, code.T_NEAR); FCODE(vmuls)(xmm0, operand2, operand3); // check if {op2, op3} are {inf, zero}/{zero, inf} FCODE(ucomis)(xmm0, xmm0); code.jnp(*end); @@ -753,10 +752,10 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { code.L(has_nan); FCODE(ucomis)(operand1, operand1); - code.jnp(op1_done); + code.jnp(op1_done, code.T_NEAR); code.movaps(result, operand1); // this is done because of NaN behavior of vfmadd231s (priority of op2, op3, op1) code.ptest(operand1, xmm0); - code.jnz(op1_done); + code.jnz(op1_done, code.T_NEAR); code.L(op1_snan); code.vorps(result, operand1, xmm0); code.jmp(*end); @@ -774,9 +773,9 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { code.L(op2_done); FCODE(ucomis)(operand3, operand3); - code.jnp(op3_done); + code.jnp(op3_done, code.T_NEAR); code.ptest(operand3, xmm0); - code.jnz(op3_done); + code.jnz(op3_done, code.T_NEAR); code.vorps(result, operand3, xmm0); code.jmp(*end); code.L(op3_done); @@ -1019,7 +1018,7 @@ static void EmitFPRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*fallback); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); code.movq(code.ABI_PARAM1, operand1); code.movq(code.ABI_PARAM2, operand2); @@ -1204,9 +1203,9 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i } // a > 0 && a < 0x00800000; - code.sub(tmp, 1); + code.dec(tmp); code.cmp(tmp, 0x007FFFFF); - code.jb(fallback); + code.jb(fallback, code.T_NEAR); //within -127,128 needs_fallback = true; } @@ -1235,17 +1234,17 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i code.ucomisd(value, result); if (ctx.FPCR().DN()) { - code.jc(default_nan); - code.je(zero); + code.jc(default_nan, code.T_NEAR); + code.je(zero, code.T_NEAR); } else { - code.jp(nan); - code.je(zero); - code.jc(default_nan); + code.jp(nan, code.T_NEAR); + code.je(zero, code.T_NEAR); + code.jc(default_nan, code.T_NEAR); } if (!ctx.FPCR().FZ()) { needs_fallback = true; - code.jmp(fallback); + code.jmp(fallback, code.T_NEAR); } else { // result = 0 code.jmp(*end, code.T_NEAR); @@ -1278,7 +1277,7 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i code.L(fallback); if (needs_fallback) { - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); code.movq(code.ABI_PARAM1, operand); code.mov(code.ABI_PARAM2.cvt32(), ctx.FPCR().Value()); @@ -1361,7 +1360,7 @@ static void EmitFPRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*fallback); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); code.movq(code.ABI_PARAM1, operand1); code.movq(code.ABI_PARAM2, operand2); @@ -2132,3 +2131,6 @@ void EmitX64::EmitFPFixedU64ToSingle(EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(inst, result); } } // namespace Dynarmic::Backend::X64 + +#undef FCODE +#undef ICODE diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h index c99980d617..b25b33101c 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h @@ -161,8 +161,7 @@ template<> template<> [[maybe_unused]] Xbyak::RegExp EmitFastmemVAddr(BlockOfCode& code, A64EmitContext& ctx, Xbyak::Label& abort, Xbyak::Reg64 vaddr, bool& require_abort_handling, std::optional tmp) { - const size_t unused_top_bits = 64 - ctx.conf.fastmem_address_space_bits; - + auto const unused_top_bits = 64 - ctx.conf.fastmem_address_space_bits; if (unused_top_bits == 0) { return r13 + vaddr; } else if (ctx.conf.silently_mirror_fastmem) { @@ -306,7 +305,7 @@ const void* EmitWriteMemoryMov(BlockOfCode& code, const Xbyak::RegExp& addr, int code.L(loop); code.lock(); code.cmpxchg16b(xword[addr]); - code.jnz(loop); + code.jnz(loop, code.T_NEAR); break; } default: @@ -373,7 +372,7 @@ void EmitExclusiveTestAndClear(BlockOfCode& code, const UserConfig& conf, Xbyak: Xbyak::Label ok; code.mov(pointer, mcl::bit_cast(GetExclusiveMonitorAddressPointer(conf.global_monitor, processor_index))); code.cmp(qword[pointer], vaddr); - code.jne(ok); + code.jne(ok, code.T_NEAR); code.mov(qword[pointer], tmp); code.L(ok); } diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp index b8aa3eb653..88d0786b03 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp @@ -33,13 +33,6 @@ #include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/microinstruction.h" -namespace Dynarmic::Backend::X64 { - -using namespace Xbyak::util; -namespace mp = mcl::mp; - -namespace { - #define FCODE(NAME) \ [&code](auto... args) { \ if constexpr (fsize == 32) { \ @@ -57,6 +50,13 @@ namespace { } \ } +namespace Dynarmic::Backend::X64 { + +using namespace Xbyak::util; +namespace mp = mcl::mp; + +namespace { + template void MaybeStandardFPSCRValue(BlockOfCode& code, EmitContext& ctx, bool fpcr_controlled, Lambda lambda) { const bool switch_mxcsr = ctx.FPCR(fpcr_controlled) != ctx.FPCR(); @@ -122,11 +122,11 @@ void HandleNaNs(BlockOfCode& code, EmitContext& ctx, bool fpcr_controlled, std:: const Xbyak::Xmm result = xmms[0]; - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); const size_t stack_space = xmms.size() * 16; - code.sub(rsp, static_cast(stack_space + ABI_SHADOW_SPACE)); + code.lea(rsp, ptr[rsp - static_cast(stack_space + ABI_SHADOW_SPACE)]); for (size_t i = 0; i < xmms.size(); ++i) { code.movaps(xword[rsp + ABI_SHADOW_SPACE + i * 16], xmms[i]); } @@ -443,7 +443,7 @@ void EmitTwoOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xbyak const u32 fpcr = ctx.FPCR(fpcr_controlled).Value(); constexpr u32 stack_space = 2 * 16; - code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (stack_space + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE + 0 * 16]); code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + 1 * 16]); code.mov(code.ABI_PARAM3.cvt32(), fpcr); @@ -479,7 +479,7 @@ void EmitThreeOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xby #ifdef _WIN32 constexpr u32 stack_space = 4 * 16; - code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (stack_space + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE + 1 * 16]); code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + 2 * 16]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE + 3 * 16]); @@ -488,7 +488,7 @@ void EmitThreeOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xby code.mov(qword[rsp + ABI_SHADOW_SPACE + 0], rax); #else constexpr u32 stack_space = 3 * 16; - code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (stack_space + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE + 0 * 16]); code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + 1 * 16]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE + 2 * 16]); @@ -536,7 +536,7 @@ void EmitFourOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xbya #ifdef _WIN32 constexpr u32 stack_space = 5 * 16; - code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (stack_space + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE + 1 * 16]); code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + 2 * 16]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE + 3 * 16]); @@ -546,7 +546,7 @@ void EmitFourOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xbya code.mov(qword[rsp + ABI_SHADOW_SPACE + 8], rax); #else constexpr u32 stack_space = 4 * 16; - code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (stack_space + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE + 0 * 16]); code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + 1 * 16]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE + 2 * 16]); @@ -1371,7 +1371,7 @@ void EmitFPVectorMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*fallback); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); if (needs_rounding_correction && needs_nan_correction) { EmitFourOpFallbackWithoutRegAlloc(code, ctx, result, xmm_a, xmm_b, xmm_c, EmitFPVectorMulAddFallback, fpcr_controlled); @@ -1635,7 +1635,7 @@ static void EmitRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* in ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*fallback); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); EmitThreeOpFallbackWithoutRegAlloc(code, ctx, result, operand1, operand2, fallback_fn, fpcr_controlled); ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); @@ -1812,7 +1812,7 @@ static void EmitRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* ins ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*bad_values); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); EmitTwoOpFallbackWithoutRegAlloc(code, ctx, result, operand, fallback_fn, fpcr_controlled); ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); @@ -1898,7 +1898,7 @@ static void EmitRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* in ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*fallback); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); EmitThreeOpFallbackWithoutRegAlloc(code, ctx, result, operand1, operand2, fallback_fn, fpcr_controlled); ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); @@ -2180,3 +2180,6 @@ void EmitX64::EmitFPVectorToUnsignedFixed64(EmitContext& ctx, IR::Inst* inst) { } } // namespace Dynarmic::Backend::X64 + +#undef FCODE +#undef ICODE diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_saturation.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_saturation.cpp index fb30549fb0..5bab9c93f4 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_saturation.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_saturation.cpp @@ -338,3 +338,6 @@ void EmitX64::EmitVectorUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) } } // namespace Dynarmic::Backend::X64 + +#undef FCODE +#undef ICODE diff --git a/externals/dynarmic/src/dynarmic/backend/x64/exception_handler_windows.cpp b/externals/dynarmic/src/dynarmic/backend/x64/exception_handler_windows.cpp index a7f964337a..633e1aac9d 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/exception_handler_windows.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/exception_handler_windows.cpp @@ -186,7 +186,7 @@ struct ExceptionHandler::Impl final { code.cmp(code.rax, static_cast(code.GetTotalCodeSize())); code.ja(exception_handler_without_cb); - code.sub(code.rsp, 8); + code.lea(code.rsp, code.ptr[code.rsp - 8]); code.mov(code.ABI_PARAM1, mcl::bit_cast(&cb)); code.mov(code.ABI_PARAM2, code.ABI_PARAM3); code.CallLambda( diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h b/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h index 9fde4f8775..38160f96d4 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h +++ b/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h @@ -15,7 +15,7 @@ namespace Dynarmic::A32 { -enum class ArchVersion; +enum class ArchVersion : std::uint8_t; enum class CoprocReg; enum class Exception; enum class ExtReg; @@ -27,12 +27,11 @@ enum class Reg; * The user of this class updates `current_location` as appropriate. */ class IREmitter : public IR::IREmitter { + IR::U64 ImmCurrentLocationDescriptor(); public: IREmitter(IR::Block& block, LocationDescriptor descriptor, ArchVersion arch_version) : IR::IREmitter(block), current_location(descriptor), arch_version(arch_version) {} - - LocationDescriptor current_location; - + size_t ArchVersion() const; u32 PC() const; @@ -107,10 +106,9 @@ public: IR::U64 CoprocGetTwoWords(size_t coproc_no, bool two, size_t opc, CoprocReg CRm); void CoprocLoadWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option); void CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option); - -private: +public: + LocationDescriptor current_location; enum ArchVersion arch_version; - IR::U64 ImmCurrentLocationDescriptor(); }; } // namespace Dynarmic::A32 diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h b/externals/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h index 16ae52e13a..e4cf4a2865 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h +++ b/externals/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h @@ -33,13 +33,11 @@ inline size_t ToFastLookupIndexArm(u32 instruction) { } // namespace detail template -ArmDecodeTable GetArmDecodeTable() { +constexpr ArmDecodeTable GetArmDecodeTable() { std::vector> list = { - #define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), #include "./arm.inc" #undef INST - }; // If a matcher has more bits in its mask it is more specific, so it should come first. @@ -62,9 +60,10 @@ ArmDecodeTable GetArmDecodeTable() { template std::optional>> DecodeArm(u32 instruction) { - static const auto table = GetArmDecodeTable(); - - const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); }; + alignas(64) static const auto table = GetArmDecodeTable(); + const auto matches_instruction = [instruction](const auto& matcher) { + return matcher.Matches(instruction); + }; const auto& subtable = table[detail::ToFastLookupIndexArm(instruction)]; auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction); diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/a32_translate.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/a32_translate.cpp index 97a7f11adf..2e69927ace 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/a32_translate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/a32_translate.cpp @@ -25,3 +25,51 @@ bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, } } // namespace Dynarmic::A32 + +// ls -l | awk '{print "#include \"dynarmic/frontend/A32/translate/impl/" $9 "\""}' +#include "dynarmic/frontend/A32/translate/impl/a32_branch.cpp" +#include "dynarmic/frontend/A32/translate/impl/a32_crc32.cpp" +#include "dynarmic/frontend/A32/translate/impl/a32_exception_generating.cpp" +#include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.cpp" +//#include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/asimd_load_store_structures.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_misc.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_one_reg_modified_immediate.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_three_regs.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_two_regs_misc.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_two_regs_scalar.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_two_regs_shift.cpp" +#include "dynarmic/frontend/A32/translate/impl/barrier.cpp" +#include "dynarmic/frontend/A32/translate/impl/coprocessor.cpp" +#include "dynarmic/frontend/A32/translate/impl/data_processing.cpp" +#include "dynarmic/frontend/A32/translate/impl/divide.cpp" +#include "dynarmic/frontend/A32/translate/impl/extension.cpp" +#include "dynarmic/frontend/A32/translate/impl/hint.cpp" +#include "dynarmic/frontend/A32/translate/impl/load_store.cpp" +#include "dynarmic/frontend/A32/translate/impl/misc.cpp" +#include "dynarmic/frontend/A32/translate/impl/multiply.cpp" +#include "dynarmic/frontend/A32/translate/impl/packing.cpp" +#include "dynarmic/frontend/A32/translate/impl/parallel.cpp" +#include "dynarmic/frontend/A32/translate/impl/reversal.cpp" +#include "dynarmic/frontend/A32/translate/impl/saturated.cpp" +#include "dynarmic/frontend/A32/translate/impl/status_register_access.cpp" +#include "dynarmic/frontend/A32/translate/impl/synchronization.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb16.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_branch.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_control.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_coprocessor.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_data_processing_shifted_register.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_load_byte.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_load_halfword.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_load_store_dual.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_load_word.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_long_multiply.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_misc.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_multiply.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_parallel.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_store_single_data_item.cpp" +#include "dynarmic/frontend/A32/translate/impl/vfp.cpp" diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.cpp index 276f8384e7..c83529f343 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.cpp @@ -11,6 +11,10 @@ namespace Dynarmic::A32 { +bool TranslatorVisitor::arm_NOP() { + return true; +} + bool TranslatorVisitor::ArmConditionPassed(Cond cond) { return IsConditionPassed(*this, cond); } diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h index 44ac24503c..d33069c6e0 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h @@ -258,7 +258,7 @@ struct TranslatorVisitor final { bool arm_CLZ(Cond cond, Reg d, Reg m); bool arm_MOVT(Cond cond, Imm<4> imm4, Reg d, Imm<12> imm12); bool arm_MOVW(Cond cond, Imm<4> imm4, Reg d, Imm<12> imm12); - bool arm_NOP() { return true; } + bool arm_NOP(); bool arm_RBIT(Cond cond, Reg d, Reg m); bool arm_SBFX(Cond cond, Imm<5> widthm1, Reg d, Imm<5> lsb, Reg n); bool arm_SEL(Cond cond, Reg n, Reg d, Reg m); diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_three_regs.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_three_regs.cpp index a69f39bfb6..da8f43f2fb 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_three_regs.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_three_regs.cpp @@ -6,6 +6,7 @@ #include #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { namespace { @@ -17,11 +18,6 @@ enum class Comparison { AbsoluteGT, }; -enum class AccumulateBehavior { - None, - Accumulate, -}; - enum class WidenBehaviour { Second, Both, diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_misc.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_misc.cpp index 62b9af55a5..ddae1f420b 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_misc.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_misc.cpp @@ -8,10 +8,11 @@ #include #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { namespace { -enum class Comparison { +enum class ComparisonATRM { EQ, GE, GT, @@ -19,7 +20,7 @@ enum class Comparison { LT, }; -bool CompareWithZero(TranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm, Comparison type) { +bool CompareWithZero(TranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm, ComparisonATRM type) { if (sz == 0b11 || (F && sz != 0b10)) { return v.UndefinedInstruction(); } @@ -36,15 +37,15 @@ bool CompareWithZero(TranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool F, if (F) { switch (type) { - case Comparison::EQ: + case ComparisonATRM::EQ: return v.ir.FPVectorEqual(32, reg_m, zero, false); - case Comparison::GE: + case ComparisonATRM::GE: return v.ir.FPVectorGreaterEqual(32, reg_m, zero, false); - case Comparison::GT: + case ComparisonATRM::GT: return v.ir.FPVectorGreater(32, reg_m, zero, false); - case Comparison::LE: + case ComparisonATRM::LE: return v.ir.FPVectorGreaterEqual(32, zero, reg_m, false); - case Comparison::LT: + case ComparisonATRM::LT: return v.ir.FPVectorGreater(32, zero, reg_m, false); } @@ -67,11 +68,6 @@ bool CompareWithZero(TranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool F, return true; } -enum class AccumulateBehavior { - None, - Accumulate, -}; - bool PairedAddOperation(TranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool op, bool Q, bool M, size_t Vm, AccumulateBehavior accumulate) { if (sz == 0b11) { return v.UndefinedInstruction(); @@ -385,23 +381,23 @@ bool TranslatorVisitor::asimd_VQNEG(bool D, size_t sz, size_t Vd, bool Q, bool M } bool TranslatorVisitor::asimd_VCGT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { - return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::GT); + return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, ComparisonATRM::GT); } bool TranslatorVisitor::asimd_VCGE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { - return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::GE); + return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, ComparisonATRM::GE); } bool TranslatorVisitor::asimd_VCEQ_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { - return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::EQ); + return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, ComparisonATRM::EQ); } bool TranslatorVisitor::asimd_VCLE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { - return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::LE); + return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, ComparisonATRM::LE); } bool TranslatorVisitor::asimd_VCLT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { - return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::LT); + return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, ComparisonATRM::LT); } bool TranslatorVisitor::asimd_VABS(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_shift.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_shift.cpp index b7300f91e4..cbdfea04ce 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_shift.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_shift.cpp @@ -16,7 +16,7 @@ enum class Accumulating { Accumulate }; -enum class Rounding { +enum class RoundingATRS { None, Round, }; @@ -32,7 +32,7 @@ enum class Signedness { Unsigned }; -IR::U128 PerformRoundingCorrection(TranslatorVisitor& v, size_t esize, u64 round_value, IR::U128 original, IR::U128 shifted) { +IR::U128 PerformRoundingATRSCorrection(TranslatorVisitor& v, size_t esize, u64 round_value, IR::U128 original, IR::U128 shifted) { const auto round_const = v.ir.VectorBroadcast(esize, v.I(esize, round_value)); const auto round_correction = v.ir.VectorEqual(esize, v.ir.VectorAnd(original, round_const), round_const); return v.ir.VectorSub(esize, shifted, round_correction); @@ -58,7 +58,7 @@ std::pair ElementSizeAndShiftAmount(bool right_shift, bool L, si } } -bool ShiftRight(TranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm, Accumulating accumulate, Rounding rounding) { +bool ShiftRight(TranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm, Accumulating accumulate, RoundingATRS RoundingATRS) { if (!L && mcl::bit::get_bits<3, 5>(imm6) == 0) { return v.DecodeError(); } @@ -75,9 +75,9 @@ bool ShiftRight(TranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bo auto result = U ? v.ir.VectorLogicalShiftRight(esize, reg_m, static_cast(shift_amount)) : v.ir.VectorArithmeticShiftRight(esize, reg_m, static_cast(shift_amount)); - if (rounding == Rounding::Round) { + if (RoundingATRS == RoundingATRS::Round) { const u64 round_value = 1ULL << (shift_amount - 1); - result = PerformRoundingCorrection(v, esize, round_value, reg_m, result); + result = PerformRoundingATRSCorrection(v, esize, round_value, reg_m, result); } if (accumulate == Accumulating::Accumulate) { @@ -89,7 +89,7 @@ bool ShiftRight(TranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bo return true; } -bool ShiftRightNarrowing(TranslatorVisitor& v, bool D, size_t imm6, size_t Vd, bool M, size_t Vm, Rounding rounding, Narrowing narrowing, Signedness signedness) { +bool ShiftRightNarrowing(TranslatorVisitor& v, bool D, size_t imm6, size_t Vd, bool M, size_t Vm, RoundingATRS RoundingATRS, Narrowing narrowing, Signedness signedness) { if (mcl::bit::get_bits<3, 5>(imm6) == 0) { return v.DecodeError(); } @@ -113,9 +113,9 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, bool D, size_t imm6, size_t Vd, b return v.ir.VectorLogicalShiftRight(source_esize, reg_m, shift_amount); }(); - if (rounding == Rounding::Round) { + if (RoundingATRS == RoundingATRS::Round) { const u64 round_value = 1ULL << (shift_amount - 1); - wide_result = PerformRoundingCorrection(v, source_esize, round_value, reg_m, wide_result); + wide_result = PerformRoundingATRSCorrection(v, source_esize, round_value, reg_m, wide_result); } const auto result = [&] { @@ -141,22 +141,22 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, bool D, size_t imm6, size_t Vd, b bool TranslatorVisitor::asimd_SHR(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm, - Accumulating::None, Rounding::None); + Accumulating::None, RoundingATRS::None); } bool TranslatorVisitor::asimd_SRA(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm, - Accumulating::Accumulate, Rounding::None); + Accumulating::Accumulate, RoundingATRS::None); } bool TranslatorVisitor::asimd_VRSHR(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm, - Accumulating::None, Rounding::Round); + Accumulating::None, RoundingATRS::Round); } bool TranslatorVisitor::asimd_VRSRA(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm, - Accumulating::Accumulate, Rounding::Round); + Accumulating::Accumulate, RoundingATRS::Round); } bool TranslatorVisitor::asimd_VSRI(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { @@ -271,32 +271,32 @@ bool TranslatorVisitor::asimd_VSHL(bool D, size_t imm6, size_t Vd, bool L, bool bool TranslatorVisitor::asimd_VSHRN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::None, Narrowing::Truncation, Signedness::Unsigned); + RoundingATRS::None, Narrowing::Truncation, Signedness::Unsigned); } bool TranslatorVisitor::asimd_VRSHRN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::Round, Narrowing::Truncation, Signedness::Unsigned); + RoundingATRS::Round, Narrowing::Truncation, Signedness::Unsigned); } bool TranslatorVisitor::asimd_VQRSHRUN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Signed); + RoundingATRS::Round, Narrowing::SaturateToUnsigned, Signedness::Signed); } bool TranslatorVisitor::asimd_VQSHRUN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Signed); + RoundingATRS::None, Narrowing::SaturateToUnsigned, Signedness::Signed); } bool TranslatorVisitor::asimd_VQSHRN(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::None, U ? Narrowing::SaturateToUnsigned : Narrowing::SaturateToSigned, U ? Signedness::Unsigned : Signedness::Signed); + RoundingATRS::None, U ? Narrowing::SaturateToUnsigned : Narrowing::SaturateToSigned, U ? Signedness::Unsigned : Signedness::Signed); } bool TranslatorVisitor::asimd_VQRSHRN(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::Round, U ? Narrowing::SaturateToUnsigned : Narrowing::SaturateToSigned, U ? Signedness::Unsigned : Signedness::Signed); + RoundingATRS::Round, U ? Narrowing::SaturateToUnsigned : Narrowing::SaturateToSigned, U ? Signedness::Unsigned : Signedness::Signed); } bool TranslatorVisitor::asimd_VSHLL(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/common.h b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/common.h new file mode 100644 index 0000000000..39b2c4355c --- /dev/null +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/common.h @@ -0,0 +1,31 @@ +#pragma once + +#include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" + +namespace Dynarmic::A32 { + +static inline IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) noexcept { + return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result); +} + +static inline IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) noexcept { + return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result); +} + +static inline IR::U32 Rotate(A32::IREmitter& ir, Reg m, SignExtendRotation rotate) noexcept { + const u8 rotate_by = static_cast(static_cast(rotate) * 8); + return ir.RotateRight(ir.GetRegister(m), ir.Imm8(rotate_by), ir.Imm1(0)).result; +} + +static inline bool ITBlockCheck(const A32::IREmitter& ir) noexcept { + return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock(); +} + +using ExtensionFunctionU16 = IR::U32 (IREmitter::*)(const IR::U16&); + +enum class AccumulateBehavior { + None, + Accumulate, +}; + +} diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/extension.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/extension.cpp index 3ee6a670f0..518f8b944e 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/extension.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/extension.cpp @@ -4,14 +4,10 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static IR::U32 Rotate(A32::IREmitter& ir, Reg m, SignExtendRotation rotate) { - const u8 rotate_by = static_cast(static_cast(rotate) * 8); - return ir.RotateRight(ir.GetRegister(m), ir.Imm8(rotate_by), ir.Imm1(0)).result; -} - // SXTAB , , {, } bool TranslatorVisitor::arm_SXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { if (d == Reg::PC || m == Reg::PC) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/saturated.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/saturated.cpp index 41db115044..2371e54ae7 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/saturated.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/saturated.cpp @@ -4,17 +4,10 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) { - return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result); -} - -static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) { - return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result); -} - // Saturation instructions // SSAT , #, {, } diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp index 1a6767dbee..14a3fef2d1 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp @@ -7,15 +7,9 @@ #include #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) { - return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result); -} - -static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) { - return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result); -} using SaturationFunction = IR::ResultAndOverflow (IREmitter::*)(const IR::U32&, size_t); diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp index 1dda532d92..1498ac6c7b 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp @@ -4,13 +4,10 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { namespace { -IR::U32 Rotate(A32::IREmitter& ir, Reg m, SignExtendRotation rotate) { - const u8 rotate_by = static_cast(static_cast(rotate) * 8); - return ir.RotateRight(ir.GetRegister(m), ir.Imm8(rotate_by), ir.Imm1(0)).result; -} using ShiftFunction = IR::ResultAndCarry (IREmitter::*)(const IR::U32&, const IR::U8&, const IR::U1&); diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_byte.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_byte.cpp index 42b2ebf4aa..d309d42d66 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_byte.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_byte.cpp @@ -25,9 +25,9 @@ static bool PLIHandler(TranslatorVisitor& v) { return v.RaiseException(Exception::PreloadInstruction); } -using ExtensionFunction = IR::U32 (IREmitter::*)(const IR::U8&); +using ExtensionFunctionU8 = IR::U32 (IREmitter::*)(const IR::U8&); -static bool LoadByteLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, ExtensionFunction ext_fn) { +static bool LoadByteLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, ExtensionFunctionU8 ext_fn) { const u32 imm32 = imm12.ZeroExtend(); const u32 base = v.ir.AlignPC(4); const u32 address = U ? (base + imm32) : (base - imm32); @@ -37,7 +37,7 @@ static bool LoadByteLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, return true; } -static bool LoadByteRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunction ext_fn) { +static bool LoadByteRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunctionU8 ext_fn) { if (m == Reg::PC) { return v.UnpredictableInstruction(); } @@ -52,7 +52,7 @@ static bool LoadByteRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Re return true; } -static bool LoadByteImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunction ext_fn) { +static bool LoadByteImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunctionU8 ext_fn) { const u32 imm32 = imm12.ZeroExtend(); const IR::U32 reg_n = v.ir.GetRegister(n); const IR::U32 offset_address = U ? v.ir.Add(reg_n, v.ir.Imm32(imm32)) diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_halfword.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_halfword.cpp index 5b9f1639af..d8a043e553 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_halfword.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_halfword.cpp @@ -4,12 +4,11 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -using ExtensionFunction = IR::U32 (IREmitter::*)(const IR::U16&); - -static bool LoadHalfLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, ExtensionFunction ext_fn) { +static bool LoadHalfLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, ExtensionFunctionU16 ext_fn) { const auto imm32 = imm12.ZeroExtend(); const auto base = v.ir.AlignPC(4); const auto address = U ? (base + imm32) : (base - imm32); @@ -19,7 +18,7 @@ static bool LoadHalfLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, return true; } -static bool LoadHalfRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunction ext_fn) { +static bool LoadHalfRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunctionU16 ext_fn) { if (m == Reg::PC) { return v.UnpredictableInstruction(); } @@ -34,7 +33,7 @@ static bool LoadHalfRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Re return true; } -static bool LoadHalfImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunction ext_fn) { +static bool LoadHalfImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunctionU16 ext_fn) { const u32 imm32 = imm12.ZeroExtend(); const IR::U32 reg_n = v.ir.GetRegister(n); const IR::U32 offset_address = U ? v.ir.Add(reg_n, v.ir.Imm32(imm32)) diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_dual.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_dual.cpp index 17d4285c23..eb574d773c 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_dual.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_dual.cpp @@ -6,11 +6,9 @@ #include #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static bool ITBlockCheck(const A32::IREmitter& ir) { - return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock(); -} static bool TableBranch(TranslatorVisitor& v, Reg n, Reg m, bool half) { if (m == Reg::PC) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp index 2bc782b973..d446fbf3dd 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp @@ -6,11 +6,9 @@ #include #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static bool ITBlockCheck(const A32::IREmitter& ir) { - return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock(); -} static bool LDMHelper(A32::IREmitter& ir, bool W, Reg n, u32 list, const IR::U32& start_address, const IR::U32& writeback_address) { auto address = start_address; diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_word.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_word.cpp index b92e27fc66..b7556a8caa 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_word.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_word.cpp @@ -4,11 +4,9 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static bool ITBlockCheck(const A32::IREmitter& ir) { - return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock(); -} bool TranslatorVisitor::thumb32_LDR_lit(bool U, Reg t, Imm<12> imm12) { if (t == Reg::PC && ITBlockCheck(ir)) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_parallel.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_parallel.cpp index 654940967d..64d57e917d 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_parallel.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_parallel.cpp @@ -4,15 +4,9 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) { - return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result); -} - -static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) { - return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result); -} bool TranslatorVisitor::thumb32_SADD8(Reg n, Reg d, Reg m) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.cpp index 3f5a70bdc0..68f536c8d5 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.cpp @@ -5,261 +5,7 @@ #include "dynarmic/frontend/A64/a64_ir_emitter.h" -#include - -#include "dynarmic/ir/opcodes.h" - namespace Dynarmic::A64 { -using Opcode = IR::Opcode; - -u64 IREmitter::PC() const { - return current_location->PC(); -} - -u64 IREmitter::AlignPC(size_t alignment) const { - const u64 pc = PC(); - return static_cast(pc - pc % alignment); -} - -void IREmitter::SetCheckBit(const IR::U1& value) { - Inst(Opcode::A64SetCheckBit, value); -} - -IR::U1 IREmitter::GetCFlag() { - return Inst(Opcode::A64GetCFlag); -} - -IR::U32 IREmitter::GetNZCVRaw() { - return Inst(Opcode::A64GetNZCVRaw); -} - -void IREmitter::SetNZCVRaw(IR::U32 value) { - Inst(Opcode::A64SetNZCVRaw, value); -} - -void IREmitter::SetNZCV(const IR::NZCV& nzcv) { - Inst(Opcode::A64SetNZCV, nzcv); -} - -void IREmitter::CallSupervisor(u32 imm) { - Inst(Opcode::A64CallSupervisor, Imm32(imm)); -} - -void IREmitter::ExceptionRaised(Exception exception) { - Inst(Opcode::A64ExceptionRaised, Imm64(PC()), Imm64(static_cast(exception))); -} - -void IREmitter::DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value) { - Inst(Opcode::A64DataCacheOperationRaised, ImmCurrentLocationDescriptor(), Imm64(static_cast(op)), value); -} - -void IREmitter::InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value) { - Inst(Opcode::A64InstructionCacheOperationRaised, Imm64(static_cast(op)), value); -} - -void IREmitter::DataSynchronizationBarrier() { - Inst(Opcode::A64DataSynchronizationBarrier); -} - -void IREmitter::DataMemoryBarrier() { - Inst(Opcode::A64DataMemoryBarrier); -} - -void IREmitter::InstructionSynchronizationBarrier() { - Inst(Opcode::A64InstructionSynchronizationBarrier); -} - -IR::U32 IREmitter::GetCNTFRQ() { - return Inst(Opcode::A64GetCNTFRQ); -} - -IR::U64 IREmitter::GetCNTPCT() { - return Inst(Opcode::A64GetCNTPCT); -} - -IR::U32 IREmitter::GetCTR() { - return Inst(Opcode::A64GetCTR); -} - -IR::U32 IREmitter::GetDCZID() { - return Inst(Opcode::A64GetDCZID); -} - -IR::U64 IREmitter::GetTPIDR() { - return Inst(Opcode::A64GetTPIDR); -} - -void IREmitter::SetTPIDR(const IR::U64& value) { - Inst(Opcode::A64SetTPIDR, value); -} - -IR::U64 IREmitter::GetTPIDRRO() { - return Inst(Opcode::A64GetTPIDRRO); -} - -void IREmitter::ClearExclusive() { - Inst(Opcode::A64ClearExclusive); -} - -IR::U8 IREmitter::ReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U16 IREmitter::ReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U64 IREmitter::ReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U128 IREmitter::ReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U64 IREmitter::ExclusiveReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U128 IREmitter::ExclusiveReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -void IREmitter::WriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -void IREmitter::WriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -void IREmitter::WriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -void IREmitter::WriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -void IREmitter::WriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::GetW(Reg reg) { - if (reg == Reg::ZR) - return Imm32(0); - return Inst(Opcode::A64GetW, IR::Value(reg)); -} - -IR::U64 IREmitter::GetX(Reg reg) { - if (reg == Reg::ZR) - return Imm64(0); - return Inst(Opcode::A64GetX, IR::Value(reg)); -} - -IR::U128 IREmitter::GetS(Vec vec) { - return Inst(Opcode::A64GetS, IR::Value(vec)); -} - -IR::U128 IREmitter::GetD(Vec vec) { - return Inst(Opcode::A64GetD, IR::Value(vec)); -} - -IR::U128 IREmitter::GetQ(Vec vec) { - return Inst(Opcode::A64GetQ, IR::Value(vec)); -} - -IR::U64 IREmitter::GetSP() { - return Inst(Opcode::A64GetSP); -} - -IR::U32 IREmitter::GetFPCR() { - return Inst(Opcode::A64GetFPCR); -} - -IR::U32 IREmitter::GetFPSR() { - return Inst(Opcode::A64GetFPSR); -} - -void IREmitter::SetW(const Reg reg, const IR::U32& value) { - if (reg == Reg::ZR) - return; - Inst(Opcode::A64SetW, IR::Value(reg), value); -} - -void IREmitter::SetX(const Reg reg, const IR::U64& value) { - if (reg == Reg::ZR) - return; - Inst(Opcode::A64SetX, IR::Value(reg), value); -} - -void IREmitter::SetS(const Vec vec, const IR::U128& value) { - Inst(Opcode::A64SetS, IR::Value(vec), value); -} - -void IREmitter::SetD(const Vec vec, const IR::U128& value) { - Inst(Opcode::A64SetD, IR::Value(vec), value); -} - -void IREmitter::SetQ(const Vec vec, const IR::U128& value) { - Inst(Opcode::A64SetQ, IR::Value(vec), value); -} - -void IREmitter::SetSP(const IR::U64& value) { - Inst(Opcode::A64SetSP, value); -} - -void IREmitter::SetFPCR(const IR::U32& value) { - Inst(Opcode::A64SetFPCR, value); -} - -void IREmitter::SetFPSR(const IR::U32& value) { - Inst(Opcode::A64SetFPSR, value); -} - -void IREmitter::SetPC(const IR::U64& value) { - Inst(Opcode::A64SetPC, value); -} - -IR::U64 IREmitter::ImmCurrentLocationDescriptor() { - return Imm64(IR::LocationDescriptor{*current_location}.Value()); -} } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.h b/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.h index 7fc8bea7c4..3bf633a3da 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.h +++ b/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.h @@ -8,12 +8,14 @@ #include #include +#include #include "dynarmic/frontend/A64/a64_location_descriptor.h" #include "dynarmic/frontend/A64/a64_types.h" #include "dynarmic/interface/A64/config.h" #include "dynarmic/ir/ir_emitter.h" #include "dynarmic/ir/value.h" +#include "dynarmic/ir/opcodes.h" namespace Dynarmic::A64 { @@ -24,79 +26,262 @@ namespace Dynarmic::A64 { */ class IREmitter : public IR::IREmitter { public: - explicit IREmitter(IR::Block& block) - : IR::IREmitter(block) {} - explicit IREmitter(IR::Block& block, LocationDescriptor descriptor) - : IR::IREmitter(block), current_location(descriptor) {} + explicit IREmitter(IR::Block& block) : IR::IREmitter(block) {} + explicit IREmitter(IR::Block& block, LocationDescriptor descriptor) : IR::IREmitter(block), current_location(descriptor) {} std::optional current_location; - u64 PC() const; - u64 AlignPC(size_t alignment) const; + using Opcode = IR::Opcode; - void SetCheckBit(const IR::U1& value); - IR::U1 GetCFlag(); - IR::U32 GetNZCVRaw(); - void SetNZCVRaw(IR::U32 value); - void SetNZCV(const IR::NZCV& nzcv); + u64 PC() const noexcept { + return current_location->PC(); + } - void CallSupervisor(u32 imm); - void ExceptionRaised(Exception exception); - void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value); - void InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value); - void DataSynchronizationBarrier(); - void DataMemoryBarrier(); - void InstructionSynchronizationBarrier(); - IR::U32 GetCNTFRQ(); - IR::U64 GetCNTPCT(); // TODO: Ensure sub-basic-block cycle counts are updated before this. - IR::U32 GetCTR(); - IR::U32 GetDCZID(); - IR::U64 GetTPIDR(); - IR::U64 GetTPIDRRO(); - void SetTPIDR(const IR::U64& value); + u64 AlignPC(size_t alignment) const noexcept { + const u64 pc = PC(); + return static_cast(pc - pc % alignment); + } - void ClearExclusive(); - IR::U8 ReadMemory8(const IR::U64& vaddr, IR::AccType acc_type); - IR::U16 ReadMemory16(const IR::U64& vaddr, IR::AccType acc_type); - IR::U32 ReadMemory32(const IR::U64& vaddr, IR::AccType acc_type); - IR::U64 ReadMemory64(const IR::U64& vaddr, IR::AccType acc_type); - IR::U128 ReadMemory128(const IR::U64& vaddr, IR::AccType acc_type); - IR::U8 ExclusiveReadMemory8(const IR::U64& vaddr, IR::AccType acc_type); - IR::U16 ExclusiveReadMemory16(const IR::U64& vaddr, IR::AccType acc_type); - IR::U32 ExclusiveReadMemory32(const IR::U64& vaddr, IR::AccType acc_type); - IR::U64 ExclusiveReadMemory64(const IR::U64& vaddr, IR::AccType acc_type); - IR::U128 ExclusiveReadMemory128(const IR::U64& vaddr, IR::AccType acc_type); - void WriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type); - void WriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type); - void WriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type); - void WriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type); - void WriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type); - IR::U32 ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type); - IR::U32 ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type); - IR::U32 ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type); - IR::U32 ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type); - IR::U32 ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type); + void SetCheckBit(const IR::U1& value) noexcept { + Inst(Opcode::A64SetCheckBit, value); + } - IR::U32 GetW(Reg source_reg); - IR::U64 GetX(Reg source_reg); - IR::U128 GetS(Vec source_vec); - IR::U128 GetD(Vec source_vec); - IR::U128 GetQ(Vec source_vec); - IR::U64 GetSP(); - IR::U32 GetFPCR(); - IR::U32 GetFPSR(); - void SetW(Reg dest_reg, const IR::U32& value); - void SetX(Reg dest_reg, const IR::U64& value); - void SetS(Vec dest_vec, const IR::U128& value); - void SetD(Vec dest_vec, const IR::U128& value); - void SetQ(Vec dest_vec, const IR::U128& value); - void SetSP(const IR::U64& value); - void SetFPCR(const IR::U32& value); - void SetFPSR(const IR::U32& value); - void SetPC(const IR::U64& value); + IR::U1 GetCFlag() noexcept { + return Inst(Opcode::A64GetCFlag); + } + + IR::U32 GetNZCVRaw() noexcept { + return Inst(Opcode::A64GetNZCVRaw); + } + + void SetNZCVRaw(IR::U32 value) noexcept { + Inst(Opcode::A64SetNZCVRaw, value); + } + + void SetNZCV(const IR::NZCV& nzcv) noexcept { + Inst(Opcode::A64SetNZCV, nzcv); + } + + void CallSupervisor(u32 imm) noexcept { + Inst(Opcode::A64CallSupervisor, Imm32(imm)); + } + + void ExceptionRaised(Exception exception) noexcept { + Inst(Opcode::A64ExceptionRaised, Imm64(PC()), Imm64(static_cast(exception))); + } + + void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value) noexcept { + Inst(Opcode::A64DataCacheOperationRaised, ImmCurrentLocationDescriptor(), Imm64(static_cast(op)), value); + } + + void InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value) noexcept { + Inst(Opcode::A64InstructionCacheOperationRaised, Imm64(static_cast(op)), value); + } + + void DataSynchronizationBarrier() noexcept { + Inst(Opcode::A64DataSynchronizationBarrier); + } + + void DataMemoryBarrier() noexcept { + Inst(Opcode::A64DataMemoryBarrier); + } + + void InstructionSynchronizationBarrier() noexcept { + Inst(Opcode::A64InstructionSynchronizationBarrier); + } + + IR::U32 GetCNTFRQ() noexcept { + return Inst(Opcode::A64GetCNTFRQ); + } + + IR::U64 GetCNTPCT() noexcept { + return Inst(Opcode::A64GetCNTPCT); + } + + IR::U32 GetCTR() noexcept { + return Inst(Opcode::A64GetCTR); + } + + IR::U32 GetDCZID() noexcept { + return Inst(Opcode::A64GetDCZID); + } + + IR::U64 GetTPIDR() noexcept { + return Inst(Opcode::A64GetTPIDR); + } + + void SetTPIDR(const IR::U64& value) noexcept { + Inst(Opcode::A64SetTPIDR, value); + } + + IR::U64 GetTPIDRRO() noexcept { + return Inst(Opcode::A64GetTPIDRRO); + } + + void ClearExclusive() noexcept { + Inst(Opcode::A64ClearExclusive); + } + + IR::U8 ReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U16 ReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U32 ReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U64 ReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U128 ReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U8 ExclusiveReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U16 ExclusiveReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U32 ExclusiveReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U64 ExclusiveReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U128 ExclusiveReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + void WriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) noexcept { + Inst(Opcode::A64WriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + void WriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) noexcept { + Inst(Opcode::A64WriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + void WriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) noexcept { + Inst(Opcode::A64WriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + void WriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) noexcept { + Inst(Opcode::A64WriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + void WriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) noexcept { + Inst(Opcode::A64WriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveWriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveWriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveWriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveWriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveWriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 GetW(Reg reg) noexcept { + if (reg == Reg::ZR) + return Imm32(0); + return Inst(Opcode::A64GetW, IR::Value(reg)); + } + + IR::U64 GetX(Reg reg) noexcept { + if (reg == Reg::ZR) + return Imm64(0); + return Inst(Opcode::A64GetX, IR::Value(reg)); + } + + IR::U128 GetS(Vec vec) noexcept { + return Inst(Opcode::A64GetS, IR::Value(vec)); + } + + IR::U128 GetD(Vec vec) noexcept { + return Inst(Opcode::A64GetD, IR::Value(vec)); + } + + IR::U128 GetQ(Vec vec) noexcept { + return Inst(Opcode::A64GetQ, IR::Value(vec)); + } + + IR::U64 GetSP() noexcept { + return Inst(Opcode::A64GetSP); + } + + IR::U32 GetFPCR() noexcept { + return Inst(Opcode::A64GetFPCR); + } + + IR::U32 GetFPSR() noexcept { + return Inst(Opcode::A64GetFPSR); + } + + void SetW(const Reg reg, const IR::U32& value) noexcept { + if (reg == Reg::ZR) + return; + Inst(Opcode::A64SetW, IR::Value(reg), value); + } + + void SetX(const Reg reg, const IR::U64& value) noexcept { + if (reg == Reg::ZR) + return; + Inst(Opcode::A64SetX, IR::Value(reg), value); + } + + void SetS(const Vec vec, const IR::U128& value) noexcept { + Inst(Opcode::A64SetS, IR::Value(vec), value); + } + + void SetD(const Vec vec, const IR::U128& value) noexcept { + Inst(Opcode::A64SetD, IR::Value(vec), value); + } + + void SetQ(const Vec vec, const IR::U128& value) noexcept { + Inst(Opcode::A64SetQ, IR::Value(vec), value); + } + + void SetSP(const IR::U64& value) noexcept { + Inst(Opcode::A64SetSP, value); + } + + void SetFPCR(const IR::U32& value) noexcept { + Inst(Opcode::A64SetFPCR, value); + } + + void SetFPSR(const IR::U32& value) noexcept { + Inst(Opcode::A64SetFPSR, value); + } + + void SetPC(const IR::U64& value) noexcept { + Inst(Opcode::A64SetPC, value); + } private: - IR::U64 ImmCurrentLocationDescriptor(); + IR::U64 ImmCurrentLocationDescriptor() noexcept { + return Imm64(IR::LocationDescriptor{*current_location}.Value()); + } }; } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h b/externals/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h index f264893502..e807490d16 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h +++ b/externals/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h @@ -33,27 +33,26 @@ inline size_t ToFastLookupIndex(u32 instruction) { } // namespace detail template -DecodeTable GetDecodeTable() { +constexpr DecodeTable GetDecodeTable() { std::vector> list = { #define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), #include "./a64.inc" #undef INST }; + // If a matcher has more bits in its mask it is more specific, so it should come first. std::stable_sort(list.begin(), list.end(), [](const auto& matcher1, const auto& matcher2) { // If a matcher has more bits in its mask it is more specific, so it should come first. return mcl::bit::count_ones(matcher1.GetMask()) > mcl::bit::count_ones(matcher2.GetMask()); }); // Exceptions to the above rule of thumb. - const std::set comes_first{ - "MOVI, MVNI, ORR, BIC (vector, immediate)", - "FMOV (vector, immediate)", - "Unallocated SIMD modified immediate", - }; - std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) { - return comes_first.count(matcher.GetName()) > 0; + return std::set{ + "MOVI, MVNI, ORR, BIC (vector, immediate)", + "FMOV (vector, immediate)", + "Unallocated SIMD modified immediate", + }.count(matcher.GetName()) > 0; }); DecodeTable table{}; @@ -75,7 +74,6 @@ std::optional>> Decode(u32 instruction) const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); }; - const auto& subtable = table[detail::ToFastLookupIndex(instruction)]; auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction); return iter != subtable.end() ? std::optional>>(*iter) : std::nullopt; diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp index 05996aeb64..352c2e6ae2 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp @@ -67,3 +67,64 @@ bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, } } // namespace Dynarmic::A64 + +// ls -l | awk '{print "#include \"dynarmic/frontend/A64/translate/impl/" $9 "\""}' +#include "dynarmic/frontend/A64/translate/impl/a64_branch.cpp" +#include "dynarmic/frontend/A64/translate/impl/a64_exception_generating.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_addsub.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_bitfield.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_conditional_compare.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_conditional_select.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_crc32.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_logical.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_multiply.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_pcrel.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_shift.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_compare.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_conditional_compare.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_conditional_select.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_conversion_fixed_point.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_conversion_integer.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_data_processing_one_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_data_processing_three_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_data_processing_two_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/impl.cpp" +#include "dynarmic/frontend/A64/translate/impl/impl.h" +#include "dynarmic/frontend/A64/translate/impl/load_store_exclusive.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_load_literal.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_multiple_structures.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_no_allocate_pair.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_register_immediate.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_register_pair.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_register_register_offset.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_register_unprivileged.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_single_structure.cpp" +#include "dynarmic/frontend/A64/translate/impl/move_wide.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_across_lanes.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_aes.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_copy.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_crypto_four_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_crypto_three_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_extract.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_modified_immediate.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_permute.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_scalar_pairwise.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_scalar_three_same.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_sha512.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_sha.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_shift_by_immediate.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_table_lookup.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_three_different.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_three_same.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_three_same_extra.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_two_register_misc.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp" +#include "dynarmic/frontend/A64/translate/impl/sys_dc.cpp" +#include "dynarmic/frontend/A64/translate/impl/sys_ic.cpp" +#include "dynarmic/frontend/A64/translate/impl/system.cpp" +#include "dynarmic/frontend/A64/translate/impl/system_flag_format.cpp" +#include "dynarmic/frontend/A64/translate/impl/system_flag_manipulation.cpp" diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_pairwise.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_pairwise.cpp index 63615b0f9a..e3b8d7502c 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_pairwise.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_pairwise.cpp @@ -7,14 +7,14 @@ namespace Dynarmic::A64 { namespace { -enum class MinMaxOperation { +enum class MinMaxOperationSSPW { Max, MaxNumeric, Min, MinNumeric, }; -bool FPPairwiseMinMax(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, MinMaxOperation operation) { +bool FPPairwiseMinMax(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, MinMaxOperationSSPW operation) { const size_t esize = sz ? 64 : 32; const IR::U128 operand = v.V(128, Vn); @@ -22,13 +22,13 @@ bool FPPairwiseMinMax(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, MinMaxOpera const IR::U32U64 element2 = v.ir.VectorGetElement(esize, operand, 1); const IR::U32U64 result = [&] { switch (operation) { - case MinMaxOperation::Max: + case MinMaxOperationSSPW::Max: return v.ir.FPMax(element1, element2); - case MinMaxOperation::MaxNumeric: + case MinMaxOperationSSPW::MaxNumeric: return v.ir.FPMaxNumeric(element1, element2); - case MinMaxOperation::Min: + case MinMaxOperationSSPW::Min: return v.ir.FPMin(element1, element2); - case MinMaxOperation::MinNumeric: + case MinMaxOperationSSPW::MinNumeric: return v.ir.FPMinNumeric(element1, element2); default: UNREACHABLE(); @@ -63,18 +63,18 @@ bool TranslatorVisitor::FADDP_pair_2(bool size, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FMAXNMP_pair_2(bool sz, Vec Vn, Vec Vd) { - return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::MaxNumeric); + return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperationSSPW::MaxNumeric); } bool TranslatorVisitor::FMAXP_pair_2(bool sz, Vec Vn, Vec Vd) { - return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::Max); + return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperationSSPW::Max); } bool TranslatorVisitor::FMINNMP_pair_2(bool sz, Vec Vn, Vec Vd) { - return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::MinNumeric); + return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperationSSPW::MinNumeric); } bool TranslatorVisitor::FMINP_pair_2(bool sz, Vec Vn, Vec Vd) { - return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::Min); + return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperationSSPW::Min); } } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp index a0570edc7e..5d60cb31c3 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp @@ -27,7 +27,7 @@ enum class ShiftExtraBehavior { Accumulate, }; -enum class Signedness { +enum class SignednessSSSBI { Signed, Unsigned, }; @@ -63,7 +63,7 @@ bool SaturatingShiftLeft(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, return true; } -bool ShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, ShiftExtraBehavior behavior, Signedness signedness) { +bool ShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, ShiftExtraBehavior behavior, SignednessSSSBI SignednessSSSBI) { if (!immh.Bit<3>()) { return v.ReservedValue(); } @@ -73,7 +73,7 @@ bool ShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, const IR::U64 operand = v.V_scalar(esize, Vn); IR::U64 result = [&]() -> IR::U64 { - if (signedness == Signedness::Signed) { + if (SignednessSSSBI == SignednessSSSBI::Signed) { return v.ir.ArithmeticShiftRight(operand, v.ir.Imm8(shift_amount)); } return v.ir.LogicalShiftRight(operand, v.ir.Imm8(shift_amount)); @@ -88,7 +88,7 @@ bool ShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, return true; } -bool RoundingShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, ShiftExtraBehavior behavior, Signedness signedness) { +bool RoundingShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, ShiftExtraBehavior behavior, SignednessSSSBI SignednessSSSBI) { if (!immh.Bit<3>()) { return v.ReservedValue(); } @@ -100,7 +100,7 @@ bool RoundingShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, const IR::U64 round_bit = v.ir.LogicalShiftRight(v.ir.LogicalShiftLeft(operand, v.ir.Imm8(64 - shift_amount)), v.ir.Imm8(63)); const IR::U64 result = [&] { const IR::U64 shifted = [&]() -> IR::U64 { - if (signedness == Signedness::Signed) { + if (SignednessSSSBI == SignednessSSSBI::Signed) { return v.ir.ArithmeticShiftRight(operand, v.ir.Imm8(shift_amount)); } return v.ir.LogicalShiftRight(operand, v.ir.Imm8(shift_amount)); @@ -163,7 +163,7 @@ bool ShiftAndInsert(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec return true; } -bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Narrowing narrowing, Signedness signedness) { +bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Narrowing narrowing, SignednessSSSBI SignednessSSSBI) { if (immh == 0b0000) { return v.ReservedValue(); } @@ -179,7 +179,7 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, const IR::U128 operand = v.ir.ZeroExtendToQuad(v.ir.VectorGetElement(source_esize, v.V(128, Vn), 0)); IR::U128 wide_result = [&] { - if (signedness == Signedness::Signed) { + if (SignednessSSSBI == SignednessSSSBI::Signed) { return v.ir.VectorArithmeticShiftRight(source_esize, operand, shift_amount); } return v.ir.VectorLogicalShiftRight(source_esize, operand, shift_amount); @@ -190,12 +190,12 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, case Narrowing::Truncation: return v.ir.VectorNarrow(source_esize, wide_result); case Narrowing::SaturateToUnsigned: - if (signedness == Signedness::Signed) { + if (SignednessSSSBI == SignednessSSSBI::Signed) { return v.ir.VectorSignedSaturatedNarrowToUnsigned(source_esize, wide_result); } return v.ir.VectorUnsignedSaturatedNarrow(source_esize, wide_result); case Narrowing::SaturateToSigned: - ASSERT(signedness == Signedness::Signed); + ASSERT(SignednessSSSBI == SignednessSSSBI::Signed); return v.ir.VectorSignedSaturatedNarrowToSigned(source_esize, wide_result); } UNREACHABLE(); @@ -206,7 +206,7 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, return true; } -bool ScalarFPConvertWithRound(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Signedness sign, FloatConversionDirection direction, FP::RoundingMode rounding_mode) { +bool ScalarFPConvertWithRound(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SignednessSSSBI sign, FloatConversionDirection direction, FP::RoundingMode rounding_mode) { const u32 immh_value = immh.ZeroExtend(); if ((immh_value & 0b1110) == 0b0000) { @@ -227,23 +227,23 @@ bool ScalarFPConvertWithRound(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Ve switch (direction) { case FloatConversionDirection::FloatToFixed: if (esize == 64) { - return sign == Signedness::Signed + return sign == SignednessSSSBI::Signed ? v.ir.FPToFixedS64(operand, fbits, rounding_mode) : v.ir.FPToFixedU64(operand, fbits, rounding_mode); } - return sign == Signedness::Signed + return sign == SignednessSSSBI::Signed ? v.ir.FPToFixedS32(operand, fbits, rounding_mode) : v.ir.FPToFixedU32(operand, fbits, rounding_mode); case FloatConversionDirection::FixedToFloat: if (esize == 64) { - return sign == Signedness::Signed + return sign == SignednessSSSBI::Signed ? v.ir.FPSignedFixedToDouble(operand, fbits, rounding_mode) : v.ir.FPUnsignedFixedToDouble(operand, fbits, rounding_mode); } - return sign == Signedness::Signed + return sign == SignednessSSSBI::Signed ? v.ir.FPSignedFixedToSingle(operand, fbits, rounding_mode) : v.ir.FPUnsignedFixedToSingle(operand, fbits, rounding_mode); } @@ -257,19 +257,19 @@ bool ScalarFPConvertWithRound(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Ve } // Anonymous namespace bool TranslatorVisitor::FCVTZS_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); + return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, SignednessSSSBI::Signed, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); } bool TranslatorVisitor::FCVTZU_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); + return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, SignednessSSSBI::Unsigned, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); } bool TranslatorVisitor::SCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); + return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, SignednessSSSBI::Signed, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); } bool TranslatorVisitor::UCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); + return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, SignednessSSSBI::Unsigned, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); } bool TranslatorVisitor::SLI_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { @@ -289,27 +289,27 @@ bool TranslatorVisitor::SQSHLU_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToSigned, Signedness::Signed); + return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToSigned, SignednessSSSBI::Signed); } bool TranslatorVisitor::SQSHRUN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, Signedness::Signed); + return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, SignednessSSSBI::Signed); } bool TranslatorVisitor::SRSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Signed); + return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, SignednessSSSBI::Signed); } bool TranslatorVisitor::SRSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Signed); + return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, SignednessSSSBI::Signed); } bool TranslatorVisitor::SSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Signed); + return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, SignednessSSSBI::Signed); } bool TranslatorVisitor::SSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Signed); + return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, SignednessSSSBI::Signed); } bool TranslatorVisitor::SHL_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { @@ -332,23 +332,23 @@ bool TranslatorVisitor::UQSHL_imm_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { } bool TranslatorVisitor::UQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, Signedness::Unsigned); + return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, SignednessSSSBI::Unsigned); } bool TranslatorVisitor::URSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Unsigned); + return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, SignednessSSSBI::Unsigned); } bool TranslatorVisitor::URSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Unsigned); + return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, SignednessSSSBI::Unsigned); } bool TranslatorVisitor::USHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Unsigned); + return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, SignednessSSSBI::Unsigned); } bool TranslatorVisitor::USRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Unsigned); + return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, SignednessSSSBI::Unsigned); } } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_three_same.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_three_same.cpp index fb9ae9d141..d551605bda 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_three_same.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_three_same.cpp @@ -26,12 +26,12 @@ enum class ComparisonVariant { Zero, }; -enum class Signedness { +enum class SignednessSSTS { Signed, Unsigned, }; -bool RoundingShiftLeft(TranslatorVisitor& v, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Signedness sign) { +bool RoundingShiftLeft(TranslatorVisitor& v, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, SignednessSSTS sign) { if (size != 0b11) { return v.ReservedValue(); } @@ -39,7 +39,7 @@ bool RoundingShiftLeft(TranslatorVisitor& v, Imm<2> size, Vec Vm, Vec Vn, Vec Vd const IR::U128 operand1 = v.V(64, Vn); const IR::U128 operand2 = v.V(64, Vm); const IR::U128 result = [&] { - if (sign == Signedness::Signed) { + if (sign == SignednessSSTS::Signed) { return v.ir.VectorRoundingShiftLeftSigned(64, operand1, operand2); } @@ -369,7 +369,7 @@ bool TranslatorVisitor::SQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SRSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingShiftLeft(*this, size, Vm, Vn, Vd, Signedness::Signed); + return RoundingShiftLeft(*this, size, Vm, Vn, Vd, SignednessSSTS::Signed); } bool TranslatorVisitor::SSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -411,7 +411,7 @@ bool TranslatorVisitor::UQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::URSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingShiftLeft(*this, size, Vm, Vn, Vd, Signedness::Unsigned); + return RoundingShiftLeft(*this, size, Vm, Vn, Vd, SignednessSSTS::Unsigned); } bool TranslatorVisitor::USHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp index 2289f5cbd1..0fc37f538f 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp @@ -7,7 +7,7 @@ namespace Dynarmic::A64 { namespace { -enum class ComparisonType { +enum class ComparisonTypeSSTRM { EQ, GE, GT, @@ -15,12 +15,12 @@ enum class ComparisonType { LT }; -enum class Signedness { +enum class SignednessSSTRM { Signed, Unsigned }; -bool ScalarFPCompareAgainstZero(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, ComparisonType type) { +bool ScalarFPCompareAgainstZero(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, ComparisonTypeSSTRM type) { const size_t esize = sz ? 64 : 32; const size_t datasize = esize; @@ -28,15 +28,15 @@ bool ScalarFPCompareAgainstZero(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, C const IR::U128 zero = v.ir.ZeroVector(); const IR::U128 result = [&] { switch (type) { - case ComparisonType::EQ: + case ComparisonTypeSSTRM::EQ: return v.ir.FPVectorEqual(esize, operand, zero); - case ComparisonType::GE: + case ComparisonTypeSSTRM::GE: return v.ir.FPVectorGreaterEqual(esize, operand, zero); - case ComparisonType::GT: + case ComparisonTypeSSTRM::GT: return v.ir.FPVectorGreater(esize, operand, zero); - case ComparisonType::LE: + case ComparisonTypeSSTRM::LE: return v.ir.FPVectorGreaterEqual(esize, zero, operand); - case ComparisonType::LT: + case ComparisonTypeSSTRM::LT: return v.ir.FPVectorGreater(esize, zero, operand); } @@ -47,18 +47,18 @@ bool ScalarFPCompareAgainstZero(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, C return true; } -bool ScalarFPConvertWithRound(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, FP::RoundingMode rmode, Signedness sign) { +bool ScalarFPConvertWithRound(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, FP::RoundingMode rmode, SignednessSSTRM sign) { const size_t esize = sz ? 64 : 32; const IR::U32U64 operand = v.V_scalar(esize, Vn); const IR::U32U64 result = [&]() -> IR::U32U64 { if (sz) { - return sign == Signedness::Signed + return sign == SignednessSSTRM::Signed ? v.ir.FPToFixedS64(operand, 0, rmode) : v.ir.FPToFixedU64(operand, 0, rmode); } - return sign == Signedness::Signed + return sign == SignednessSSTRM::Signed ? v.ir.FPToFixedS32(operand, 0, rmode) : v.ir.FPToFixedU32(operand, 0, rmode); }(); @@ -107,55 +107,55 @@ bool TranslatorVisitor::FCMEQ_zero_1(Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCMEQ_zero_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::EQ); + return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonTypeSSTRM::EQ); } bool TranslatorVisitor::FCMGE_zero_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::GE); + return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonTypeSSTRM::GE); } bool TranslatorVisitor::FCMGT_zero_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::GT); + return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonTypeSSTRM::GT); } bool TranslatorVisitor::FCMLE_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::LE); + return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonTypeSSTRM::LE); } bool TranslatorVisitor::FCMLT_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::LT); + return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonTypeSSTRM::LT); } bool TranslatorVisitor::FCVTAS_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, Signedness::Signed); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, SignednessSSTRM::Signed); } bool TranslatorVisitor::FCVTAU_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, Signedness::Unsigned); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, SignednessSSTRM::Unsigned); } bool TranslatorVisitor::FCVTMS_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, Signedness::Signed); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, SignednessSSTRM::Signed); } bool TranslatorVisitor::FCVTMU_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, Signedness::Unsigned); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, SignednessSSTRM::Unsigned); } bool TranslatorVisitor::FCVTNS_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, Signedness::Signed); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, SignednessSSTRM::Signed); } bool TranslatorVisitor::FCVTNU_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, Signedness::Unsigned); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, SignednessSSTRM::Unsigned); } bool TranslatorVisitor::FCVTPS_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, Signedness::Signed); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, SignednessSSTRM::Signed); } bool TranslatorVisitor::FCVTPU_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, Signedness::Unsigned); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, SignednessSSTRM::Unsigned); } bool TranslatorVisitor::FCVTXN_1(bool sz, Vec Vn, Vec Vd) { @@ -171,11 +171,11 @@ bool TranslatorVisitor::FCVTXN_1(bool sz, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCVTZS_int_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, Signedness::Signed); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, SignednessSSTRM::Signed); } bool TranslatorVisitor::FCVTZU_int_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, Signedness::Unsigned); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, SignednessSSTRM::Unsigned); } bool TranslatorVisitor::FRECPE_1(Vec Vn, Vec Vd) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp index dbbc4ce12c..7a5d9847d7 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp @@ -9,7 +9,7 @@ namespace Dynarmic::A64 { namespace { -std::pair Combine(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> Vmlo) { +std::pair CombineScalar(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> Vmlo) { if (size == 0b01) { return {concatenate(H, L, M).ZeroExtend(), Vmlo.ZeroExtend()}; } @@ -122,7 +122,7 @@ bool TranslatorVisitor::SQDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vm } const size_t esize = 8 << size.ZeroExtend(); - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineScalar(size, H, L, M, Vmlo); const IR::UAny operand1 = V_scalar(esize, Vn); const IR::UAny operand2 = ir.VectorGetElement(esize, V(128, Vm), index); @@ -137,7 +137,7 @@ bool TranslatorVisitor::SQRDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> V } const size_t esize = 8 << size.ZeroExtend(); - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineScalar(size, H, L, M, Vmlo); const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0)); const IR::U128 operand2 = V(128, Vm); @@ -154,7 +154,7 @@ bool TranslatorVisitor::SQDMULL_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vm } const size_t esize = 8 << size.ZeroExtend(); - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineScalar(size, H, L, M, Vmlo); const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0)); const IR::U128 operand2 = V(128, Vm); diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_shift_by_immediate.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_shift_by_immediate.cpp index 41b0952249..559721a22a 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_shift_by_immediate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_shift_by_immediate.cpp @@ -20,24 +20,24 @@ enum class Accumulating { Accumulate }; -enum class Signedness { +enum class SignednessSSBI { Signed, Unsigned }; -enum class Narrowing { +enum class NarrowingSSBI { Truncation, SaturateToUnsigned, SaturateToSigned, }; -enum class SaturatingShiftLeftType { +enum class SaturatingShiftLeftTypeSSBI { Signed, Unsigned, SignedWithUnsignedSaturation, }; -enum class FloatConversionDirection { +enum class FloatConversionDirectionSSBI { FixedToFloat, FloatToFixed, }; @@ -48,7 +48,7 @@ IR::U128 PerformRoundingCorrection(TranslatorVisitor& v, size_t esize, u64 round return v.ir.VectorSub(esize, shifted, round_correction); } -bool ShiftRight(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Rounding rounding, Accumulating accumulating, Signedness signedness) { +bool ShiftRight(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Rounding rounding, Accumulating accumulating, SignednessSSBI SignednessSSBI) { if (immh == 0b0000) { return v.DecodeError(); } @@ -65,7 +65,7 @@ bool ShiftRight(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, const IR::U128 operand = v.V(datasize, Vn); IR::U128 result = [&] { - if (signedness == Signedness::Signed) { + if (SignednessSSBI == SignednessSSBI::Signed) { return v.ir.VectorArithmeticShiftRight(esize, operand, shift_amount); } return v.ir.VectorLogicalShiftRight(esize, operand, shift_amount); @@ -85,7 +85,7 @@ bool ShiftRight(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, return true; } -bool ShiftRightNarrowing(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Rounding rounding, Narrowing narrowing, Signedness signedness) { +bool ShiftRightNarrowingSSBI(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Rounding rounding, NarrowingSSBI NarrowingSSBI, SignednessSSBI SignednessSSBI) { if (immh == 0b0000) { return v.DecodeError(); } @@ -103,7 +103,7 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, const IR::U128 operand = v.V(128, Vn); IR::U128 wide_result = [&] { - if (signedness == Signedness::Signed) { + if (SignednessSSBI == SignednessSSBI::Signed) { return v.ir.VectorArithmeticShiftRight(source_esize, operand, shift_amount); } return v.ir.VectorLogicalShiftRight(source_esize, operand, shift_amount); @@ -115,16 +115,16 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, } const IR::U128 result = [&] { - switch (narrowing) { - case Narrowing::Truncation: + switch (NarrowingSSBI) { + case NarrowingSSBI::Truncation: return v.ir.VectorNarrow(source_esize, wide_result); - case Narrowing::SaturateToUnsigned: - if (signedness == Signedness::Signed) { + case NarrowingSSBI::SaturateToUnsigned: + if (SignednessSSBI == SignednessSSBI::Signed) { return v.ir.VectorSignedSaturatedNarrowToUnsigned(source_esize, wide_result); } return v.ir.VectorUnsignedSaturatedNarrow(source_esize, wide_result); - case Narrowing::SaturateToSigned: - ASSERT(signedness == Signedness::Signed); + case NarrowingSSBI::SaturateToSigned: + ASSERT(SignednessSSBI == SignednessSSBI::Signed); return v.ir.VectorSignedSaturatedNarrowToSigned(source_esize, wide_result); } UNREACHABLE(); @@ -134,7 +134,7 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, return true; } -bool ShiftLeftLong(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Signedness signedness) { +bool ShiftLeftLong(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SignednessSSBI SignednessSSBI) { if (immh == 0b0000) { return v.DecodeError(); } @@ -151,7 +151,7 @@ bool ShiftLeftLong(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec V const IR::U128 operand = v.Vpart(datasize, Vn, part); const IR::U128 expanded_operand = [&] { - if (signedness == Signedness::Signed) { + if (SignednessSSBI == SignednessSSBI::Signed) { return v.ir.VectorSignExtend(esize, operand); } return v.ir.VectorZeroExtend(esize, operand); @@ -162,7 +162,7 @@ bool ShiftLeftLong(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec V return true; } -bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SaturatingShiftLeftType type) { +bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SaturatingShiftLeftTypeSSBI type) { if (!Q && immh.Bit<3>()) { return v.ReservedValue(); } @@ -174,11 +174,11 @@ bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, const IR::U128 operand = v.V(datasize, Vn); const IR::U128 shift_vec = v.ir.VectorBroadcast(esize, v.I(esize, shift)); const IR::U128 result = [&] { - if (type == SaturatingShiftLeftType::Signed) { + if (type == SaturatingShiftLeftTypeSSBI::Signed) { return v.ir.VectorSignedSaturatedShiftLeft(esize, operand, shift_vec); } - if (type == SaturatingShiftLeftType::Unsigned) { + if (type == SaturatingShiftLeftTypeSSBI::Unsigned) { return v.ir.VectorUnsignedSaturatedShiftLeft(esize, operand, shift_vec); } @@ -189,7 +189,7 @@ bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, return true; } -bool ConvertFloat(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Signedness signedness, FloatConversionDirection direction, FP::RoundingMode rounding_mode) { +bool ConvertFloat(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SignednessSSBI SignednessSSBI, FloatConversionDirectionSSBI direction, FP::RoundingMode rounding_mode) { if (immh == 0b0000) { return v.DecodeError(); } @@ -210,12 +210,12 @@ bool ConvertFloat(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn const IR::U128 operand = v.V(datasize, Vn); const IR::U128 result = [&] { switch (direction) { - case FloatConversionDirection::FixedToFloat: - return signedness == Signedness::Signed + case FloatConversionDirectionSSBI::FixedToFloat: + return SignednessSSBI == SignednessSSBI::Signed ? v.ir.FPVectorFromSignedFixed(esize, operand, fbits, rounding_mode) : v.ir.FPVectorFromUnsignedFixed(esize, operand, fbits, rounding_mode); - case FloatConversionDirection::FloatToFixed: - return signedness == Signedness::Signed + case FloatConversionDirectionSSBI::FloatToFixed: + return SignednessSSBI == SignednessSSBI::Signed ? v.ir.FPVectorToSignedFixed(esize, operand, fbits, rounding_mode) : v.ir.FPVectorToUnsignedFixed(esize, operand, fbits, rounding_mode); } @@ -229,19 +229,19 @@ bool ConvertFloat(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn } // Anonymous namespace bool TranslatorVisitor::SSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, Signedness::Signed); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, SignednessSSBI::Signed); } bool TranslatorVisitor::SRSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, Signedness::Signed); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, SignednessSSBI::Signed); } bool TranslatorVisitor::SRSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, Signedness::Signed); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, SignednessSSBI::Signed); } bool TranslatorVisitor::SSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, Signedness::Signed); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, SignednessSSBI::Signed); } bool TranslatorVisitor::SHL_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { @@ -264,71 +264,71 @@ bool TranslatorVisitor::SHL_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) } bool TranslatorVisitor::SHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::Truncation, Signedness::Unsigned); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::None, NarrowingSSBI::Truncation, SignednessSSBI::Unsigned); } bool TranslatorVisitor::RSHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::Truncation, Signedness::Unsigned); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::Round, NarrowingSSBI::Truncation, SignednessSSBI::Unsigned); } bool TranslatorVisitor::SQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::Signed); + return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftTypeSSBI::Signed); } bool TranslatorVisitor::SQSHLU_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::SignedWithUnsignedSaturation); + return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftTypeSSBI::SignedWithUnsignedSaturation); } bool TranslatorVisitor::SQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToSigned, Signedness::Signed); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::None, NarrowingSSBI::SaturateToSigned, SignednessSSBI::Signed); } bool TranslatorVisitor::SQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToSigned, Signedness::Signed); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::Round, NarrowingSSBI::SaturateToSigned, SignednessSSBI::Signed); } bool TranslatorVisitor::SQSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Signed); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::None, NarrowingSSBI::SaturateToUnsigned, SignednessSSBI::Signed); } bool TranslatorVisitor::SQRSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Signed); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::Round, NarrowingSSBI::SaturateToUnsigned, SignednessSSBI::Signed); } bool TranslatorVisitor::UQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::Unsigned); + return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftTypeSSBI::Unsigned); } bool TranslatorVisitor::UQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Unsigned); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::None, NarrowingSSBI::SaturateToUnsigned, SignednessSSBI::Unsigned); } bool TranslatorVisitor::UQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Unsigned); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::Round, NarrowingSSBI::SaturateToUnsigned, SignednessSSBI::Unsigned); } bool TranslatorVisitor::SSHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, Signedness::Signed); + return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Signed); } bool TranslatorVisitor::URSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, Signedness::Unsigned); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, SignednessSSBI::Unsigned); } bool TranslatorVisitor::URSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, Signedness::Unsigned); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, SignednessSSBI::Unsigned); } bool TranslatorVisitor::USHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, Signedness::Unsigned); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, SignednessSSBI::Unsigned); } bool TranslatorVisitor::USRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, Signedness::Unsigned); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, SignednessSSBI::Unsigned); } bool TranslatorVisitor::USHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned); + return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Unsigned); } bool TranslatorVisitor::SRI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { @@ -384,19 +384,19 @@ bool TranslatorVisitor::SLI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) } bool TranslatorVisitor::SCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); + return ConvertFloat(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Signed, FloatConversionDirectionSSBI::FixedToFloat, ir.current_location->FPCR().RMode()); } bool TranslatorVisitor::UCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); + return ConvertFloat(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Unsigned, FloatConversionDirectionSSBI::FixedToFloat, ir.current_location->FPCR().RMode()); } bool TranslatorVisitor::FCVTZS_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); + return ConvertFloat(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Signed, FloatConversionDirectionSSBI::FloatToFixed, FP::RoundingMode::TowardsZero); } bool TranslatorVisitor::FCVTZU_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); + return ConvertFloat(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Unsigned, FloatConversionDirectionSSBI::FloatToFixed, FP::RoundingMode::TowardsZero); } } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_different.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_different.cpp index 8cc677b652..8f460665da 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_different.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_different.cpp @@ -12,12 +12,12 @@ enum class AbsoluteDifferenceBehavior { Accumulate }; -enum class Signedness { +enum class SignednessSTD { Signed, Unsigned }; -bool AbsoluteDifferenceLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, AbsoluteDifferenceBehavior behavior, Signedness sign) { +bool AbsoluteDifferenceLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, AbsoluteDifferenceBehavior behavior, SignednessSTD sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -27,7 +27,7 @@ bool AbsoluteDifferenceLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, V const IR::U128 operand1 = v.ir.VectorZeroExtend(esize, v.Vpart(datasize, Vn, Q)); const IR::U128 operand2 = v.ir.VectorZeroExtend(esize, v.Vpart(datasize, Vm, Q)); - IR::U128 result = sign == Signedness::Signed ? v.ir.VectorSignedAbsoluteDifference(esize, operand1, operand2) + IR::U128 result = sign == SignednessSTD::Signed ? v.ir.VectorSignedAbsoluteDifference(esize, operand1, operand2) : v.ir.VectorUnsignedAbsoluteDifference(esize, operand1, operand2); if (behavior == AbsoluteDifferenceBehavior::Accumulate) { @@ -45,7 +45,7 @@ enum class MultiplyLongBehavior { Subtract }; -bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MultiplyLongBehavior behavior, Signedness sign) { +bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MultiplyLongBehavior behavior, SignednessSTD sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -59,7 +59,7 @@ bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec const auto reg_n = v.Vpart(datasize, Vn, Q); const auto reg_m = v.Vpart(datasize, Vm, Q); - return sign == Signedness::Signed + return sign == SignednessSTD::Signed ? v.ir.VectorMultiplySignedWiden(esize, reg_n, reg_m) : v.ir.VectorMultiplyUnsignedWiden(esize, reg_n, reg_m); }(); @@ -81,7 +81,7 @@ enum class LongOperationBehavior { Subtraction }; -bool LongOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, LongOperationBehavior behavior, Signedness sign) { +bool LongOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, LongOperationBehavior behavior, SignednessSTD sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -92,7 +92,7 @@ bool LongOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Ve const auto get_operand = [&](Vec vec) { const IR::U128 tmp = v.Vpart(64, vec, part); - if (sign == Signedness::Signed) { + if (sign == SignednessSTD::Signed) { return v.ir.VectorSignExtend(esize, tmp); } @@ -118,7 +118,7 @@ enum class WideOperationBehavior { Subtraction }; -bool WideOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, WideOperationBehavior behavior, Signedness sign) { +bool WideOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, WideOperationBehavior behavior, SignednessSTD sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -130,7 +130,7 @@ bool WideOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Ve const IR::U128 operand2 = [&] { const IR::U128 tmp = v.Vpart(64, Vm, part); - if (sign == Signedness::Signed) { + if (sign == SignednessSTD::Signed) { return v.ir.VectorSignExtend(esize, tmp); } @@ -166,75 +166,75 @@ bool TranslatorVisitor::PMULL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SABAL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::Accumulate, Signedness::Signed); + return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::Accumulate, SignednessSTD::Signed); } bool TranslatorVisitor::SABDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::None, Signedness::Signed); + return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::None, SignednessSTD::Signed); } bool TranslatorVisitor::SADDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Addition, Signedness::Signed); + return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Addition, SignednessSTD::Signed); } bool TranslatorVisitor::SADDW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Addition, Signedness::Signed); + return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Addition, SignednessSTD::Signed); } bool TranslatorVisitor::SMLAL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Accumulate, Signedness::Signed); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Accumulate, SignednessSTD::Signed); } bool TranslatorVisitor::SMLSL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Subtract, Signedness::Signed); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Subtract, SignednessSTD::Signed); } bool TranslatorVisitor::SMULL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::None, Signedness::Signed); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::None, SignednessSTD::Signed); } bool TranslatorVisitor::SSUBW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Subtraction, Signedness::Signed); + return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Subtraction, SignednessSTD::Signed); } bool TranslatorVisitor::SSUBL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Subtraction, Signedness::Signed); + return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Subtraction, SignednessSTD::Signed); } bool TranslatorVisitor::UADDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Addition, Signedness::Unsigned); + return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Addition, SignednessSTD::Unsigned); } bool TranslatorVisitor::UABAL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::Accumulate, Signedness::Unsigned); + return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::Accumulate, SignednessSTD::Unsigned); } bool TranslatorVisitor::UABDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::None, Signedness::Unsigned); + return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::None, SignednessSTD::Unsigned); } bool TranslatorVisitor::UADDW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Addition, Signedness::Unsigned); + return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Addition, SignednessSTD::Unsigned); } bool TranslatorVisitor::UMLAL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Accumulate, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Accumulate, SignednessSTD::Unsigned); } bool TranslatorVisitor::UMLSL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Subtract, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Subtract, SignednessSTD::Unsigned); } bool TranslatorVisitor::UMULL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::None, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::None, SignednessSTD::Unsigned); } bool TranslatorVisitor::USUBW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Subtraction, Signedness::Unsigned); + return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Subtraction, SignednessSTD::Unsigned); } bool TranslatorVisitor::USUBL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Subtraction, Signedness::Unsigned); + return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Subtraction, SignednessSTD::Unsigned); } bool TranslatorVisitor::SQDMULL_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_same.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_same.cpp index 5c8bf13aeb..1cfc2ced78 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_same.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_same.cpp @@ -12,12 +12,12 @@ enum class Operation { Subtract, }; -enum class ExtraBehavior { +enum class ExtraBehaviorSTS { None, Round }; -bool HighNarrowingOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Operation op, ExtraBehavior behavior) { +bool HighNarrowingOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Operation op, ExtraBehaviorSTS behavior) { if (size == 0b11) { return v.ReservedValue(); } @@ -35,7 +35,7 @@ bool HighNarrowingOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, V return v.ir.VectorSub(doubled_esize, operand1, operand2); }(); - if (behavior == ExtraBehavior::Round) { + if (behavior == ExtraBehaviorSTS::Round) { const u64 round_const = 1ULL << (esize - 1); const IR::U128 round_operand = v.ir.VectorBroadcast(doubled_esize, v.I(doubled_esize, round_const)); wide = v.ir.VectorAdd(doubled_esize, wide, round_operand); @@ -48,12 +48,12 @@ bool HighNarrowingOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, V return true; } -enum class AbsDiffExtraBehavior { +enum class AbsDiffExtraBehaviorSTS { None, Accumulate }; -bool SignedAbsoluteDifference(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, AbsDiffExtraBehavior behavior) { +bool SignedAbsoluteDifference(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, AbsDiffExtraBehaviorSTS behavior) { if (size == 0b11) { return v.ReservedValue(); } @@ -66,7 +66,7 @@ bool SignedAbsoluteDifference(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, const IR::U128 result = [&] { const IR::U128 tmp = v.ir.VectorSignedAbsoluteDifference(esize, operand1, operand2); - if (behavior == AbsDiffExtraBehavior::Accumulate) { + if (behavior == AbsDiffExtraBehaviorSTS::Accumulate) { const IR::U128 d = v.V(datasize, Vd); return v.ir.VectorAdd(esize, d, tmp); } @@ -78,12 +78,12 @@ bool SignedAbsoluteDifference(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, return true; } -enum class Signedness { +enum class SignednessSTS { Signed, Unsigned }; -bool RoundingHalvingAdd(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Signedness sign) { +bool RoundingHalvingAdd(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, SignednessSTS sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -93,14 +93,14 @@ bool RoundingHalvingAdd(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec V const IR::U128 operand1 = v.V(datasize, Vm); const IR::U128 operand2 = v.V(datasize, Vn); - const IR::U128 result = sign == Signedness::Signed ? v.ir.VectorRoundingHalvingAddSigned(esize, operand1, operand2) + const IR::U128 result = sign == SignednessSTS::Signed ? v.ir.VectorRoundingHalvingAddSigned(esize, operand1, operand2) : v.ir.VectorRoundingHalvingAddUnsigned(esize, operand1, operand2); v.V(datasize, Vd, result); return true; } -bool RoundingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Signedness sign) { +bool RoundingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, SignednessSTS sign) { if (size == 0b11 && !Q) { return v.ReservedValue(); } @@ -111,7 +111,7 @@ bool RoundingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { - if (sign == Signedness::Signed) { + if (sign == SignednessSTS::Signed) { return v.ir.VectorRoundingShiftLeftSigned(esize, operand1, operand2); } @@ -122,7 +122,7 @@ bool RoundingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn return true; } -enum class ComparisonType { +enum class ComparisonTypeSTS { EQ, GE, AbsoluteGE, @@ -130,7 +130,7 @@ enum class ComparisonType { AbsoluteGT }; -bool FPCompareRegister(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, ComparisonType type) { +bool FPCompareRegister(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, ComparisonTypeSTS type) { if (sz && !Q) { return v.ReservedValue(); } @@ -142,17 +142,17 @@ bool FPCompareRegister(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Ve const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { switch (type) { - case ComparisonType::EQ: + case ComparisonTypeSTS::EQ: return v.ir.FPVectorEqual(esize, operand1, operand2); - case ComparisonType::GE: + case ComparisonTypeSTS::GE: return v.ir.FPVectorGreaterEqual(esize, operand1, operand2); - case ComparisonType::AbsoluteGE: + case ComparisonTypeSTS::AbsoluteGE: return v.ir.FPVectorGreaterEqual(esize, v.ir.FPVectorAbs(esize, operand1), v.ir.FPVectorAbs(esize, operand2)); - case ComparisonType::GT: + case ComparisonTypeSTS::GT: return v.ir.FPVectorGreater(esize, operand1, operand2); - case ComparisonType::AbsoluteGT: + case ComparisonTypeSTS::AbsoluteGT: return v.ir.FPVectorGreater(esize, v.ir.FPVectorAbs(esize, operand1), v.ir.FPVectorAbs(esize, operand2)); @@ -165,12 +165,12 @@ bool FPCompareRegister(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Ve return true; } -enum class MinMaxOperation { +enum class MinMaxOperationSTS { Min, Max, }; -bool VectorMinMaxOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MinMaxOperation operation, Signedness sign) { +bool VectorMinMaxOperationSTS(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MinMaxOperationSTS operation, SignednessSTS sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -182,14 +182,14 @@ bool VectorMinMaxOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Ve const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { switch (operation) { - case MinMaxOperation::Max: - if (sign == Signedness::Signed) { + case MinMaxOperationSTS::Max: + if (sign == SignednessSTS::Signed) { return v.ir.VectorMaxSigned(esize, operand1, operand2); } return v.ir.VectorMaxUnsigned(esize, operand1, operand2); - case MinMaxOperation::Min: - if (sign == Signedness::Signed) { + case MinMaxOperationSTS::Min: + if (sign == SignednessSTS::Signed) { return v.ir.VectorMinSigned(esize, operand1, operand2); } return v.ir.VectorMinUnsigned(esize, operand1, operand2); @@ -203,7 +203,7 @@ bool VectorMinMaxOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Ve return true; } -bool FPMinMaxOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, MinMaxOperation operation) { +bool FPMinMaxOperationSTS(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, MinMaxOperationSTS operation) { if (sz && !Q) { return v.ReservedValue(); } @@ -214,7 +214,7 @@ bool FPMinMaxOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Ve const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { - if (operation == MinMaxOperation::Min) { + if (operation == MinMaxOperationSTS::Min) { return v.ir.FPVectorMin(esize, operand1, operand2); } @@ -225,7 +225,7 @@ bool FPMinMaxOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Ve return true; } -bool FPMinMaxNumericOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, MinMaxOperation operation) { +bool FPMinMaxNumericOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, MinMaxOperationSTS operation) { if (sz && !Q) { return v.ReservedValue(); } @@ -236,7 +236,7 @@ bool FPMinMaxNumericOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { - if (operation == MinMaxOperation::Min) { + if (operation == MinMaxOperationSTS::Min) { return v.ir.FPVectorMinNumeric(esize, operand1, operand2); } @@ -247,7 +247,7 @@ bool FPMinMaxNumericOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec return true; } -bool PairedMinMaxOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MinMaxOperation operation, Signedness sign) { +bool PairedMinMaxOperationSTS(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MinMaxOperationSTS operation, SignednessSTS sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -259,14 +259,14 @@ bool PairedMinMaxOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Ve const IR::U128 operand2 = v.V(datasize, Vm); IR::U128 result = [&] { switch (operation) { - case MinMaxOperation::Max: - if (sign == Signedness::Signed) { + case MinMaxOperationSTS::Max: + if (sign == SignednessSTS::Signed) { return Q ? v.ir.VectorPairedMaxSigned(esize, operand1, operand2) : v.ir.VectorPairedMaxSignedLower(esize, operand1, operand2); } return Q ? v.ir.VectorPairedMaxUnsigned(esize, operand1, operand2) : v.ir.VectorPairedMaxUnsignedLower(esize, operand1, operand2); - case MinMaxOperation::Min: - if (sign == Signedness::Signed) { + case MinMaxOperationSTS::Min: + if (sign == SignednessSTS::Signed) { return Q ? v.ir.VectorPairedMinSigned(esize, operand1, operand2) : v.ir.VectorPairedMinSignedLower(esize, operand1, operand2); } return Q ? v.ir.VectorPairedMinUnsigned(esize, operand1, operand2) : v.ir.VectorPairedMinUnsignedLower(esize, operand1, operand2); @@ -311,7 +311,7 @@ bool FPPairedMinMax(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec V return true; } -bool SaturatingArithmeticOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Operation op, Signedness sign) { +bool SaturatingArithmeticOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Operation op, SignednessSTS sign) { if (size == 0b11 && !Q) { return v.ReservedValue(); } @@ -323,7 +323,7 @@ bool SaturatingArithmeticOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Ve const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { - if (sign == Signedness::Signed) { + if (sign == SignednessSTS::Signed) { if (op == Operation::Add) { return v.ir.VectorSignedSaturatedAdd(esize, operand1, operand2); } @@ -342,7 +342,7 @@ bool SaturatingArithmeticOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Ve return true; } -bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Signedness sign) { +bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, SignednessSTS sign) { if (size == 0b11 && !Q) { return v.ReservedValue(); } @@ -353,7 +353,7 @@ bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { - if (sign == Signedness::Signed) { + if (sign == SignednessSTS::Signed) { return v.ir.VectorSignedSaturatedShiftLeft(esize, operand1, operand2); } @@ -401,27 +401,27 @@ bool TranslatorVisitor::CMGE_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) } bool TranslatorVisitor::SABA(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SignedAbsoluteDifference(*this, Q, size, Vm, Vn, Vd, AbsDiffExtraBehavior::Accumulate); + return SignedAbsoluteDifference(*this, Q, size, Vm, Vn, Vd, AbsDiffExtraBehaviorSTS::Accumulate); } bool TranslatorVisitor::SABD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SignedAbsoluteDifference(*this, Q, size, Vm, Vn, Vd, AbsDiffExtraBehavior::None); + return SignedAbsoluteDifference(*this, Q, size, Vm, Vn, Vd, AbsDiffExtraBehaviorSTS::None); } bool TranslatorVisitor::SMAX(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return VectorMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Max, Signedness::Signed); + return VectorMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Max, SignednessSTS::Signed); } bool TranslatorVisitor::SMAXP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return PairedMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Max, Signedness::Signed); + return PairedMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Max, SignednessSTS::Signed); } bool TranslatorVisitor::SMIN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return VectorMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Min, Signedness::Signed); + return VectorMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Min, SignednessSTS::Signed); } bool TranslatorVisitor::SMINP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return PairedMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Min, Signedness::Signed); + return PairedMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Min, SignednessSTS::Signed); } bool TranslatorVisitor::SQDMULH_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -506,19 +506,19 @@ bool TranslatorVisitor::MUL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::ADDHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, ExtraBehavior::None); + return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, ExtraBehaviorSTS::None); } bool TranslatorVisitor::RADDHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, ExtraBehavior::Round); + return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, ExtraBehaviorSTS::Round); } bool TranslatorVisitor::SUBHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, ExtraBehavior::None); + return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, ExtraBehaviorSTS::None); } bool TranslatorVisitor::RSUBHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, ExtraBehavior::Round); + return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, ExtraBehaviorSTS::Round); } bool TranslatorVisitor::SHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -554,15 +554,15 @@ bool TranslatorVisitor::SHSUB(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SQADD_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, Signedness::Signed); + return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, SignednessSTS::Signed); } bool TranslatorVisitor::SQSUB_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, Signedness::Signed); + return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, SignednessSTS::Signed); } bool TranslatorVisitor::SRHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingHalvingAdd(*this, Q, size, Vm, Vn, Vd, Signedness::Signed); + return RoundingHalvingAdd(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Signed); } bool TranslatorVisitor::UHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -598,15 +598,15 @@ bool TranslatorVisitor::UHSUB(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::UQADD_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, Signedness::Unsigned); + return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, SignednessSTS::Unsigned); } bool TranslatorVisitor::UQSUB_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, Signedness::Unsigned); + return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, SignednessSTS::Unsigned); } bool TranslatorVisitor::URHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingHalvingAdd(*this, Q, size, Vm, Vn, Vd, Signedness::Unsigned); + return RoundingHalvingAdd(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Unsigned); } bool TranslatorVisitor::ADDP_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -642,11 +642,11 @@ bool TranslatorVisitor::FABD_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FACGE_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonType::AbsoluteGE); + return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonTypeSTS::AbsoluteGE); } bool TranslatorVisitor::FACGT_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonType::AbsoluteGT); + return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonTypeSTS::AbsoluteGT); } bool TranslatorVisitor::FADD_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { @@ -737,15 +737,15 @@ bool TranslatorVisitor::FCMEQ_reg_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCMEQ_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonType::EQ); + return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonTypeSTS::EQ); } bool TranslatorVisitor::FCMGE_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonType::GE); + return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonTypeSTS::GE); } bool TranslatorVisitor::FCMGT_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonType::GT); + return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonTypeSTS::GT); } bool TranslatorVisitor::AND_asimd(bool Q, Vec Vm, Vec Vn, Vec Vd) { @@ -827,11 +827,11 @@ bool TranslatorVisitor::CMTST_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SQSHL_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingShiftLeft(*this, Q, size, Vm, Vn, Vd, Signedness::Signed); + return SaturatingShiftLeft(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Signed); } bool TranslatorVisitor::SRSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingShiftLeft(*this, Q, size, Vm, Vn, Vd, Signedness::Signed); + return RoundingShiftLeft(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Signed); } bool TranslatorVisitor::SSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -851,11 +851,11 @@ bool TranslatorVisitor::SSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::UQSHL_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingShiftLeft(*this, Q, size, Vm, Vn, Vd, Signedness::Unsigned); + return SaturatingShiftLeft(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Unsigned); } bool TranslatorVisitor::URSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingShiftLeft(*this, Q, size, Vm, Vn, Vd, Signedness::Unsigned); + return RoundingShiftLeft(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Unsigned); } bool TranslatorVisitor::USHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -875,11 +875,11 @@ bool TranslatorVisitor::USHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::UMAX(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return VectorMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Max, Signedness::Unsigned); + return VectorMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Max, SignednessSTS::Unsigned); } bool TranslatorVisitor::UMAXP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return PairedMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Max, Signedness::Unsigned); + return PairedMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Max, SignednessSTS::Unsigned); } bool TranslatorVisitor::UABA(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -918,11 +918,11 @@ bool TranslatorVisitor::UABD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::UMIN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return VectorMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Min, Signedness::Unsigned); + return VectorMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Min, SignednessSTS::Unsigned); } bool TranslatorVisitor::UMINP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return PairedMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Min, Signedness::Unsigned); + return PairedMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Min, SignednessSTS::Unsigned); } bool TranslatorVisitor::FSUB_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { @@ -1104,11 +1104,11 @@ bool TranslatorVisitor::EOR_asimd(bool Q, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FMAX_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPMinMaxOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperation::Max); + return FPMinMaxOperationSTS(*this, Q, sz, Vm, Vn, Vd, MinMaxOperationSTS::Max); } bool TranslatorVisitor::FMAXNM_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPMinMaxNumericOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperation::Max); + return FPMinMaxNumericOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperationSTS::Max); } bool TranslatorVisitor::FMAXNMP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { @@ -1120,11 +1120,11 @@ bool TranslatorVisitor::FMAXP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FMIN_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPMinMaxOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperation::Min); + return FPMinMaxOperationSTS(*this, Q, sz, Vm, Vn, Vd, MinMaxOperationSTS::Min); } bool TranslatorVisitor::FMINNM_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPMinMaxNumericOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperation::Min); + return FPMinMaxNumericOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperationSTS::Min); } bool TranslatorVisitor::FMINNMP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_two_register_misc.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_two_register_misc.cpp index ca3e3b9591..80a8e531a7 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_two_register_misc.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_two_register_misc.cpp @@ -8,7 +8,7 @@ namespace Dynarmic::A64 { namespace { -enum class ComparisonType { +enum class ComparisonTypeSTRM { EQ, GE, GT, @@ -16,7 +16,7 @@ enum class ComparisonType { LT, }; -bool CompareAgainstZero(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, ComparisonType type) { +bool CompareAgainstZero(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, ComparisonTypeSTRM type) { if (size == 0b11 && !Q) { return v.ReservedValue(); } @@ -28,15 +28,15 @@ bool CompareAgainstZero(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec V const IR::U128 zero = v.ir.ZeroVector(); IR::U128 result = [&] { switch (type) { - case ComparisonType::EQ: + case ComparisonTypeSTRM::EQ: return v.ir.VectorEqual(esize, operand, zero); - case ComparisonType::GE: + case ComparisonTypeSTRM::GE: return v.ir.VectorGreaterEqualSigned(esize, operand, zero); - case ComparisonType::GT: + case ComparisonTypeSTRM::GT: return v.ir.VectorGreaterSigned(esize, operand, zero); - case ComparisonType::LE: + case ComparisonTypeSTRM::LE: return v.ir.VectorLessEqualSigned(esize, operand, zero); - case ComparisonType::LT: + case ComparisonTypeSTRM::LT: default: return v.ir.VectorLessSigned(esize, operand, zero); } @@ -50,7 +50,7 @@ bool CompareAgainstZero(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec V return true; } -bool FPCompareAgainstZero(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, ComparisonType type) { +bool FPCompareAgainstZero(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, ComparisonTypeSTRM type) { if (sz && !Q) { return v.ReservedValue(); } @@ -62,15 +62,15 @@ bool FPCompareAgainstZero(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, const IR::U128 zero = v.ir.ZeroVector(); const IR::U128 result = [&] { switch (type) { - case ComparisonType::EQ: + case ComparisonTypeSTRM::EQ: return v.ir.FPVectorEqual(esize, operand, zero); - case ComparisonType::GE: + case ComparisonTypeSTRM::GE: return v.ir.FPVectorGreaterEqual(esize, operand, zero); - case ComparisonType::GT: + case ComparisonTypeSTRM::GT: return v.ir.FPVectorGreater(esize, operand, zero); - case ComparisonType::LE: + case ComparisonTypeSTRM::LE: return v.ir.FPVectorGreaterEqual(esize, zero, operand); - case ComparisonType::LT: + case ComparisonTypeSTRM::LT: return v.ir.FPVectorGreater(esize, zero, operand); } @@ -81,12 +81,12 @@ bool FPCompareAgainstZero(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, return true; } -enum class Signedness { +enum class SignednessSTRM { Signed, Unsigned }; -bool IntegerConvertToFloat(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, Signedness signedness) { +bool IntegerConvertToFloat(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, SignednessSTRM SignednessSTRM) { if (sz && !Q) { return v.ReservedValue(); } @@ -96,7 +96,7 @@ bool IntegerConvertToFloat(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd const FP::RoundingMode rounding_mode = v.ir.current_location->FPCR().RMode(); const IR::U128 operand = v.V(datasize, Vn); - const IR::U128 result = signedness == Signedness::Signed + const IR::U128 result = SignednessSTRM == SignednessSTRM::Signed ? v.ir.FPVectorFromSignedFixed(esize, operand, 0, rounding_mode) : v.ir.FPVectorFromUnsignedFixed(esize, operand, 0, rounding_mode); @@ -104,7 +104,7 @@ bool IntegerConvertToFloat(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd return true; } -bool FloatConvertToInteger(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, Signedness signedness, FP::RoundingMode rounding_mode) { +bool FloatConvertToInteger(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, SignednessSTRM SignednessSTRM, FP::RoundingMode rounding_mode) { if (sz && !Q) { return v.ReservedValue(); } @@ -113,7 +113,7 @@ bool FloatConvertToInteger(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd const size_t esize = sz ? 64 : 32; const IR::U128 operand = v.V(datasize, Vn); - const IR::U128 result = signedness == Signedness::Signed + const IR::U128 result = SignednessSTRM == SignednessSTRM::Signed ? v.ir.FPVectorToSignedFixed(esize, operand, 0, rounding_mode) : v.ir.FPVectorToUnsignedFixed(esize, operand, 0, rounding_mode); @@ -168,7 +168,7 @@ enum class PairedAddLongExtraBehavior { Accumulate, }; -bool PairedAddLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, Signedness sign, PairedAddLongExtraBehavior behavior) { +bool PairedAddLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, SignednessSTRM sign, PairedAddLongExtraBehavior behavior) { if (size == 0b11) { return v.ReservedValue(); } @@ -178,7 +178,7 @@ bool PairedAddLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, Si const IR::U128 operand = v.V(datasize, Vn); IR::U128 result = [&] { - if (sign == Signedness::Signed) { + if (sign == SignednessSTRM::Signed) { return v.ir.VectorPairedAddSignedWiden(esize, operand); } @@ -254,23 +254,23 @@ bool TranslatorVisitor::CNT(bool Q, Imm<2> size, Vec Vn, Vec Vd) { } bool TranslatorVisitor::CMGE_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::GE); + return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonTypeSTRM::GE); } bool TranslatorVisitor::CMGT_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::GT); + return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonTypeSTRM::GT); } bool TranslatorVisitor::CMEQ_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::EQ); + return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonTypeSTRM::EQ); } bool TranslatorVisitor::CMLE_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::LE); + return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonTypeSTRM::LE); } bool TranslatorVisitor::CMLT_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::LT); + return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonTypeSTRM::LT); } bool TranslatorVisitor::ABS_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { @@ -341,23 +341,23 @@ bool TranslatorVisitor::FCMEQ_zero_3(bool Q, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCMEQ_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::EQ); + return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonTypeSTRM::EQ); } bool TranslatorVisitor::FCMGE_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::GE); + return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonTypeSTRM::GE); } bool TranslatorVisitor::FCMGT_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::GT); + return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonTypeSTRM::GT); } bool TranslatorVisitor::FCMLE_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::LE); + return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonTypeSTRM::LE); } bool TranslatorVisitor::FCMLT_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::LT); + return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonTypeSTRM::LT); } bool TranslatorVisitor::FCVTL(bool Q, bool sz, Vec Vn, Vec Vd) { @@ -411,19 +411,19 @@ bool TranslatorVisitor::FCVTN(bool Q, bool sz, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCVTNS_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::ToNearest_TieEven); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed, FP::RoundingMode::ToNearest_TieEven); } bool TranslatorVisitor::FCVTMS_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsMinusInfinity); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed, FP::RoundingMode::TowardsMinusInfinity); } bool TranslatorVisitor::FCVTAS_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::ToNearest_TieAwayFromZero); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed, FP::RoundingMode::ToNearest_TieAwayFromZero); } bool TranslatorVisitor::FCVTPS_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsPlusInfinity); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed, FP::RoundingMode::TowardsPlusInfinity); } bool TranslatorVisitor::FCVTXN_2(bool Q, bool sz, Vec Vn, Vec Vd) { @@ -447,27 +447,27 @@ bool TranslatorVisitor::FCVTXN_2(bool Q, bool sz, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCVTZS_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsZero); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed, FP::RoundingMode::TowardsZero); } bool TranslatorVisitor::FCVTNU_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::ToNearest_TieEven); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned, FP::RoundingMode::ToNearest_TieEven); } bool TranslatorVisitor::FCVTMU_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::TowardsMinusInfinity); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned, FP::RoundingMode::TowardsMinusInfinity); } bool TranslatorVisitor::FCVTAU_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::ToNearest_TieAwayFromZero); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned, FP::RoundingMode::ToNearest_TieAwayFromZero); } bool TranslatorVisitor::FCVTPU_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::TowardsPlusInfinity); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned, FP::RoundingMode::TowardsPlusInfinity); } bool TranslatorVisitor::FCVTZU_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::TowardsZero); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned, FP::RoundingMode::TowardsZero); } bool TranslatorVisitor::FRINTN_1(bool Q, Vec Vn, Vec Vd) { @@ -780,19 +780,19 @@ bool TranslatorVisitor::USQADD_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SADALP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Signed, PairedAddLongExtraBehavior::Accumulate); + return PairedAddLong(*this, Q, size, Vn, Vd, SignednessSTRM::Signed, PairedAddLongExtraBehavior::Accumulate); } bool TranslatorVisitor::SADDLP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Signed, PairedAddLongExtraBehavior::None); + return PairedAddLong(*this, Q, size, Vn, Vd, SignednessSTRM::Signed, PairedAddLongExtraBehavior::None); } bool TranslatorVisitor::UADALP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Unsigned, PairedAddLongExtraBehavior::Accumulate); + return PairedAddLong(*this, Q, size, Vn, Vd, SignednessSTRM::Unsigned, PairedAddLongExtraBehavior::Accumulate); } bool TranslatorVisitor::UADDLP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Unsigned, PairedAddLongExtraBehavior::None); + return PairedAddLong(*this, Q, size, Vn, Vd, SignednessSTRM::Unsigned, PairedAddLongExtraBehavior::None); } bool TranslatorVisitor::URECPE(bool Q, bool sz, Vec Vn, Vec Vd) { @@ -824,11 +824,11 @@ bool TranslatorVisitor::URSQRTE(bool Q, bool sz, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SCVTF_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return IntegerConvertToFloat(*this, Q, sz, Vn, Vd, Signedness::Signed); + return IntegerConvertToFloat(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed); } bool TranslatorVisitor::UCVTF_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return IntegerConvertToFloat(*this, Q, sz, Vn, Vd, Signedness::Unsigned); + return IntegerConvertToFloat(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned); } bool TranslatorVisitor::SHLL(bool Q, Imm<2> size, Vec Vn, Vec Vd) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp index 07234fc61b..d23b5fc144 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp @@ -11,7 +11,7 @@ namespace Dynarmic::A64 { namespace { -std::pair Combine(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> Vmlo) { +std::pair CombineVector(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> Vmlo) { if (size == 0b01) { return {concatenate(H, L, M).ZeroExtend(), Vmlo.ZeroExtend()}; } @@ -19,19 +19,19 @@ std::pair Combine(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> return {concatenate(H, L).ZeroExtend(), concatenate(M, Vmlo).ZeroExtend()}; } -enum class ExtraBehavior { +enum class ExtraBehaviorSVXIE { None, Extended, Accumulate, Subtract, }; -bool MultiplyByElement(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehavior extra_behavior) { +bool MultiplyByElement(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehaviorSVXIE extra_behavior) { if (size != 0b01 && size != 0b10) { return v.ReservedValue(); } - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineVector(size, H, L, M, Vmlo); const size_t idxdsize = H == 1 ? 128 : 64; const size_t esize = 8 << size.ZeroExtend(); const size_t datasize = Q ? 128 : 64; @@ -41,9 +41,9 @@ bool MultiplyByElement(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm< const IR::U128 operand3 = v.V(datasize, Vd); IR::U128 result = v.ir.VectorMultiply(esize, operand1, operand2); - if (extra_behavior == ExtraBehavior::Accumulate) { + if (extra_behavior == ExtraBehaviorSVXIE::Accumulate) { result = v.ir.VectorAdd(esize, operand3, result); - } else if (extra_behavior == ExtraBehavior::Subtract) { + } else if (extra_behavior == ExtraBehaviorSVXIE::Subtract) { result = v.ir.VectorSub(esize, operand3, result); } @@ -51,7 +51,7 @@ bool MultiplyByElement(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm< return true; } -bool FPMultiplyByElement(TranslatorVisitor& v, bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehavior extra_behavior) { +bool FPMultiplyByElement(TranslatorVisitor& v, bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehaviorSVXIE extra_behavior) { if (sz && L == 1) { return v.ReservedValue(); } @@ -71,13 +71,13 @@ bool FPMultiplyByElement(TranslatorVisitor& v, bool Q, bool sz, Imm<1> L, Imm<1> const IR::U128 result = [&] { switch (extra_behavior) { - case ExtraBehavior::None: + case ExtraBehaviorSVXIE::None: return v.ir.FPVectorMul(esize, operand1, operand2); - case ExtraBehavior::Extended: + case ExtraBehaviorSVXIE::Extended: return v.ir.FPVectorMulX(esize, operand1, operand2); - case ExtraBehavior::Accumulate: + case ExtraBehaviorSVXIE::Accumulate: return v.ir.FPVectorMulAdd(esize, operand3, operand1, operand2); - case ExtraBehavior::Subtract: + case ExtraBehaviorSVXIE::Subtract: return v.ir.FPVectorMulAdd(esize, operand3, v.ir.FPVectorNeg(esize, operand1), operand2); } UNREACHABLE(); @@ -86,7 +86,7 @@ bool FPMultiplyByElement(TranslatorVisitor& v, bool Q, bool sz, Imm<1> L, Imm<1> return true; } -bool FPMultiplyByElementHalfPrecision(TranslatorVisitor& v, bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehavior extra_behavior) { +bool FPMultiplyByElementHalfPrecision(TranslatorVisitor& v, bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehaviorSVXIE extra_behavior) { const size_t idxdsize = H == 1 ? 128 : 64; const size_t index = concatenate(H, L, M).ZeroExtend(); const Vec Vm = Vmlo.ZeroExtend(); @@ -101,13 +101,13 @@ bool FPMultiplyByElementHalfPrecision(TranslatorVisitor& v, bool Q, Imm<1> L, Im // regular multiplies and extended multiplies. const IR::U128 result = [&] { switch (extra_behavior) { - case ExtraBehavior::None: + case ExtraBehaviorSVXIE::None: break; - case ExtraBehavior::Extended: + case ExtraBehaviorSVXIE::Extended: break; - case ExtraBehavior::Accumulate: + case ExtraBehaviorSVXIE::Accumulate: return v.ir.FPVectorMulAdd(esize, operand3, operand1, operand2); - case ExtraBehavior::Subtract: + case ExtraBehaviorSVXIE::Subtract: return v.ir.FPVectorMulAdd(esize, operand3, v.ir.FPVectorNeg(esize, operand1), operand2); } UNREACHABLE(); @@ -151,12 +151,12 @@ bool DotProduct(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, I return true; } -enum class Signedness { +enum class SignednessSVXIE { Signed, Unsigned }; -bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehavior extra_behavior, Signedness sign) { +bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehaviorSVXIE extra_behavior, SignednessSVXIE sign) { if (size == 0b00 || size == 0b11) { return v.ReservedValue(); } @@ -164,23 +164,23 @@ bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, const size_t idxsize = H == 1 ? 128 : 64; const size_t esize = 8 << size.ZeroExtend(); const size_t datasize = 64; - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineVector(size, H, L, M, Vmlo); const IR::U128 operand1 = v.Vpart(datasize, Vn, Q); const IR::U128 operand2 = v.V(idxsize, Vm); const IR::U128 index_vector = v.ir.VectorBroadcastElement(esize, operand2, index); const IR::U128 result = [&] { - const IR::U128 product = sign == Signedness::Signed + const IR::U128 product = sign == SignednessSVXIE::Signed ? v.ir.VectorMultiplySignedWiden(esize, operand1, index_vector) : v.ir.VectorMultiplyUnsignedWiden(esize, operand1, index_vector); - if (extra_behavior == ExtraBehavior::None) { + if (extra_behavior == ExtraBehaviorSVXIE::None) { return product; } const IR::U128 operand3 = v.V(2 * datasize, Vd); - if (extra_behavior == ExtraBehavior::Accumulate) { + if (extra_behavior == ExtraBehaviorSVXIE::Accumulate) { return v.ir.VectorAdd(2 * esize, operand3, product); } @@ -193,15 +193,15 @@ bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, } // Anonymous namespace bool TranslatorVisitor::MLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate); + return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Accumulate); } bool TranslatorVisitor::MLS_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract); + return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Subtract); } bool TranslatorVisitor::MUL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None); + return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::None); } bool TranslatorVisitor::FCMLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<2> rot, Imm<1> H, Vec Vn, Vec Vd) { @@ -292,39 +292,39 @@ bool TranslatorVisitor::FCMLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4 } bool TranslatorVisitor::FMLA_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElementHalfPrecision(*this, Q, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate); + return FPMultiplyByElementHalfPrecision(*this, Q, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Accumulate); } bool TranslatorVisitor::FMLA_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate); + return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Accumulate); } bool TranslatorVisitor::FMLS_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElementHalfPrecision(*this, Q, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract); + return FPMultiplyByElementHalfPrecision(*this, Q, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Subtract); } bool TranslatorVisitor::FMLS_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract); + return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Subtract); } bool TranslatorVisitor::FMUL_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None); + return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::None); } bool TranslatorVisitor::FMULX_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Extended); + return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Extended); } bool TranslatorVisitor::SMLAL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate, Signedness::Signed); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Accumulate, SignednessSVXIE::Signed); } bool TranslatorVisitor::SMLSL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract, Signedness::Signed); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Subtract, SignednessSVXIE::Signed); } bool TranslatorVisitor::SMULL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None, Signedness::Signed); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::None, SignednessSVXIE::Signed); } bool TranslatorVisitor::SQDMULL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { @@ -336,7 +336,7 @@ bool TranslatorVisitor::SQDMULL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, I const size_t idxsize = H == 1 ? 128 : 64; const size_t esize = 8 << size.ZeroExtend(); const size_t datasize = 64; - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineVector(size, H, L, M, Vmlo); const IR::U128 operand1 = Vpart(datasize, Vn, part); const IR::U128 operand2 = V(idxsize, Vm); @@ -355,7 +355,7 @@ bool TranslatorVisitor::SQDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, I const size_t idxsize = H == 1 ? 128 : 64; const size_t esize = 8 << size.ZeroExtend(); const size_t datasize = Q ? 128 : 64; - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineVector(size, H, L, M, Vmlo); const IR::U128 operand1 = V(datasize, Vn); const IR::U128 operand2 = V(idxsize, Vm); @@ -374,7 +374,7 @@ bool TranslatorVisitor::SQRDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, const size_t idxsize = H == 1 ? 128 : 64; const size_t esize = 8 << size.ZeroExtend(); const size_t datasize = Q ? 128 : 64; - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineVector(size, H, L, M, Vmlo); const IR::U128 operand1 = V(datasize, Vn); const IR::U128 operand2 = V(idxsize, Vm); @@ -394,15 +394,15 @@ bool TranslatorVisitor::UDOT_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> } bool TranslatorVisitor::UMLAL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Accumulate, SignednessSVXIE::Unsigned); } bool TranslatorVisitor::UMLSL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Subtract, SignednessSVXIE::Unsigned); } bool TranslatorVisitor::UMULL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::None, SignednessSVXIE::Unsigned); } } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/interface/A32/arch_version.h b/externals/dynarmic/src/dynarmic/interface/A32/arch_version.h index 240e40ee4c..209bc594f2 100644 --- a/externals/dynarmic/src/dynarmic/interface/A32/arch_version.h +++ b/externals/dynarmic/src/dynarmic/interface/A32/arch_version.h @@ -5,10 +5,12 @@ #pragma once +#include + namespace Dynarmic { namespace A32 { -enum class ArchVersion { +enum class ArchVersion : std::uint8_t { v3, v4, v4T, diff --git a/externals/dynarmic/src/dynarmic/interface/A32/config.h b/externals/dynarmic/src/dynarmic/interface/A32/config.h index 360df06e2a..033967dc00 100644 --- a/externals/dynarmic/src/dynarmic/interface/A32/config.h +++ b/externals/dynarmic/src/dynarmic/interface/A32/config.h @@ -120,14 +120,32 @@ struct UserCallbacks : public TranslateCallbacks { }; struct UserConfig { + bool HasOptimization(OptimizationFlag f) const { + if (!unsafe_optimizations) { + f &= all_safe_optimizations; + } + return (f & optimizations) != no_optimizations; + } + UserCallbacks* callbacks; - size_t processor_id = 0; ExclusiveMonitor* global_monitor = nullptr; - /// Select the architecture version to use. - /// There are minor behavioural differences between versions. - ArchVersion arch_version = ArchVersion::v8; + // Page Table + // The page table is used for faster memory access. If an entry in the table is nullptr, + // the JIT will fallback to calling the MemoryRead*/MemoryWrite* callbacks. + static constexpr std::size_t PAGE_BITS = 12; + static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); + std::array* page_table = nullptr; + + /// Coprocessors + std::array, 16> coprocessors{}; + + /// Fastmem Pointer + /// This should point to the beginning of a 4GB address space which is in arranged just like + /// what you wish for emulated memory to be. If the host page faults on an address, the JIT + /// will fallback to calling the MemoryRead*/MemoryWrite* callbacks. + std::optional fastmem_pointer = std::nullopt; /// This selects other optimizations than can't otherwise be disabled by setting other /// configuration options. This includes: @@ -137,12 +155,29 @@ struct UserConfig { /// This is intended to be used for debugging. OptimizationFlag optimizations = all_safe_optimizations; - bool HasOptimization(OptimizationFlag f) const { - if (!unsafe_optimizations) { - f &= all_safe_optimizations; - } - return (f & optimizations) != no_optimizations; - } + /// Minimum size is about 8MiB. Maximum size is about 128MiB (arm64 host) or 2GiB (x64 host). + /// Maximum size is limited by the maximum length of a x86_64 / arm64 jump. + std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes + + /// Processor ID + std::uint32_t processor_id = 0; + + /// Masks out the first N bits in host pointers from the page table. + /// The intention behind this is to allow users of Dynarmic to pack attributes in the + /// same integer and update the pointer attribute pair atomically. + /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes. + std::int32_t page_table_pointer_mask_bits = 0; + + /// Select the architecture version to use. + /// There are minor behavioural differences between versions. + ArchVersion arch_version = ArchVersion::v8; + + /// Determines if we should detect memory accesses via page_table that straddle are + /// misaligned. Accesses that straddle page boundaries will fallback to the relevant + /// memory callback. + /// This value should be the required access sizes this applies to ORed together. + /// To detect any access, use: 8 | 16 | 32 | 64. + std::uint8_t detect_misaligned_access_via_page_table = 0; /// This enables unsafe optimizations that reduce emulation accuracy in favour of speed. /// For safety, in order to enable unsafe optimizations you have to set BOTH this flag @@ -150,12 +185,6 @@ struct UserConfig { /// The prefered and tested mode for this library is with unsafe optimizations disabled. bool unsafe_optimizations = false; - // Page Table - // The page table is used for faster memory access. If an entry in the table is nullptr, - // the JIT will fallback to calling the MemoryRead*/MemoryWrite* callbacks. - static constexpr std::size_t PAGE_BITS = 12; - static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); - std::array* page_table = nullptr; /// Determines if the pointer in the page_table shall be offseted locally or globally. /// 'false' will access page_table[addr >> bits][addr & mask] /// 'true' will access page_table[addr >> bits][addr] @@ -163,26 +192,11 @@ struct UserConfig { /// So there might be wrongly faulted pages which maps to nullptr. /// This can be avoided by carefully allocating the memory region. bool absolute_offset_page_table = false; - /// Masks out the first N bits in host pointers from the page table. - /// The intention behind this is to allow users of Dynarmic to pack attributes in the - /// same integer and update the pointer attribute pair atomically. - /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes. - int page_table_pointer_mask_bits = 0; - /// Determines if we should detect memory accesses via page_table that straddle are - /// misaligned. Accesses that straddle page boundaries will fallback to the relevant - /// memory callback. - /// This value should be the required access sizes this applies to ORed together. - /// To detect any access, use: 8 | 16 | 32 | 64. - std::uint8_t detect_misaligned_access_via_page_table = 0; + /// Determines if the above option only triggers when the misalignment straddles a /// page boundary. bool only_detect_misalignment_via_page_table_on_page_boundary = false; - // Fastmem Pointer - // This should point to the beginning of a 4GB address space which is in arranged just like - // what you wish for emulated memory to be. If the host page faults on an address, the JIT - // will fallback to calling the MemoryRead*/MemoryWrite* callbacks. - std::optional fastmem_pointer = std::nullopt; /// Determines if instructions that pagefault should cause recompilation of that block /// with fastmem disabled. /// Recompiled code will use the page_table if this is available, otherwise memory @@ -198,9 +212,6 @@ struct UserConfig { /// callbacks. bool recompile_on_exclusive_fastmem_failure = true; - // Coprocessors - std::array, 16> coprocessors{}; - /// When set to true, UserCallbacks::InstructionSynchronizationBarrierRaised will be /// called when an ISB instruction is executed. /// When set to false, ISB will be treated as a NOP instruction. @@ -234,10 +245,6 @@ struct UserConfig { /// in unusual behavior. bool always_little_endian = false; - // Minimum size is about 8MiB. Maximum size is about 128MiB (arm64 host) or 2GiB (x64 host). - // Maximum size is limited by the maximum length of a x86_64 / arm64 jump. - size_t code_cache_size = 128 * 1024 * 1024; // bytes - /// Internal use only bool very_verbose_debugging_output = false; }; diff --git a/externals/dynarmic/src/dynarmic/interface/A64/config.h b/externals/dynarmic/src/dynarmic/interface/A64/config.h index c8ed623eb4..3563c0b2f4 100644 --- a/externals/dynarmic/src/dynarmic/interface/A64/config.h +++ b/externals/dynarmic/src/dynarmic/interface/A64/config.h @@ -136,11 +136,30 @@ struct UserCallbacks { }; struct UserConfig { + /// Fastmem Pointer + /// This should point to the beginning of a 2^page_table_address_space_bits bytes + /// address space which is in arranged just like what you wish for emulated memory to + /// be. If the host page faults on an address, the JIT will fallback to calling the + /// MemoryRead*/MemoryWrite* callbacks. + std::optional fastmem_pointer = std::nullopt; + UserCallbacks* callbacks; - size_t processor_id = 0; ExclusiveMonitor* global_monitor = nullptr; + /// Pointer to where TPIDRRO_EL0 is stored. This pointer will be inserted into + /// emitted code. + const std::uint64_t* tpidrro_el0 = nullptr; + + /// Pointer to where TPIDR_EL0 is stored. This pointer will be inserted into + /// emitted code. + std::uint64_t* tpidr_el0 = nullptr; + + /// Pointer to the page table which we can use for direct page table access. + /// If an entry in page_table is null, the relevant memory callback will be called. + /// If page_table is nullptr, all memory accesses hit the memory callbacks. + void** page_table = nullptr; + /// This selects other optimizations than can't otherwise be disabled by setting other /// configuration options. This includes: /// - IR optimizations @@ -149,12 +168,50 @@ struct UserConfig { /// This is intended to be used for debugging. OptimizationFlag optimizations = all_safe_optimizations; - bool HasOptimization(OptimizationFlag f) const { - if (!unsafe_optimizations) { - f &= all_safe_optimizations; - } - return (f & optimizations) != no_optimizations; - } + /// Declares how many valid address bits are there in virtual addresses. + /// Determines the size of page_table. Valid values are between 12 and 64 inclusive. + /// This is only used if page_table is not nullptr. + std::uint32_t page_table_address_space_bits = 36; + + /// Masks out the first N bits in host pointers from the page table. + /// The intention behind this is to allow users of Dynarmic to pack attributes in the + /// same integer and update the pointer attribute pair atomically. + /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes. + std::int32_t page_table_pointer_mask_bits = 0; + + /// Counter-timer frequency register. The value of the register is not interpreted by + /// dynarmic. + std::uint32_t cntfrq_el0 = 600000000; + + /// CTR_EL0<27:24> is log2 of the cache writeback granule in words. + /// CTR_EL0<23:20> is log2 of the exclusives reservation granule in words. + /// CTR_EL0<19:16> is log2 of the smallest data/unified cacheline in words. + /// CTR_EL0<15:14> is the level 1 instruction cache policy. + /// CTR_EL0<3:0> is log2 of the smallest instruction cacheline in words. + std::uint32_t ctr_el0 = 0x8444c004; + + /// DCZID_EL0<3:0> is log2 of the block size in words + /// DCZID_EL0<4> is 0 if the DC ZVA instruction is permitted. + std::uint32_t dczid_el0 = 4; + + /// Declares how many valid address bits are there in virtual addresses. + /// Determines the size of fastmem arena. Valid values are between 12 and 64 inclusive. + /// This is only used if fastmem_pointer is set. + std::uint32_t fastmem_address_space_bits = 36; + + // Minimum size is about 8MiB. Maximum size is about 128MiB (arm64 host) or 2GiB (x64 host). + // Maximum size is limited by the maximum length of a x86_64 / arm64 jump. + std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes + + /// Determines if we should detect memory accesses via page_table that straddle are + /// misaligned. Accesses that straddle page boundaries will fallback to the relevant + /// memory callback. + /// This value should be the required access sizes this applies to ORed together. + /// To detect any access, use: 8 | 16 | 32 | 64 | 128. + std::uint8_t detect_misaligned_access_via_page_table = 0; + + /// Processor ID + std::uint8_t processor_id = 0; /// This enables unsafe optimizations that reduce emulation accuracy in favour of speed. /// For safety, in order to enable unsafe optimizations you have to set BOTH this flag @@ -177,48 +234,13 @@ struct UserConfig { /// instruction is executed. bool hook_hint_instructions = false; - /// Counter-timer frequency register. The value of the register is not interpreted by - /// dynarmic. - std::uint32_t cntfrq_el0 = 600000000; - - /// CTR_EL0<27:24> is log2 of the cache writeback granule in words. - /// CTR_EL0<23:20> is log2 of the exclusives reservation granule in words. - /// CTR_EL0<19:16> is log2 of the smallest data/unified cacheline in words. - /// CTR_EL0<15:14> is the level 1 instruction cache policy. - /// CTR_EL0<3:0> is log2 of the smallest instruction cacheline in words. - std::uint32_t ctr_el0 = 0x8444c004; - - /// DCZID_EL0<3:0> is log2 of the block size in words - /// DCZID_EL0<4> is 0 if the DC ZVA instruction is permitted. - std::uint32_t dczid_el0 = 4; - - /// Pointer to where TPIDRRO_EL0 is stored. This pointer will be inserted into - /// emitted code. - const std::uint64_t* tpidrro_el0 = nullptr; - - /// Pointer to where TPIDR_EL0 is stored. This pointer will be inserted into - /// emitted code. - std::uint64_t* tpidr_el0 = nullptr; - - /// Pointer to the page table which we can use for direct page table access. - /// If an entry in page_table is null, the relevant memory callback will be called. - /// If page_table is nullptr, all memory accesses hit the memory callbacks. - void** page_table = nullptr; - /// Declares how many valid address bits are there in virtual addresses. - /// Determines the size of page_table. Valid values are between 12 and 64 inclusive. - /// This is only used if page_table is not nullptr. - size_t page_table_address_space_bits = 36; - /// Masks out the first N bits in host pointers from the page table. - /// The intention behind this is to allow users of Dynarmic to pack attributes in the - /// same integer and update the pointer attribute pair atomically. - /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes. - int page_table_pointer_mask_bits = 0; /// Determines what happens if the guest accesses an entry that is off the end of the /// page table. If true, Dynarmic will silently mirror page_table's address space. If /// false, accessing memory outside of page_table bounds will result in a call to the /// relevant memory callback. /// This is only used if page_table is not nullptr. bool silently_mirror_page_table = true; + /// Determines if the pointer in the page_table shall be offseted locally or globally. /// 'false' will access page_table[addr >> bits][addr & mask] /// 'true' will access page_table[addr >> bits][addr] @@ -226,31 +248,17 @@ struct UserConfig { /// So there might be wrongly faulted pages which maps to nullptr. /// This can be avoided by carefully allocating the memory region. bool absolute_offset_page_table = false; - /// Determines if we should detect memory accesses via page_table that straddle are - /// misaligned. Accesses that straddle page boundaries will fallback to the relevant - /// memory callback. - /// This value should be the required access sizes this applies to ORed together. - /// To detect any access, use: 8 | 16 | 32 | 64 | 128. - std::uint8_t detect_misaligned_access_via_page_table = 0; + /// Determines if the above option only triggers when the misalignment straddles a /// page boundary. bool only_detect_misalignment_via_page_table_on_page_boundary = false; - /// Fastmem Pointer - /// This should point to the beginning of a 2^page_table_address_space_bits bytes - /// address space which is in arranged just like what you wish for emulated memory to - /// be. If the host page faults on an address, the JIT will fallback to calling the - /// MemoryRead*/MemoryWrite* callbacks. - std::optional fastmem_pointer = std::nullopt; /// Determines if instructions that pagefault should cause recompilation of that block /// with fastmem disabled. /// Recompiled code will use the page_table if this is available, otherwise memory /// accesses will hit the memory callbacks. bool recompile_on_fastmem_failure = true; - /// Declares how many valid address bits are there in virtual addresses. - /// Determines the size of fastmem arena. Valid values are between 12 and 64 inclusive. - /// This is only used if fastmem_pointer is set. - size_t fastmem_address_space_bits = 36; + /// Determines what happens if the guest accesses an entry that is off the end of the /// fastmem arena. If true, Dynarmic will silently mirror fastmem's address space. If /// false, accessing memory outside of fastmem bounds will result in a call to the @@ -285,12 +293,15 @@ struct UserConfig { /// AddTicks and GetTicksRemaining are never called, and no cycle counting is done. bool enable_cycle_counting = true; - // Minimum size is about 8MiB. Maximum size is about 128MiB (arm64 host) or 2GiB (x64 host). - // Maximum size is limited by the maximum length of a x86_64 / arm64 jump. - size_t code_cache_size = 128 * 1024 * 1024; // bytes - /// Internal use only bool very_verbose_debugging_output = false; + + inline bool HasOptimization(OptimizationFlag f) const { + if (!unsafe_optimizations) { + f &= all_safe_optimizations; + } + return (f & optimizations) != no_optimizations; + } }; } // namespace A64 diff --git a/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp b/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp index fc4f69b3e0..3734aae4d5 100644 --- a/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp +++ b/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp @@ -14,2877 +14,5 @@ namespace Dynarmic::IR { -U1 IREmitter::Imm1(bool imm1) const { - return U1(Value(imm1)); -} - -U8 IREmitter::Imm8(u8 imm8) const { - return U8(Value(imm8)); -} - -U16 IREmitter::Imm16(u16 imm16) const { - return U16(Value(imm16)); -} - -U32 IREmitter::Imm32(u32 imm32) const { - return U32(Value(imm32)); -} - -U64 IREmitter::Imm64(u64 imm64) const { - return U64(Value(imm64)); -} - -void IREmitter::PushRSB(const LocationDescriptor& return_location) { - Inst(Opcode::PushRSB, IR::Value(return_location.Value())); -} - -U64 IREmitter::Pack2x32To1x64(const U32& lo, const U32& hi) { - return Inst(Opcode::Pack2x32To1x64, lo, hi); -} - -U128 IREmitter::Pack2x64To1x128(const U64& lo, const U64& hi) { - return Inst(Opcode::Pack2x64To1x128, lo, hi); -} - -UAny IREmitter::LeastSignificant(size_t bitsize, const U32U64& value) { - switch (bitsize) { - case 8: - return LeastSignificantByte(value); - case 16: - return LeastSignificantHalf(value); - case 32: - if (value.GetType() == Type::U32) { - return value; - } - return LeastSignificantWord(value); - case 64: - ASSERT(value.GetType() == Type::U64); - return value; - } - ASSERT_FALSE("Invalid bitsize"); -} - -U32 IREmitter::LeastSignificantWord(const U64& value) { - return Inst(Opcode::LeastSignificantWord, value); -} - -U16 IREmitter::LeastSignificantHalf(U32U64 value) { - if (value.GetType() == Type::U64) { - value = LeastSignificantWord(value); - } - return Inst(Opcode::LeastSignificantHalf, value); -} - -U8 IREmitter::LeastSignificantByte(U32U64 value) { - if (value.GetType() == Type::U64) { - value = LeastSignificantWord(value); - } - return Inst(Opcode::LeastSignificantByte, value); -} - -ResultAndCarry IREmitter::MostSignificantWord(const U64& value) { - const auto result = Inst(Opcode::MostSignificantWord, value); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -U1 IREmitter::MostSignificantBit(const U32& value) { - return Inst(Opcode::MostSignificantBit, value); -} - -U1 IREmitter::IsZero(const U32& value) { - return Inst(Opcode::IsZero32, value); -} - -U1 IREmitter::IsZero(const U64& value) { - return Inst(Opcode::IsZero64, value); -} - -U1 IREmitter::IsZero(const U32U64& value) { - if (value.GetType() == Type::U32) { - return Inst(Opcode::IsZero32, value); - } else { - return Inst(Opcode::IsZero64, value); - } -} - -U1 IREmitter::TestBit(const U32U64& value, const U8& bit) { - if (value.GetType() == Type::U32) { - return Inst(Opcode::TestBit, IndeterminateExtendToLong(value), bit); - } else { - return Inst(Opcode::TestBit, value, bit); - } -} - -U32 IREmitter::ConditionalSelect(Cond cond, const U32& a, const U32& b) { - return Inst(Opcode::ConditionalSelect32, Value{cond}, a, b); -} - -U64 IREmitter::ConditionalSelect(Cond cond, const U64& a, const U64& b) { - return Inst(Opcode::ConditionalSelect64, Value{cond}, a, b); -} - -NZCV IREmitter::ConditionalSelect(Cond cond, const NZCV& a, const NZCV& b) { - return Inst(Opcode::ConditionalSelectNZCV, Value{cond}, a, b); -} - -U32U64 IREmitter::ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::ConditionalSelect32, Value{cond}, a, b); - } else { - return Inst(Opcode::ConditionalSelect64, Value{cond}, a, b); - } -} - -U1 IREmitter::GetCFlagFromNZCV(const NZCV& nzcv) { - return Inst(Opcode::GetCFlagFromNZCV, nzcv); -} - -NZCV IREmitter::NZCVFromPackedFlags(const U32& a) { - return Inst(Opcode::NZCVFromPackedFlags, a); -} - -NZCV IREmitter::NZCVFrom(const Value& value) { - return Inst(Opcode::GetNZCVFromOp, value); -} - -ResultAndCarry IREmitter::LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - const auto result = Inst(Opcode::LogicalShiftLeft32, value_in, shift_amount, carry_in); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -ResultAndCarry IREmitter::LogicalShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - const auto result = Inst(Opcode::LogicalShiftRight32, value_in, shift_amount, carry_in); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -ResultAndCarry IREmitter::ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - const auto result = Inst(Opcode::ArithmeticShiftRight32, value_in, shift_amount, carry_in); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -ResultAndCarry IREmitter::RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - const auto result = Inst(Opcode::RotateRight32, value_in, shift_amount, carry_in); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -ResultAndCarry IREmitter::RotateRightExtended(const U32& value_in, const U1& carry_in) { - const auto result = Inst(Opcode::RotateRightExtended, value_in, carry_in); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -U32U64 IREmitter::LogicalShiftLeft(const U32U64& value_in, const U8& shift_amount) { - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::LogicalShiftLeft32, value_in, shift_amount, Imm1(0)); - } else { - return Inst(Opcode::LogicalShiftLeft64, value_in, shift_amount); - } -} - -U32U64 IREmitter::LogicalShiftRight(const U32U64& value_in, const U8& shift_amount) { - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::LogicalShiftRight32, value_in, shift_amount, Imm1(0)); - } else { - return Inst(Opcode::LogicalShiftRight64, value_in, shift_amount); - } -} - -U32U64 IREmitter::ArithmeticShiftRight(const U32U64& value_in, const U8& shift_amount) { - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::ArithmeticShiftRight32, value_in, shift_amount, Imm1(0)); - } else { - return Inst(Opcode::ArithmeticShiftRight64, value_in, shift_amount); - } -} - -U32U64 IREmitter::RotateRight(const U32U64& value_in, const U8& shift_amount) { - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::RotateRight32, value_in, shift_amount, Imm1(0)); - } else { - return Inst(Opcode::RotateRight64, value_in, shift_amount); - } -} - -U32U64 IREmitter::LogicalShiftLeftMasked(const U32U64& value_in, const U32U64& shift_amount) { - ASSERT(value_in.GetType() == shift_amount.GetType()); - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::LogicalShiftLeftMasked32, value_in, shift_amount); - } else { - return Inst(Opcode::LogicalShiftLeftMasked64, value_in, shift_amount); - } -} - -U32U64 IREmitter::LogicalShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount) { - ASSERT(value_in.GetType() == shift_amount.GetType()); - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::LogicalShiftRightMasked32, value_in, shift_amount); - } else { - return Inst(Opcode::LogicalShiftRightMasked64, value_in, shift_amount); - } -} - -U32U64 IREmitter::ArithmeticShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount) { - ASSERT(value_in.GetType() == shift_amount.GetType()); - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::ArithmeticShiftRightMasked32, value_in, shift_amount); - } else { - return Inst(Opcode::ArithmeticShiftRightMasked64, value_in, shift_amount); - } -} - -U32U64 IREmitter::RotateRightMasked(const U32U64& value_in, const U32U64& shift_amount) { - ASSERT(value_in.GetType() == shift_amount.GetType()); - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::RotateRightMasked32, value_in, shift_amount); - } else { - return Inst(Opcode::RotateRightMasked64, value_in, shift_amount); - } -} - -U32U64 IREmitter::AddWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Add32, a, b, carry_in); - } else { - return Inst(Opcode::Add64, a, b, carry_in); - } -} - -U32U64 IREmitter::Add(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Add32, a, b, Imm1(0)); - } else { - return Inst(Opcode::Add64, a, b, Imm1(0)); - } -} - -U32U64 IREmitter::SubWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Sub32, a, b, carry_in); - } else { - return Inst(Opcode::Sub64, a, b, carry_in); - } -} - -U32U64 IREmitter::Sub(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Sub32, a, b, Imm1(1)); - } else { - return Inst(Opcode::Sub64, a, b, Imm1(1)); - } -} - -U32U64 IREmitter::Mul(const U32U64& a, const U32U64& b) { - if (a.GetType() == Type::U32) { - return Inst(Opcode::Mul32, a, b); - } - - return Inst(Opcode::Mul64, a, b); -} - -U64 IREmitter::UnsignedMultiplyHigh(const U64& a, const U64& b) { - return Inst(Opcode::UnsignedMultiplyHigh64, a, b); -} - -U64 IREmitter::SignedMultiplyHigh(const U64& a, const U64& b) { - return Inst(Opcode::SignedMultiplyHigh64, a, b); -} - -U32U64 IREmitter::UnsignedDiv(const U32U64& a, const U32U64& b) { - if (a.GetType() == Type::U32) { - return Inst(Opcode::UnsignedDiv32, a, b); - } - - return Inst(Opcode::UnsignedDiv64, a, b); -} - -U32U64 IREmitter::SignedDiv(const U32U64& a, const U32U64& b) { - if (a.GetType() == Type::U32) { - return Inst(Opcode::SignedDiv32, a, b); - } - - return Inst(Opcode::SignedDiv64, a, b); -} - -U32U64 IREmitter::And(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::And32, a, b); - } else { - return Inst(Opcode::And64, a, b); - } -} - -U32U64 IREmitter::AndNot(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::AndNot32, a, b); - } else { - return Inst(Opcode::AndNot64, a, b); - } -} - -U32U64 IREmitter::Eor(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Eor32, a, b); - } else { - return Inst(Opcode::Eor64, a, b); - } -} - -U32U64 IREmitter::Or(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Or32, a, b); - } else { - return Inst(Opcode::Or64, a, b); - } -} - -U32U64 IREmitter::Not(const U32U64& a) { - if (a.GetType() == Type::U32) { - return Inst(Opcode::Not32, a); - } else { - return Inst(Opcode::Not64, a); - } -} - -U64 IREmitter::SignExtendToLong(const UAny& a) { - switch (a.GetType()) { - case Type::U8: - return Inst(Opcode::SignExtendByteToLong, a); - case Type::U16: - return Inst(Opcode::SignExtendHalfToLong, a); - case Type::U32: - return Inst(Opcode::SignExtendWordToLong, a); - case Type::U64: - return U64(a); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::SignExtendToWord(const UAny& a) { - switch (a.GetType()) { - case Type::U8: - return Inst(Opcode::SignExtendByteToWord, a); - case Type::U16: - return Inst(Opcode::SignExtendHalfToWord, a); - case Type::U32: - return U32(a); - case Type::U64: - return Inst(Opcode::LeastSignificantWord, a); - default: - UNREACHABLE(); - } -} - -U64 IREmitter::SignExtendWordToLong(const U32& a) { - return Inst(Opcode::SignExtendWordToLong, a); -} - -U32 IREmitter::SignExtendHalfToWord(const U16& a) { - return Inst(Opcode::SignExtendHalfToWord, a); -} - -U32 IREmitter::SignExtendByteToWord(const U8& a) { - return Inst(Opcode::SignExtendByteToWord, a); -} - -U64 IREmitter::ZeroExtendToLong(const UAny& a) { - switch (a.GetType()) { - case Type::U8: - return Inst(Opcode::ZeroExtendByteToLong, a); - case Type::U16: - return Inst(Opcode::ZeroExtendHalfToLong, a); - case Type::U32: - return Inst(Opcode::ZeroExtendWordToLong, a); - case Type::U64: - return U64(a); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::ZeroExtendToWord(const UAny& a) { - switch (a.GetType()) { - case Type::U8: - return Inst(Opcode::ZeroExtendByteToWord, a); - case Type::U16: - return Inst(Opcode::ZeroExtendHalfToWord, a); - case Type::U32: - return U32(a); - case Type::U64: - return Inst(Opcode::LeastSignificantWord, a); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::ZeroExtendToQuad(const UAny& a) { - return Inst(Opcode::ZeroExtendLongToQuad, ZeroExtendToLong(a)); -} - -U64 IREmitter::ZeroExtendWordToLong(const U32& a) { - return Inst(Opcode::ZeroExtendWordToLong, a); -} - -U32 IREmitter::ZeroExtendHalfToWord(const U16& a) { - return Inst(Opcode::ZeroExtendHalfToWord, a); -} - -U32 IREmitter::ZeroExtendByteToWord(const U8& a) { - return Inst(Opcode::ZeroExtendByteToWord, a); -} - -U32 IREmitter::IndeterminateExtendToWord(const UAny& a) { - // TODO: Implement properly - return ZeroExtendToWord(a); -} - -U64 IREmitter::IndeterminateExtendToLong(const UAny& a) { - // TODO: Implement properly - return ZeroExtendToLong(a); -} - -U32 IREmitter::ByteReverseWord(const U32& a) { - return Inst(Opcode::ByteReverseWord, a); -} - -U16 IREmitter::ByteReverseHalf(const U16& a) { - return Inst(Opcode::ByteReverseHalf, a); -} - -U64 IREmitter::ByteReverseDual(const U64& a) { - return Inst(Opcode::ByteReverseDual, a); -} - -U32U64 IREmitter::CountLeadingZeros(const U32U64& a) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::CountLeadingZeros32, a); - } - - return Inst(Opcode::CountLeadingZeros64, a); -} - -U32U64 IREmitter::ExtractRegister(const U32U64& a, const U32U64& b, const U8& lsb) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::ExtractRegister32, a, b, lsb); - } - - return Inst(Opcode::ExtractRegister64, a, b, lsb); -} - -U32U64 IREmitter::ReplicateBit(const U32U64& a, u8 bit) { - if (a.GetType() == IR::Type::U32) { - ASSERT(bit < 32); - return Inst(Opcode::ReplicateBit32, a, Imm8(bit)); - } - - ASSERT(bit < 64); - return Inst(Opcode::ReplicateBit64, a, Imm8(bit)); -} - -U32U64 IREmitter::MaxSigned(const U32U64& a, const U32U64& b) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::MaxSigned32, a, b); - } - - return Inst(Opcode::MaxSigned64, a, b); -} - -U32U64 IREmitter::MaxUnsigned(const U32U64& a, const U32U64& b) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::MaxUnsigned32, a, b); - } - - return Inst(Opcode::MaxUnsigned64, a, b); -} - -U32U64 IREmitter::MinSigned(const U32U64& a, const U32U64& b) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::MinSigned32, a, b); - } - - return Inst(Opcode::MinSigned64, a, b); -} - -U32U64 IREmitter::MinUnsigned(const U32U64& a, const U32U64& b) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::MinUnsigned32, a, b); - } - - return Inst(Opcode::MinUnsigned64, a, b); -} - -ResultAndOverflow IREmitter::SignedSaturatedAddWithFlag(const U32& a, const U32& b) { - const auto result = Inst(Opcode::SignedSaturatedAddWithFlag32, a, b); - const auto overflow = Inst(Opcode::GetOverflowFromOp, result); - return {result, overflow}; -} - -ResultAndOverflow IREmitter::SignedSaturatedSubWithFlag(const U32& a, const U32& b) { - const auto result = Inst(Opcode::SignedSaturatedSubWithFlag32, a, b); - const auto overflow = Inst(Opcode::GetOverflowFromOp, result); - return {result, overflow}; -} - -ResultAndOverflow IREmitter::SignedSaturation(const U32& a, size_t bit_size_to_saturate_to) { - ASSERT(bit_size_to_saturate_to >= 1 && bit_size_to_saturate_to <= 32); - const auto result = Inst(Opcode::SignedSaturation, a, Imm8(static_cast(bit_size_to_saturate_to))); - const auto overflow = Inst(Opcode::GetOverflowFromOp, result); - return {result, overflow}; -} - -ResultAndOverflow IREmitter::UnsignedSaturation(const U32& a, size_t bit_size_to_saturate_to) { - ASSERT(bit_size_to_saturate_to <= 31); - const auto result = Inst(Opcode::UnsignedSaturation, a, Imm8(static_cast(bit_size_to_saturate_to))); - const auto overflow = Inst(Opcode::GetOverflowFromOp, result); - return {result, overflow}; -} - -UAny IREmitter::SignedSaturatedAdd(const UAny& a, const UAny& b) { - ASSERT(a.GetType() == b.GetType()); - const auto result = [&]() -> IR::UAny { - switch (a.GetType()) { - case IR::Type::U8: - return Inst(Opcode::SignedSaturatedAdd8, a, b); - case IR::Type::U16: - return Inst(Opcode::SignedSaturatedAdd16, a, b); - case IR::Type::U32: - return Inst(Opcode::SignedSaturatedAdd32, a, b); - case IR::Type::U64: - return Inst(Opcode::SignedSaturatedAdd64, a, b); - default: - return IR::UAny{}; - } - }(); - return result; -} - -UAny IREmitter::SignedSaturatedDoublingMultiplyReturnHigh(const UAny& a, const UAny& b) { - ASSERT(a.GetType() == b.GetType()); - const auto result = [&]() -> IR::UAny { - switch (a.GetType()) { - case IR::Type::U16: - return Inst(Opcode::SignedSaturatedDoublingMultiplyReturnHigh16, a, b); - case IR::Type::U32: - return Inst(Opcode::SignedSaturatedDoublingMultiplyReturnHigh32, a, b); - default: - UNREACHABLE(); - } - }(); - return result; -} - -UAny IREmitter::SignedSaturatedSub(const UAny& a, const UAny& b) { - ASSERT(a.GetType() == b.GetType()); - const auto result = [&]() -> IR::UAny { - switch (a.GetType()) { - case IR::Type::U8: - return Inst(Opcode::SignedSaturatedSub8, a, b); - case IR::Type::U16: - return Inst(Opcode::SignedSaturatedSub16, a, b); - case IR::Type::U32: - return Inst(Opcode::SignedSaturatedSub32, a, b); - case IR::Type::U64: - return Inst(Opcode::SignedSaturatedSub64, a, b); - default: - return IR::UAny{}; - } - }(); - return result; -} - -UAny IREmitter::UnsignedSaturatedAdd(const UAny& a, const UAny& b) { - ASSERT(a.GetType() == b.GetType()); - const auto result = [&]() -> IR::UAny { - switch (a.GetType()) { - case IR::Type::U8: - return Inst(Opcode::UnsignedSaturatedAdd8, a, b); - case IR::Type::U16: - return Inst(Opcode::UnsignedSaturatedAdd16, a, b); - case IR::Type::U32: - return Inst(Opcode::UnsignedSaturatedAdd32, a, b); - case IR::Type::U64: - return Inst(Opcode::UnsignedSaturatedAdd64, a, b); - default: - return IR::UAny{}; - } - }(); - return result; -} - -UAny IREmitter::UnsignedSaturatedSub(const UAny& a, const UAny& b) { - ASSERT(a.GetType() == b.GetType()); - const auto result = [&]() -> IR::UAny { - switch (a.GetType()) { - case IR::Type::U8: - return Inst(Opcode::UnsignedSaturatedSub8, a, b); - case IR::Type::U16: - return Inst(Opcode::UnsignedSaturatedSub16, a, b); - case IR::Type::U32: - return Inst(Opcode::UnsignedSaturatedSub32, a, b); - case IR::Type::U64: - return Inst(Opcode::UnsignedSaturatedSub64, a, b); - default: - return IR::UAny{}; - } - }(); - return result; -} - -U128 IREmitter::VectorSignedSaturatedAdd(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedAdd8, a, b); - case 16: - return Inst(Opcode::VectorSignedSaturatedAdd16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedAdd32, a, b); - case 64: - return Inst(Opcode::VectorSignedSaturatedAdd64, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorSignedSaturatedSub(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedSub8, a, b); - case 16: - return Inst(Opcode::VectorSignedSaturatedSub16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedSub32, a, b); - case 64: - return Inst(Opcode::VectorSignedSaturatedSub64, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorUnsignedSaturatedAdd(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorUnsignedSaturatedAdd8, a, b); - case 16: - return Inst(Opcode::VectorUnsignedSaturatedAdd16, a, b); - case 32: - return Inst(Opcode::VectorUnsignedSaturatedAdd32, a, b); - case 64: - return Inst(Opcode::VectorUnsignedSaturatedAdd64, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorUnsignedSaturatedSub(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorUnsignedSaturatedSub8, a, b); - case 16: - return Inst(Opcode::VectorUnsignedSaturatedSub16, a, b); - case 32: - return Inst(Opcode::VectorUnsignedSaturatedSub32, a, b); - case 64: - return Inst(Opcode::VectorUnsignedSaturatedSub64, a, b); - default: - UNREACHABLE(); - } -} - -ResultAndGE IREmitter::PackedAddU8(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddU8, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedAddS8(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddS8, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedAddU16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddU16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedAddS16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddS16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubU8(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubU8, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubS8(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubS8, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubU16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubU16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubS16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubS16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedAddSubU16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddSubU16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedAddSubS16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddSubS16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubAddU16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubAddU16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubAddS16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubAddS16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -U32 IREmitter::PackedHalvingAddU8(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddU8, a, b); -} - -U32 IREmitter::PackedHalvingAddS8(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddS8, a, b); -} - -U32 IREmitter::PackedHalvingSubU8(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubU8, a, b); -} - -U32 IREmitter::PackedHalvingSubS8(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubS8, a, b); -} - -U32 IREmitter::PackedHalvingAddU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddU16, a, b); -} - -U32 IREmitter::PackedHalvingAddS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddS16, a, b); -} - -U32 IREmitter::PackedHalvingSubU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubU16, a, b); -} - -U32 IREmitter::PackedHalvingSubS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubS16, a, b); -} - -U32 IREmitter::PackedHalvingAddSubU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddSubU16, a, b); -} - -U32 IREmitter::PackedHalvingAddSubS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddSubS16, a, b); -} - -U32 IREmitter::PackedHalvingSubAddU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubAddU16, a, b); -} - -U32 IREmitter::PackedHalvingSubAddS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubAddS16, a, b); -} - -U32 IREmitter::PackedSaturatedAddU8(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedAddU8, a, b); -} - -U32 IREmitter::PackedSaturatedAddS8(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedAddS8, a, b); -} - -U32 IREmitter::PackedSaturatedSubU8(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedSubU8, a, b); -} - -U32 IREmitter::PackedSaturatedSubS8(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedSubS8, a, b); -} - -U32 IREmitter::PackedSaturatedAddU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedAddU16, a, b); -} - -U32 IREmitter::PackedSaturatedAddS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedAddS16, a, b); -} - -U32 IREmitter::PackedSaturatedSubU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedSubU16, a, b); -} - -U32 IREmitter::PackedSaturatedSubS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedSubS16, a, b); -} - -U32 IREmitter::PackedAbsDiffSumU8(const U32& a, const U32& b) { - return Inst(Opcode::PackedAbsDiffSumU8, a, b); -} - -U32 IREmitter::PackedSelect(const U32& ge, const U32& a, const U32& b) { - return Inst(Opcode::PackedSelect, ge, a, b); -} - -U32 IREmitter::CRC32Castagnoli8(const U32& a, const U32& b) { - return Inst(Opcode::CRC32Castagnoli8, a, b); -} - -U32 IREmitter::CRC32Castagnoli16(const U32& a, const U32& b) { - return Inst(Opcode::CRC32Castagnoli16, a, b); -} - -U32 IREmitter::CRC32Castagnoli32(const U32& a, const U32& b) { - return Inst(Opcode::CRC32Castagnoli32, a, b); -} - -U32 IREmitter::CRC32Castagnoli64(const U32& a, const U64& b) { - return Inst(Opcode::CRC32Castagnoli64, a, b); -} - -U32 IREmitter::CRC32ISO8(const U32& a, const U32& b) { - return Inst(Opcode::CRC32ISO8, a, b); -} - -U32 IREmitter::CRC32ISO16(const U32& a, const U32& b) { - return Inst(Opcode::CRC32ISO16, a, b); -} - -U32 IREmitter::CRC32ISO32(const U32& a, const U32& b) { - return Inst(Opcode::CRC32ISO32, a, b); -} - -U32 IREmitter::CRC32ISO64(const U32& a, const U64& b) { - return Inst(Opcode::CRC32ISO64, a, b); -} - -U128 IREmitter::AESDecryptSingleRound(const U128& a) { - return Inst(Opcode::AESDecryptSingleRound, a); -} - -U128 IREmitter::AESEncryptSingleRound(const U128& a) { - return Inst(Opcode::AESEncryptSingleRound, a); -} - -U128 IREmitter::AESInverseMixColumns(const U128& a) { - return Inst(Opcode::AESInverseMixColumns, a); -} - -U128 IREmitter::AESMixColumns(const U128& a) { - return Inst(Opcode::AESMixColumns, a); -} - -U8 IREmitter::SM4AccessSubstitutionBox(const U8& a) { - return Inst(Opcode::SM4AccessSubstitutionBox, a); -} - -U128 IREmitter::SHA256Hash(const U128& x, const U128& y, const U128& w, bool part1) { - return Inst(Opcode::SHA256Hash, x, y, w, Imm1(part1)); -} - -U128 IREmitter::SHA256MessageSchedule0(const U128& x, const U128& y) { - return Inst(Opcode::SHA256MessageSchedule0, x, y); -} - -U128 IREmitter::SHA256MessageSchedule1(const U128& x, const U128& y, const U128& z) { - return Inst(Opcode::SHA256MessageSchedule1, x, y, z); -} - -UAny IREmitter::VectorGetElement(size_t esize, const U128& a, size_t index) { - ASSERT_MSG(esize * index < 128, "Invalid index"); - switch (esize) { - case 8: - return Inst(Opcode::VectorGetElement8, a, Imm8(static_cast(index))); - case 16: - return Inst(Opcode::VectorGetElement16, a, Imm8(static_cast(index))); - case 32: - return Inst(Opcode::VectorGetElement32, a, Imm8(static_cast(index))); - case 64: - return Inst(Opcode::VectorGetElement64, a, Imm8(static_cast(index))); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorSetElement(size_t esize, const U128& a, size_t index, const IR::UAny& elem) { - ASSERT_MSG(esize * index < 128, "Invalid index"); - switch (esize) { - case 8: - return Inst(Opcode::VectorSetElement8, a, Imm8(static_cast(index)), elem); - case 16: - return Inst(Opcode::VectorSetElement16, a, Imm8(static_cast(index)), elem); - case 32: - return Inst(Opcode::VectorSetElement32, a, Imm8(static_cast(index)), elem); - case 64: - return Inst(Opcode::VectorSetElement64, a, Imm8(static_cast(index)), elem); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorAbs(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorAbs8, a); - case 16: - return Inst(Opcode::VectorAbs16, a); - case 32: - return Inst(Opcode::VectorAbs32, a); - case 64: - return Inst(Opcode::VectorAbs64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorAdd(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorAdd8, a, b); - case 16: - return Inst(Opcode::VectorAdd16, a, b); - case 32: - return Inst(Opcode::VectorAdd32, a, b); - case 64: - return Inst(Opcode::VectorAdd64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorAnd(const U128& a, const U128& b) { - return Inst(Opcode::VectorAnd, a, b); -} - -U128 IREmitter::VectorAndNot(const U128& a, const U128& b) { - return Inst(Opcode::VectorAndNot, a, b); -} - -U128 IREmitter::VectorArithmeticShiftRight(size_t esize, const U128& a, u8 shift_amount) { - switch (esize) { - case 8: - return Inst(Opcode::VectorArithmeticShiftRight8, a, Imm8(shift_amount)); - case 16: - return Inst(Opcode::VectorArithmeticShiftRight16, a, Imm8(shift_amount)); - case 32: - return Inst(Opcode::VectorArithmeticShiftRight32, a, Imm8(shift_amount)); - case 64: - return Inst(Opcode::VectorArithmeticShiftRight64, a, Imm8(shift_amount)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorArithmeticVShift(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorArithmeticVShift8, a, b); - case 16: - return Inst(Opcode::VectorArithmeticVShift16, a, b); - case 32: - return Inst(Opcode::VectorArithmeticVShift32, a, b); - case 64: - return Inst(Opcode::VectorArithmeticVShift64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorBroadcastLower(size_t esize, const UAny& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorBroadcastLower8, U8(a)); - case 16: - return Inst(Opcode::VectorBroadcastLower16, U16(a)); - case 32: - return Inst(Opcode::VectorBroadcastLower32, U32(a)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorBroadcast(size_t esize, const UAny& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorBroadcast8, U8(a)); - case 16: - return Inst(Opcode::VectorBroadcast16, U16(a)); - case 32: - return Inst(Opcode::VectorBroadcast32, U32(a)); - case 64: - return Inst(Opcode::VectorBroadcast64, U64(a)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorBroadcastElementLower(size_t esize, const U128& a, size_t index) { - ASSERT_MSG(esize * index < 128, "Invalid index"); - switch (esize) { - case 8: - return Inst(Opcode::VectorBroadcastElementLower8, a, u8(index)); - case 16: - return Inst(Opcode::VectorBroadcastElementLower16, a, u8(index)); - case 32: - return Inst(Opcode::VectorBroadcastElementLower32, a, u8(index)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorBroadcastElement(size_t esize, const U128& a, size_t index) { - ASSERT_MSG(esize * index < 128, "Invalid index"); - switch (esize) { - case 8: - return Inst(Opcode::VectorBroadcastElement8, a, u8(index)); - case 16: - return Inst(Opcode::VectorBroadcastElement16, a, u8(index)); - case 32: - return Inst(Opcode::VectorBroadcastElement32, a, u8(index)); - case 64: - return Inst(Opcode::VectorBroadcastElement64, a, u8(index)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorCountLeadingZeros(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorCountLeadingZeros8, a); - case 16: - return Inst(Opcode::VectorCountLeadingZeros16, a); - case 32: - return Inst(Opcode::VectorCountLeadingZeros32, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorDeinterleaveEven(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorDeinterleaveEven8, a, b); - case 16: - return Inst(Opcode::VectorDeinterleaveEven16, a, b); - case 32: - return Inst(Opcode::VectorDeinterleaveEven32, a, b); - case 64: - return Inst(Opcode::VectorDeinterleaveEven64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorDeinterleaveOdd(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorDeinterleaveOdd8, a, b); - case 16: - return Inst(Opcode::VectorDeinterleaveOdd16, a, b); - case 32: - return Inst(Opcode::VectorDeinterleaveOdd32, a, b); - case 64: - return Inst(Opcode::VectorDeinterleaveOdd64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorDeinterleaveEvenLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorDeinterleaveEvenLower8, a, b); - case 16: - return Inst(Opcode::VectorDeinterleaveEvenLower16, a, b); - case 32: - return Inst(Opcode::VectorDeinterleaveEvenLower32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorDeinterleaveOddLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorDeinterleaveOddLower8, a, b); - case 16: - return Inst(Opcode::VectorDeinterleaveOddLower16, a, b); - case 32: - return Inst(Opcode::VectorDeinterleaveOddLower32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorEor(const U128& a, const U128& b) { - return Inst(Opcode::VectorEor, a, b); -} - -U128 IREmitter::VectorEqual(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorEqual8, a, b); - case 16: - return Inst(Opcode::VectorEqual16, a, b); - case 32: - return Inst(Opcode::VectorEqual32, a, b); - case 64: - return Inst(Opcode::VectorEqual64, a, b); - case 128: - return Inst(Opcode::VectorEqual128, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorExtract(const U128& a, const U128& b, size_t position) { - ASSERT(position <= 128); - return Inst(Opcode::VectorExtract, a, b, Imm8(static_cast(position))); -} - -U128 IREmitter::VectorExtractLower(const U128& a, const U128& b, size_t position) { - ASSERT(position <= 64); - return Inst(Opcode::VectorExtractLower, a, b, Imm8(static_cast(position))); -} - -U128 IREmitter::VectorGreaterSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorGreaterS8, a, b); - case 16: - return Inst(Opcode::VectorGreaterS16, a, b); - case 32: - return Inst(Opcode::VectorGreaterS32, a, b); - case 64: - return Inst(Opcode::VectorGreaterS64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorGreaterEqualSigned(size_t esize, const U128& a, const U128& b) { - return VectorOr(VectorGreaterSigned(esize, a, b), VectorEqual(esize, a, b)); -} - -U128 IREmitter::VectorGreaterEqualUnsigned(size_t esize, const U128& a, const U128& b) { - return VectorEqual(esize, VectorMaxUnsigned(esize, a, b), a); -} - -U128 IREmitter::VectorGreaterUnsigned(size_t esize, const U128& a, const U128& b) { - return VectorNot(VectorEqual(esize, VectorMinUnsigned(esize, a, b), a)); -} - -U128 IREmitter::VectorHalvingAddSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorHalvingAddS8, a, b); - case 16: - return Inst(Opcode::VectorHalvingAddS16, a, b); - case 32: - return Inst(Opcode::VectorHalvingAddS32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorHalvingAddUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorHalvingAddU8, a, b); - case 16: - return Inst(Opcode::VectorHalvingAddU16, a, b); - case 32: - return Inst(Opcode::VectorHalvingAddU32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorHalvingSubSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorHalvingSubS8, a, b); - case 16: - return Inst(Opcode::VectorHalvingSubS16, a, b); - case 32: - return Inst(Opcode::VectorHalvingSubS32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorHalvingSubUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorHalvingSubU8, a, b); - case 16: - return Inst(Opcode::VectorHalvingSubU16, a, b); - case 32: - return Inst(Opcode::VectorHalvingSubU32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorInterleaveLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorInterleaveLower8, a, b); - case 16: - return Inst(Opcode::VectorInterleaveLower16, a, b); - case 32: - return Inst(Opcode::VectorInterleaveLower32, a, b); - case 64: - return Inst(Opcode::VectorInterleaveLower64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorInterleaveUpper(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorInterleaveUpper8, a, b); - case 16: - return Inst(Opcode::VectorInterleaveUpper16, a, b); - case 32: - return Inst(Opcode::VectorInterleaveUpper32, a, b); - case 64: - return Inst(Opcode::VectorInterleaveUpper64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorLessEqualSigned(size_t esize, const U128& a, const U128& b) { - return VectorNot(VectorGreaterSigned(esize, a, b)); -} - -U128 IREmitter::VectorLessEqualUnsigned(size_t esize, const U128& a, const U128& b) { - return VectorEqual(esize, VectorMinUnsigned(esize, a, b), a); -} - -U128 IREmitter::VectorLessSigned(size_t esize, const U128& a, const U128& b) { - return VectorNot(VectorOr(VectorGreaterSigned(esize, a, b), VectorEqual(esize, a, b))); -} - -U128 IREmitter::VectorLessUnsigned(size_t esize, const U128& a, const U128& b) { - return VectorNot(VectorEqual(esize, VectorMaxUnsigned(esize, a, b), a)); -} - -U128 IREmitter::VectorLogicalShiftLeft(size_t esize, const U128& a, u8 shift_amount) { - switch (esize) { - case 8: - return Inst(Opcode::VectorLogicalShiftLeft8, a, Imm8(shift_amount)); - case 16: - return Inst(Opcode::VectorLogicalShiftLeft16, a, Imm8(shift_amount)); - case 32: - return Inst(Opcode::VectorLogicalShiftLeft32, a, Imm8(shift_amount)); - case 64: - return Inst(Opcode::VectorLogicalShiftLeft64, a, Imm8(shift_amount)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorLogicalShiftRight(size_t esize, const U128& a, u8 shift_amount) { - switch (esize) { - case 8: - return Inst(Opcode::VectorLogicalShiftRight8, a, Imm8(shift_amount)); - case 16: - return Inst(Opcode::VectorLogicalShiftRight16, a, Imm8(shift_amount)); - case 32: - return Inst(Opcode::VectorLogicalShiftRight32, a, Imm8(shift_amount)); - case 64: - return Inst(Opcode::VectorLogicalShiftRight64, a, Imm8(shift_amount)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorLogicalVShift(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorLogicalVShift8, a, b); - case 16: - return Inst(Opcode::VectorLogicalVShift16, a, b); - case 32: - return Inst(Opcode::VectorLogicalVShift32, a, b); - case 64: - return Inst(Opcode::VectorLogicalVShift64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMaxSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMaxS8, a, b); - case 16: - return Inst(Opcode::VectorMaxS16, a, b); - case 32: - return Inst(Opcode::VectorMaxS32, a, b); - case 64: - return Inst(Opcode::VectorMaxS64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMaxUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMaxU8, a, b); - case 16: - return Inst(Opcode::VectorMaxU16, a, b); - case 32: - return Inst(Opcode::VectorMaxU32, a, b); - case 64: - return Inst(Opcode::VectorMaxU64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMinSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMinS8, a, b); - case 16: - return Inst(Opcode::VectorMinS16, a, b); - case 32: - return Inst(Opcode::VectorMinS32, a, b); - case 64: - return Inst(Opcode::VectorMinS64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMinUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMinU8, a, b); - case 16: - return Inst(Opcode::VectorMinU16, a, b); - case 32: - return Inst(Opcode::VectorMinU32, a, b); - case 64: - return Inst(Opcode::VectorMinU64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMultiply(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMultiply8, a, b); - case 16: - return Inst(Opcode::VectorMultiply16, a, b); - case 32: - return Inst(Opcode::VectorMultiply32, a, b); - case 64: - return Inst(Opcode::VectorMultiply64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMultiplySignedWiden(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMultiplySignedWiden8, a, b); - case 16: - return Inst(Opcode::VectorMultiplySignedWiden16, a, b); - case 32: - return Inst(Opcode::VectorMultiplySignedWiden32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMultiplyUnsignedWiden(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMultiplyUnsignedWiden8, a, b); - case 16: - return Inst(Opcode::VectorMultiplyUnsignedWiden16, a, b); - case 32: - return Inst(Opcode::VectorMultiplyUnsignedWiden32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorNarrow(size_t original_esize, const U128& a) { - switch (original_esize) { - case 16: - return Inst(Opcode::VectorNarrow16, a); - case 32: - return Inst(Opcode::VectorNarrow32, a); - case 64: - return Inst(Opcode::VectorNarrow64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorNot(const U128& a) { - return Inst(Opcode::VectorNot, a); -} - -U128 IREmitter::VectorOr(const U128& a, const U128& b) { - return Inst(Opcode::VectorOr, a, b); -} - -U128 IREmitter::VectorPairedAdd(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedAdd8, a, b); - case 16: - return Inst(Opcode::VectorPairedAdd16, a, b); - case 32: - return Inst(Opcode::VectorPairedAdd32, a, b); - case 64: - return Inst(Opcode::VectorPairedAdd64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorPairedAddLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedAddLower8, a, b); - case 16: - return Inst(Opcode::VectorPairedAddLower16, a, b); - case 32: - return Inst(Opcode::VectorPairedAddLower32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorPairedAddSignedWiden(size_t original_esize, const U128& a) { - switch (original_esize) { - case 8: - return Inst(Opcode::VectorPairedAddSignedWiden8, a); - case 16: - return Inst(Opcode::VectorPairedAddSignedWiden16, a); - case 32: - return Inst(Opcode::VectorPairedAddSignedWiden32, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorPairedAddUnsignedWiden(size_t original_esize, const U128& a) { - switch (original_esize) { - case 8: - return Inst(Opcode::VectorPairedAddUnsignedWiden8, a); - case 16: - return Inst(Opcode::VectorPairedAddUnsignedWiden16, a); - case 32: - return Inst(Opcode::VectorPairedAddUnsignedWiden32, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorPairedMaxSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMaxS8, a, b); - case 16: - return Inst(Opcode::VectorPairedMaxS16, a, b); - case 32: - return Inst(Opcode::VectorPairedMaxS32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMaxUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMaxU8, a, b); - case 16: - return Inst(Opcode::VectorPairedMaxU16, a, b); - case 32: - return Inst(Opcode::VectorPairedMaxU32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMinSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMinS8, a, b); - case 16: - return Inst(Opcode::VectorPairedMinS16, a, b); - case 32: - return Inst(Opcode::VectorPairedMinS32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMinUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMinU8, a, b); - case 16: - return Inst(Opcode::VectorPairedMinU16, a, b); - case 32: - return Inst(Opcode::VectorPairedMinU32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMaxSignedLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMaxLowerS8, a, b); - case 16: - return Inst(Opcode::VectorPairedMaxLowerS16, a, b); - case 32: - return Inst(Opcode::VectorPairedMaxLowerS32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMaxUnsignedLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMaxLowerU8, a, b); - case 16: - return Inst(Opcode::VectorPairedMaxLowerU16, a, b); - case 32: - return Inst(Opcode::VectorPairedMaxLowerU32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMinSignedLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMinLowerS8, a, b); - case 16: - return Inst(Opcode::VectorPairedMinLowerS16, a, b); - case 32: - return Inst(Opcode::VectorPairedMinLowerS32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMinUnsignedLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMinLowerU8, a, b); - case 16: - return Inst(Opcode::VectorPairedMinLowerU16, a, b); - case 32: - return Inst(Opcode::VectorPairedMinLowerU32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPolynomialMultiply(const U128& a, const U128& b) { - return Inst(Opcode::VectorPolynomialMultiply8, a, b); -} - -U128 IREmitter::VectorPolynomialMultiplyLong(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPolynomialMultiplyLong8, a, b); - case 64: - return Inst(Opcode::VectorPolynomialMultiplyLong64, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPopulationCount(const U128& a) { - return Inst(Opcode::VectorPopulationCount, a); -} - -U128 IREmitter::VectorReverseBits(const U128& a) { - return Inst(Opcode::VectorReverseBits, a); -} - -U128 IREmitter::VectorReverseElementsInHalfGroups(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorReverseElementsInHalfGroups8, a); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorReverseElementsInWordGroups(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorReverseElementsInWordGroups8, a); - case 16: - return Inst(Opcode::VectorReverseElementsInWordGroups16, a); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorReverseElementsInLongGroups(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorReverseElementsInLongGroups8, a); - case 16: - return Inst(Opcode::VectorReverseElementsInLongGroups16, a); - case 32: - return Inst(Opcode::VectorReverseElementsInLongGroups32, a); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorReduceAdd(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorReduceAdd8, a); - case 16: - return Inst(Opcode::VectorReduceAdd16, a); - case 32: - return Inst(Opcode::VectorReduceAdd32, a); - case 64: - return Inst(Opcode::VectorReduceAdd64, a); - } - - UNREACHABLE(); -} - -U128 IREmitter::VectorRotateLeft(size_t esize, const U128& a, u8 amount) { - ASSERT(amount < esize); - - if (amount == 0) { - return a; - } - - return VectorOr(VectorLogicalShiftLeft(esize, a, amount), - VectorLogicalShiftRight(esize, a, static_cast(esize - amount))); -} - -U128 IREmitter::VectorRotateRight(size_t esize, const U128& a, u8 amount) { - ASSERT(amount < esize); - - if (amount == 0) { - return a; - } - - return VectorOr(VectorLogicalShiftRight(esize, a, amount), - VectorLogicalShiftLeft(esize, a, static_cast(esize - amount))); -} - -U128 IREmitter::VectorRotateWholeVectorRight(const U128& a, u8 amount) { - ASSERT(amount % 32 == 0); - return Inst(Opcode::VectorRotateWholeVectorRight, a, Imm8(amount)); -} - -U128 IREmitter::VectorRoundingHalvingAddSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorRoundingHalvingAddS8, a, b); - case 16: - return Inst(Opcode::VectorRoundingHalvingAddS16, a, b); - case 32: - return Inst(Opcode::VectorRoundingHalvingAddS32, a, b); - } - - UNREACHABLE(); -} - -U128 IREmitter::VectorRoundingHalvingAddUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorRoundingHalvingAddU8, a, b); - case 16: - return Inst(Opcode::VectorRoundingHalvingAddU16, a, b); - case 32: - return Inst(Opcode::VectorRoundingHalvingAddU32, a, b); - } - - UNREACHABLE(); -} - -U128 IREmitter::VectorRoundingShiftLeftSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorRoundingShiftLeftS8, a, b); - case 16: - return Inst(Opcode::VectorRoundingShiftLeftS16, a, b); - case 32: - return Inst(Opcode::VectorRoundingShiftLeftS32, a, b); - case 64: - return Inst(Opcode::VectorRoundingShiftLeftS64, a, b); - } - - UNREACHABLE(); -} - -U128 IREmitter::VectorRoundingShiftLeftUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorRoundingShiftLeftU8, a, b); - case 16: - return Inst(Opcode::VectorRoundingShiftLeftU16, a, b); - case 32: - return Inst(Opcode::VectorRoundingShiftLeftU32, a, b); - case 64: - return Inst(Opcode::VectorRoundingShiftLeftU64, a, b); - } - - UNREACHABLE(); -} - -U128 IREmitter::VectorSignExtend(size_t original_esize, const U128& a) { - switch (original_esize) { - case 8: - return Inst(Opcode::VectorSignExtend8, a); - case 16: - return Inst(Opcode::VectorSignExtend16, a); - case 32: - return Inst(Opcode::VectorSignExtend32, a); - case 64: - return Inst(Opcode::VectorSignExtend64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedAbsoluteDifference(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedAbsoluteDifference8, a, b); - case 16: - return Inst(Opcode::VectorSignedAbsoluteDifference16, a, b); - case 32: - return Inst(Opcode::VectorSignedAbsoluteDifference32, a, b); - } - UNREACHABLE(); -} - -UpperAndLower IREmitter::VectorSignedMultiply(size_t esize, const U128& a, const U128& b) { - const Value multiply = [&] { - switch (esize) { - case 16: - return Inst(Opcode::VectorSignedMultiply16, a, b); - case 32: - return Inst(Opcode::VectorSignedMultiply32, a, b); - } - UNREACHABLE(); - }(); - - return { - Inst(Opcode::GetUpperFromOp, multiply), - Inst(Opcode::GetLowerFromOp, multiply), - }; -} - -U128 IREmitter::VectorSignedSaturatedAbs(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedAbs8, a); - case 16: - return Inst(Opcode::VectorSignedSaturatedAbs16, a); - case 32: - return Inst(Opcode::VectorSignedSaturatedAbs32, a); - case 64: - return Inst(Opcode::VectorSignedSaturatedAbs64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedAccumulateUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned8, a, b); - case 16: - return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned32, a, b); - case 64: - return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedDoublingMultiplyHigh(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 16: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHigh16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHigh32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorSignedSaturatedDoublingMultiplyHighRounding(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 16: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHighRounding16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHighRounding32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorSignedSaturatedDoublingMultiplyLong(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 16: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyLong16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyLong32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedNarrowToSigned(size_t original_esize, const U128& a) { - switch (original_esize) { - case 16: - return Inst(Opcode::VectorSignedSaturatedNarrowToSigned16, a); - case 32: - return Inst(Opcode::VectorSignedSaturatedNarrowToSigned32, a); - case 64: - return Inst(Opcode::VectorSignedSaturatedNarrowToSigned64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedNarrowToUnsigned(size_t original_esize, const U128& a) { - switch (original_esize) { - case 16: - return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned16, a); - case 32: - return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned32, a); - case 64: - return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedNeg(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedNeg8, a); - case 16: - return Inst(Opcode::VectorSignedSaturatedNeg16, a); - case 32: - return Inst(Opcode::VectorSignedSaturatedNeg32, a); - case 64: - return Inst(Opcode::VectorSignedSaturatedNeg64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedShiftLeft8, a, b); - case 16: - return Inst(Opcode::VectorSignedSaturatedShiftLeft16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedShiftLeft32, a, b); - case 64: - return Inst(Opcode::VectorSignedSaturatedShiftLeft64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedShiftLeftUnsigned(size_t esize, const U128& a, u8 shift_amount) { - ASSERT(shift_amount < esize); - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned8, a, Imm8(shift_amount)); - case 16: - return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned16, a, Imm8(shift_amount)); - case 32: - return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned32, a, Imm8(shift_amount)); - case 64: - return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned64, a, Imm8(shift_amount)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSub(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSub8, a, b); - case 16: - return Inst(Opcode::VectorSub16, a, b); - case 32: - return Inst(Opcode::VectorSub32, a, b); - case 64: - return Inst(Opcode::VectorSub64, a, b); - } - UNREACHABLE(); -} - -Table IREmitter::VectorTable(std::vector values) { - ASSERT(values.size() >= 1 && values.size() <= 4); - values.resize(4); - return Inst(Opcode::VectorTable, values[0], values[1], values[2], values[3]); -} - -Table IREmitter::VectorTable(std::vector values) { - ASSERT(values.size() >= 1 && values.size() <= 4); - values.resize(4); - return Inst
(Opcode::VectorTable, values[0], values[1], values[2], values[3]); -} - -U64 IREmitter::VectorTableLookup(const U64& defaults, const Table& table, const U64& indices) { - ASSERT(table.GetInst()->GetArg(0).GetType() == Type::U64); - return Inst(Opcode::VectorTableLookup64, defaults, table, indices); -} - -U128 IREmitter::VectorTableLookup(const U128& defaults, const Table& table, const U128& indices) { - ASSERT(table.GetInst()->GetArg(0).GetType() == Type::U128); - return Inst(Opcode::VectorTableLookup128, defaults, table, indices); -} - -U128 IREmitter::VectorTranspose(size_t esize, const U128& a, const U128& b, bool part) { - switch (esize) { - case 8: - return Inst(Opcode::VectorTranspose8, a, b, Imm1(part)); - case 16: - return Inst(Opcode::VectorTranspose16, a, b, Imm1(part)); - case 32: - return Inst(Opcode::VectorTranspose32, a, b, Imm1(part)); - case 64: - return Inst(Opcode::VectorTranspose64, a, b, Imm1(part)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorUnsignedAbsoluteDifference(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorUnsignedAbsoluteDifference8, a, b); - case 16: - return Inst(Opcode::VectorUnsignedAbsoluteDifference16, a, b); - case 32: - return Inst(Opcode::VectorUnsignedAbsoluteDifference32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorUnsignedRecipEstimate(const U128& a) { - return Inst(Opcode::VectorUnsignedRecipEstimate, a); -} - -U128 IREmitter::VectorUnsignedRecipSqrtEstimate(const U128& a) { - return Inst(Opcode::VectorUnsignedRecipSqrtEstimate, a); -} - -U128 IREmitter::VectorUnsignedSaturatedAccumulateSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned8, a, b); - case 16: - return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned16, a, b); - case 32: - return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned32, a, b); - case 64: - return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorUnsignedSaturatedNarrow(size_t esize, const U128& a) { - switch (esize) { - case 16: - return Inst(Opcode::VectorUnsignedSaturatedNarrow16, a); - case 32: - return Inst(Opcode::VectorUnsignedSaturatedNarrow32, a); - case 64: - return Inst(Opcode::VectorUnsignedSaturatedNarrow64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorUnsignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorUnsignedSaturatedShiftLeft8, a, b); - case 16: - return Inst(Opcode::VectorUnsignedSaturatedShiftLeft16, a, b); - case 32: - return Inst(Opcode::VectorUnsignedSaturatedShiftLeft32, a, b); - case 64: - return Inst(Opcode::VectorUnsignedSaturatedShiftLeft64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorZeroExtend(size_t original_esize, const U128& a) { - switch (original_esize) { - case 8: - return Inst(Opcode::VectorZeroExtend8, a); - case 16: - return Inst(Opcode::VectorZeroExtend16, a); - case 32: - return Inst(Opcode::VectorZeroExtend32, a); - case 64: - return Inst(Opcode::VectorZeroExtend64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorZeroUpper(const U128& a) { - return Inst(Opcode::VectorZeroUpper, a); -} - -U128 IREmitter::ZeroVector() { - return Inst(Opcode::ZeroVector); -} - -U16U32U64 IREmitter::FPAbs(const U16U32U64& a) { - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPAbs16, a); - case Type::U32: - return Inst(Opcode::FPAbs32, a); - case Type::U64: - return Inst(Opcode::FPAbs64, a); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPAdd(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPAdd32, a, b); - case Type::U64: - return Inst(Opcode::FPAdd64, a, b); - default: - UNREACHABLE(); - } -} - -NZCV IREmitter::FPCompare(const U32U64& a, const U32U64& b, bool exc_on_qnan) { - ASSERT(a.GetType() == b.GetType()); - - const IR::U1 exc_on_qnan_imm = Imm1(exc_on_qnan); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPCompare32, a, b, exc_on_qnan_imm); - case Type::U64: - return Inst(Opcode::FPCompare64, a, b, exc_on_qnan_imm); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPDiv(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPDiv32, a, b); - case Type::U64: - return Inst(Opcode::FPDiv64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMax(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMax32, a, b); - case Type::U64: - return Inst(Opcode::FPMax64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMaxNumeric(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMaxNumeric32, a, b); - case Type::U64: - return Inst(Opcode::FPMaxNumeric64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMin(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMin32, a, b); - case Type::U64: - return Inst(Opcode::FPMin64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMinNumeric(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMinNumeric32, a, b); - case Type::U64: - return Inst(Opcode::FPMinNumeric64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMul(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMul32, a, b); - case Type::U64: - return Inst(Opcode::FPMul64, a, b); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPMulAdd(const U16U32U64& a, const U16U32U64& b, const U16U32U64& c) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPMulAdd16, a, b, c); - case Type::U32: - return Inst(Opcode::FPMulAdd32, a, b, c); - case Type::U64: - return Inst(Opcode::FPMulAdd64, a, b, c); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPMulSub(const U16U32U64& a, const U16U32U64& b, const U16U32U64& c) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPMulSub16, a, b, c); - case Type::U32: - return Inst(Opcode::FPMulSub32, a, b, c); - case Type::U64: - return Inst(Opcode::FPMulSub64, a, b, c); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMulX(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMulX32, a, b); - case Type::U64: - return Inst(Opcode::FPMulX64, a, b); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPNeg(const U16U32U64& a) { - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPNeg16, a); - case Type::U32: - return Inst(Opcode::FPNeg32, a); - case Type::U64: - return Inst(Opcode::FPNeg64, a); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRecipEstimate(const U16U32U64& a) { - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRecipEstimate16, a); - case Type::U32: - return Inst(Opcode::FPRecipEstimate32, a); - case Type::U64: - return Inst(Opcode::FPRecipEstimate64, a); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRecipExponent(const U16U32U64& a) { - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRecipExponent16, a); - case Type::U32: - return Inst(Opcode::FPRecipExponent32, a); - case Type::U64: - return Inst(Opcode::FPRecipExponent64, a); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRecipStepFused(const U16U32U64& a, const U16U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRecipStepFused16, a, b); - case Type::U32: - return Inst(Opcode::FPRecipStepFused32, a, b); - case Type::U64: - return Inst(Opcode::FPRecipStepFused64, a, b); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRoundInt(const U16U32U64& a, FP::RoundingMode rounding, bool exact) { - const u8 rounding_value = static_cast(rounding); - const IR::U1 exact_imm = Imm1(exact); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRoundInt16, a, rounding_value, exact_imm); - case Type::U32: - return Inst(Opcode::FPRoundInt32, a, rounding_value, exact_imm); - case Type::U64: - return Inst(Opcode::FPRoundInt64, a, rounding_value, exact_imm); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRSqrtEstimate(const U16U32U64& a) { - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRSqrtEstimate16, a); - case Type::U32: - return Inst(Opcode::FPRSqrtEstimate32, a); - case Type::U64: - return Inst(Opcode::FPRSqrtEstimate64, a); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRSqrtStepFused(const U16U32U64& a, const U16U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRSqrtStepFused16, a, b); - case Type::U32: - return Inst(Opcode::FPRSqrtStepFused32, a, b); - case Type::U64: - return Inst(Opcode::FPRSqrtStepFused64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPSqrt(const U32U64& a) { - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPSqrt32, a); - case Type::U64: - return Inst(Opcode::FPSqrt64, a); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPSub(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPSub32, a, b); - case Type::U64: - return Inst(Opcode::FPSub64, a, b); - default: - UNREACHABLE(); - } -} - -U16 IREmitter::FPDoubleToHalf(const U64& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPDoubleToHalf, a, Imm8(static_cast(rounding))); -} - -U32 IREmitter::FPDoubleToSingle(const U64& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPDoubleToSingle, a, Imm8(static_cast(rounding))); -} - -U64 IREmitter::FPHalfToDouble(const U16& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPHalfToDouble, a, Imm8(static_cast(rounding))); -} - -U32 IREmitter::FPHalfToSingle(const U16& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPHalfToSingle, a, Imm8(static_cast(rounding))); -} - -U64 IREmitter::FPSingleToDouble(const U32& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPSingleToDouble, a, Imm8(static_cast(rounding))); -} - -U16 IREmitter::FPSingleToHalf(const U32& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPSingleToHalf, a, Imm8(static_cast(rounding))); -} - -U16 IREmitter::FPToFixedS16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 16); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedS16, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedS16, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedS16, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::FPToFixedS32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 32); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedS32, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedS32, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedS32, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U64 IREmitter::FPToFixedS64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 64); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedS64, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedS64, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedS64, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U16 IREmitter::FPToFixedU16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 16); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedU16, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedU16, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedU16, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::FPToFixedU32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 32); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedU32, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedU32, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedU32, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U64 IREmitter::FPToFixedU64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 64); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedU64, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedU64, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedU64, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::FPSignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); - - const IR::U8 fbits_imm = Imm8(static_cast(fbits)); - const IR::U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPFixedS16ToSingle, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPFixedS32ToSingle, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPFixedS64ToSingle, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::FPUnsignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); - - const IR::U8 fbits_imm = Imm8(static_cast(fbits)); - const IR::U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPFixedU16ToSingle, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPFixedU32ToSingle, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPFixedU64ToSingle, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U64 IREmitter::FPSignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); - - const IR::U8 fbits_imm = Imm8(static_cast(fbits)); - const IR::U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPFixedS16ToDouble, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPFixedS32ToDouble, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPFixedS64ToDouble, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U64 IREmitter::FPUnsignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); - - const IR::U8 fbits_imm = Imm8(static_cast(fbits)); - const IR::U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPFixedU16ToDouble, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPFixedU32ToDouble, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPFixedU64ToDouble, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::FPVectorAbs(size_t esize, const U128& a) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorAbs16, a); - case 32: - return Inst(Opcode::FPVectorAbs32, a); - case 64: - return Inst(Opcode::FPVectorAbs64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorAdd32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorAdd64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorDiv(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorDiv32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorDiv64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorEqual16, a, b, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorEqual32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorEqual64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorFromHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(esize == 32); - return Inst(Opcode::FPVectorFromHalf32, a, Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); -} - -U128 IREmitter::FPVectorFromSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(fbits <= esize); - switch (esize) { - case 32: - return Inst(Opcode::FPVectorFromSignedFixed32, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorFromSignedFixed64, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorFromUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(fbits <= esize); - switch (esize) { - case 32: - return Inst(Opcode::FPVectorFromUnsignedFixed32, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorFromUnsignedFixed64, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorGreater(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorGreater32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorGreater64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorGreaterEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorGreaterEqual32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorGreaterEqual64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMax(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMax32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMax64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMaxNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMaxNumeric32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMaxNumeric64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMin(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMin32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMin64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMinNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMinNumeric32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMinNumeric64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMul(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMul32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMul64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMulAdd(size_t esize, const U128& a, const U128& b, const U128& c, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorMulAdd16, a, b, c, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorMulAdd32, a, b, c, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMulAdd64, a, b, c, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMulX(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMulX32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMulX64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorNeg(size_t esize, const U128& a) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorNeg16, a); - case 32: - return Inst(Opcode::FPVectorNeg32, a); - case 64: - return Inst(Opcode::FPVectorNeg64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorPairedAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorPairedAdd32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorPairedAdd64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorPairedAddLower(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorPairedAddLower32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorPairedAddLower64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorRecipEstimate(size_t esize, const U128& a, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorRecipEstimate16, a, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorRecipEstimate32, a, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorRecipEstimate64, a, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorRecipStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorRecipStepFused16, a, b, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorRecipStepFused32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorRecipStepFused64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorRoundInt(size_t esize, const U128& operand, FP::RoundingMode rounding, bool exact, bool fpcr_controlled) { - const IR::U8 rounding_imm = Imm8(static_cast(rounding)); - const IR::U1 exact_imm = Imm1(exact); - - switch (esize) { - case 16: - return Inst(Opcode::FPVectorRoundInt16, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorRoundInt32, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorRoundInt64, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorRSqrtEstimate(size_t esize, const U128& a, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorRSqrtEstimate16, a, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorRSqrtEstimate32, a, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorRSqrtEstimate64, a, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorRSqrtStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorRSqrtStepFused16, a, b, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorRSqrtStepFused32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorRSqrtStepFused64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorSqrt(size_t esize, const U128& a, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorSqrt32, a, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorSqrt64, a, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorSub(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorSub32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorSub64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorToHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(esize == 32); - return Inst(Opcode::FPVectorToHalf32, a, Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); -} - -U128 IREmitter::FPVectorToSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(fbits <= esize); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (esize) { - case 16: - return Inst(Opcode::FPVectorToSignedFixed16, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorToSignedFixed32, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorToSignedFixed64, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - } - - UNREACHABLE(); -} - -U128 IREmitter::FPVectorToUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(fbits <= esize); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (esize) { - case 16: - return Inst(Opcode::FPVectorToUnsignedFixed16, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorToUnsignedFixed32, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorToUnsignedFixed64, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - } - - UNREACHABLE(); -} - -void IREmitter::Breakpoint() { - Inst(Opcode::Breakpoint); -} - -void IREmitter::CallHostFunction(void (*fn)(void)) { - Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), Value{}, Value{}, Value{}); -} - -void IREmitter::CallHostFunction(void (*fn)(u64), const U64& arg1) { - Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, Value{}, Value{}); -} - -void IREmitter::CallHostFunction(void (*fn)(u64, u64), const U64& arg1, const U64& arg2) { - Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, arg2, Value{}); -} - -void IREmitter::CallHostFunction(void (*fn)(u64, u64, u64), const U64& arg1, const U64& arg2, const U64& arg3) { - Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, arg2, arg3); -} - -void IREmitter::SetTerm(const Terminal& terminal) { - block.SetTerminal(terminal); -} } // namespace Dynarmic::IR diff --git a/externals/dynarmic/src/dynarmic/ir/ir_emitter.h b/externals/dynarmic/src/dynarmic/ir/ir_emitter.h index d37df24572..23cfb47498 100644 --- a/externals/dynarmic/src/dynarmic/ir/ir_emitter.h +++ b/externals/dynarmic/src/dynarmic/ir/ir_emitter.h @@ -5,8 +5,13 @@ #pragma once -#include +#include +#include +#include +#include + +#include "dynarmic/ir/opcodes.h" #include "dynarmic/ir/acc_type.h" #include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/location_descriptor.h" @@ -69,337 +74,2878 @@ public: Block& block; - U1 Imm1(bool value) const; - U8 Imm8(u8 value) const; - U16 Imm16(u16 value) const; - U32 Imm32(u32 value) const; - U64 Imm64(u64 value) const; + U1 Imm1(bool imm1) const { + return U1(Value(imm1)); + } - void PushRSB(const LocationDescriptor& return_location); + U8 Imm8(u8 imm8) const { + return U8(Value(imm8)); + } - U64 Pack2x32To1x64(const U32& lo, const U32& hi); - U128 Pack2x64To1x128(const U64& lo, const U64& hi); - UAny LeastSignificant(size_t bitsize, const U32U64& value); - U32 LeastSignificantWord(const U64& value); - U16 LeastSignificantHalf(U32U64 value); - U8 LeastSignificantByte(U32U64 value); - ResultAndCarry MostSignificantWord(const U64& value); - U1 MostSignificantBit(const U32& value); - U1 IsZero(const U32& value); - U1 IsZero(const U64& value); - U1 IsZero(const U32U64& value); - U1 TestBit(const U32U64& value, const U8& bit); - U32 ConditionalSelect(Cond cond, const U32& a, const U32& b); - U64 ConditionalSelect(Cond cond, const U64& a, const U64& b); - NZCV ConditionalSelect(Cond cond, const NZCV& a, const NZCV& b); - U32U64 ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b); + U16 Imm16(u16 imm16) const { + return U16(Value(imm16)); + } - U1 GetCFlagFromNZCV(const NZCV& nzcv); - NZCV NZCVFromPackedFlags(const U32& a); - // This pseudo-instruction may only be added to instructions that support it. - NZCV NZCVFrom(const Value& value); + U32 Imm32(u32 imm32) const { + return U32(Value(imm32)); + } - ResultAndCarry LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in); - ResultAndCarry LogicalShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in); - ResultAndCarry ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in); - ResultAndCarry RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in); - U32U64 LogicalShiftLeft(const U32U64& value_in, const U8& shift_amount); - U32U64 LogicalShiftRight(const U32U64& value_in, const U8& shift_amount); - U32U64 ArithmeticShiftRight(const U32U64& value_in, const U8& shift_amount); - U32U64 RotateRight(const U32U64& value_in, const U8& shift_amount); - U32U64 LogicalShiftLeftMasked(const U32U64& value_in, const U32U64& shift_amount); - U32U64 LogicalShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount); - U32U64 ArithmeticShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount); - U32U64 RotateRightMasked(const U32U64& value_in, const U32U64& shift_amount); - ResultAndCarry RotateRightExtended(const U32& value_in, const U1& carry_in); - U32U64 AddWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in); - U32U64 SubWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in); - U32U64 Add(const U32U64& a, const U32U64& b); - U32U64 Sub(const U32U64& a, const U32U64& b); - U32U64 Mul(const U32U64& a, const U32U64& b); - U64 UnsignedMultiplyHigh(const U64& a, const U64& b); - U64 SignedMultiplyHigh(const U64& a, const U64& b); - U32U64 UnsignedDiv(const U32U64& a, const U32U64& b); - U32U64 SignedDiv(const U32U64& a, const U32U64& b); - U32U64 And(const U32U64& a, const U32U64& b); - U32U64 AndNot(const U32U64& a, const U32U64& b); - U32U64 Eor(const U32U64& a, const U32U64& b); - U32U64 Or(const U32U64& a, const U32U64& b); - U32U64 Not(const U32U64& a); - U32 SignExtendToWord(const UAny& a); - U64 SignExtendToLong(const UAny& a); - U32 SignExtendByteToWord(const U8& a); - U32 SignExtendHalfToWord(const U16& a); - U64 SignExtendWordToLong(const U32& a); - U32 ZeroExtendToWord(const UAny& a); - U64 ZeroExtendToLong(const UAny& a); - U128 ZeroExtendToQuad(const UAny& a); - U32 ZeroExtendByteToWord(const U8& a); - U32 ZeroExtendHalfToWord(const U16& a); - U64 ZeroExtendWordToLong(const U32& a); - U32 IndeterminateExtendToWord(const UAny& a); - U64 IndeterminateExtendToLong(const UAny& a); - U32 ByteReverseWord(const U32& a); - U16 ByteReverseHalf(const U16& a); - U64 ByteReverseDual(const U64& a); - U32U64 CountLeadingZeros(const U32U64& a); - U32U64 ExtractRegister(const U32U64& a, const U32U64& b, const U8& lsb); - U32U64 ReplicateBit(const U32U64& a, u8 bit); - U32U64 MaxSigned(const U32U64& a, const U32U64& b); - U32U64 MaxUnsigned(const U32U64& a, const U32U64& b); - U32U64 MinSigned(const U32U64& a, const U32U64& b); - U32U64 MinUnsigned(const U32U64& a, const U32U64& b); + U64 Imm64(u64 imm64) const { + return U64(Value(imm64)); + } - ResultAndOverflow SignedSaturatedAddWithFlag(const U32& a, const U32& b); - ResultAndOverflow SignedSaturatedSubWithFlag(const U32& a, const U32& b); - ResultAndOverflow SignedSaturation(const U32& a, size_t bit_size_to_saturate_to); - ResultAndOverflow UnsignedSaturation(const U32& a, size_t bit_size_to_saturate_to); + void PushRSB(const LocationDescriptor& return_location) { + Inst(Opcode::PushRSB, IR::Value(return_location.Value())); + } - UAny SignedSaturatedAdd(const UAny& a, const UAny& b); - UAny SignedSaturatedDoublingMultiplyReturnHigh(const UAny& a, const UAny& b); - UAny SignedSaturatedSub(const UAny& a, const UAny& b); - UAny UnsignedSaturatedAdd(const UAny& a, const UAny& b); - UAny UnsignedSaturatedSub(const UAny& a, const UAny& b); + U64 Pack2x32To1x64(const U32& lo, const U32& hi) { + return Inst(Opcode::Pack2x32To1x64, lo, hi); + } - U128 VectorSignedSaturatedAdd(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedSub(size_t esize, const U128& a, const U128& b); - U128 VectorUnsignedSaturatedAdd(size_t esize, const U128& a, const U128& b); - U128 VectorUnsignedSaturatedSub(size_t esize, const U128& a, const U128& b); + U128 Pack2x64To1x128(const U64& lo, const U64& hi) { + return Inst(Opcode::Pack2x64To1x128, lo, hi); + } - ResultAndGE PackedAddU8(const U32& a, const U32& b); - ResultAndGE PackedAddS8(const U32& a, const U32& b); - ResultAndGE PackedAddU16(const U32& a, const U32& b); - ResultAndGE PackedAddS16(const U32& a, const U32& b); - ResultAndGE PackedSubU8(const U32& a, const U32& b); - ResultAndGE PackedSubS8(const U32& a, const U32& b); - ResultAndGE PackedSubU16(const U32& a, const U32& b); - ResultAndGE PackedSubS16(const U32& a, const U32& b); - ResultAndGE PackedAddSubU16(const U32& a, const U32& b); - ResultAndGE PackedAddSubS16(const U32& a, const U32& b); - ResultAndGE PackedSubAddU16(const U32& a, const U32& b); - ResultAndGE PackedSubAddS16(const U32& a, const U32& b); - U32 PackedHalvingAddU8(const U32& a, const U32& b); - U32 PackedHalvingAddS8(const U32& a, const U32& b); - U32 PackedHalvingSubU8(const U32& a, const U32& b); - U32 PackedHalvingSubS8(const U32& a, const U32& b); - U32 PackedHalvingAddU16(const U32& a, const U32& b); - U32 PackedHalvingAddS16(const U32& a, const U32& b); - U32 PackedHalvingSubU16(const U32& a, const U32& b); - U32 PackedHalvingSubS16(const U32& a, const U32& b); - U32 PackedHalvingAddSubU16(const U32& a, const U32& b); - U32 PackedHalvingAddSubS16(const U32& a, const U32& b); - U32 PackedHalvingSubAddU16(const U32& a, const U32& b); - U32 PackedHalvingSubAddS16(const U32& a, const U32& b); - U32 PackedSaturatedAddU8(const U32& a, const U32& b); - U32 PackedSaturatedAddS8(const U32& a, const U32& b); - U32 PackedSaturatedSubU8(const U32& a, const U32& b); - U32 PackedSaturatedSubS8(const U32& a, const U32& b); - U32 PackedSaturatedAddU16(const U32& a, const U32& b); - U32 PackedSaturatedAddS16(const U32& a, const U32& b); - U32 PackedSaturatedSubU16(const U32& a, const U32& b); - U32 PackedSaturatedSubS16(const U32& a, const U32& b); - U32 PackedAbsDiffSumU8(const U32& a, const U32& b); - U32 PackedSelect(const U32& ge, const U32& a, const U32& b); + UAny LeastSignificant(size_t bitsize, const U32U64& value) { + switch (bitsize) { + case 8: + return LeastSignificantByte(value); + case 16: + return LeastSignificantHalf(value); + case 32: + if (value.GetType() == Type::U32) { + return value; + } + return LeastSignificantWord(value); + case 64: + ASSERT(value.GetType() == Type::U64); + return value; + } + ASSERT_FALSE("Invalid bitsize"); + } - U32 CRC32Castagnoli8(const U32& a, const U32& b); - U32 CRC32Castagnoli16(const U32& a, const U32& b); - U32 CRC32Castagnoli32(const U32& a, const U32& b); - U32 CRC32Castagnoli64(const U32& a, const U64& b); - U32 CRC32ISO8(const U32& a, const U32& b); - U32 CRC32ISO16(const U32& a, const U32& b); - U32 CRC32ISO32(const U32& a, const U32& b); - U32 CRC32ISO64(const U32& a, const U64& b); + U32 LeastSignificantWord(const U64& value) { + return Inst(Opcode::LeastSignificantWord, value); + } - U128 AESDecryptSingleRound(const U128& a); - U128 AESEncryptSingleRound(const U128& a); - U128 AESInverseMixColumns(const U128& a); - U128 AESMixColumns(const U128& a); + U16 LeastSignificantHalf(U32U64 value) { + if (value.GetType() == Type::U64) { + value = LeastSignificantWord(value); + } + return Inst(Opcode::LeastSignificantHalf, value); + } - U8 SM4AccessSubstitutionBox(const U8& a); + U8 LeastSignificantByte(U32U64 value) { + if (value.GetType() == Type::U64) { + value = LeastSignificantWord(value); + } + return Inst(Opcode::LeastSignificantByte, value); + } - U128 SHA256Hash(const U128& x, const U128& y, const U128& w, bool part1); - U128 SHA256MessageSchedule0(const U128& x, const U128& y); - U128 SHA256MessageSchedule1(const U128& x, const U128& y, const U128& z); + ResultAndCarry MostSignificantWord(const U64& value) { + const auto result = Inst(Opcode::MostSignificantWord, value); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } - UAny VectorGetElement(size_t esize, const U128& a, size_t index); - U128 VectorSetElement(size_t esize, const U128& a, size_t index, const UAny& elem); - U128 VectorAbs(size_t esize, const U128& a); - U128 VectorAdd(size_t esize, const U128& a, const U128& b); - U128 VectorAnd(const U128& a, const U128& b); - U128 VectorAndNot(const U128& a, const U128& b); - U128 VectorArithmeticShiftRight(size_t esize, const U128& a, u8 shift_amount); - U128 VectorArithmeticVShift(size_t esize, const U128& a, const U128& b); - U128 VectorBroadcast(size_t esize, const UAny& a); - U128 VectorBroadcastLower(size_t esize, const UAny& a); - U128 VectorBroadcastElement(size_t esize, const U128& a, size_t index); - U128 VectorBroadcastElementLower(size_t esize, const U128& a, size_t index); - U128 VectorCountLeadingZeros(size_t esize, const U128& a); - U128 VectorEor(const U128& a, const U128& b); - U128 VectorDeinterleaveEven(size_t esize, const U128& a, const U128& b); - U128 VectorDeinterleaveEvenLower(size_t esize, const U128& a, const U128& b); - U128 VectorDeinterleaveOdd(size_t esize, const U128& a, const U128& b); - U128 VectorDeinterleaveOddLower(size_t esize, const U128& a, const U128& b); - U128 VectorEqual(size_t esize, const U128& a, const U128& b); - U128 VectorExtract(const U128& a, const U128& b, size_t position); - U128 VectorExtractLower(const U128& a, const U128& b, size_t position); - U128 VectorGreaterEqualSigned(size_t esize, const U128& a, const U128& b); - U128 VectorGreaterEqualUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorGreaterSigned(size_t esize, const U128& a, const U128& b); - U128 VectorGreaterUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorHalvingAddSigned(size_t esize, const U128& a, const U128& b); - U128 VectorHalvingAddUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorHalvingSubSigned(size_t esize, const U128& a, const U128& b); - U128 VectorHalvingSubUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorInterleaveLower(size_t esize, const U128& a, const U128& b); - U128 VectorInterleaveUpper(size_t esize, const U128& a, const U128& b); - U128 VectorLessEqualSigned(size_t esize, const U128& a, const U128& b); - U128 VectorLessEqualUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorLessSigned(size_t esize, const U128& a, const U128& b); - U128 VectorLessUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorLogicalShiftLeft(size_t esize, const U128& a, u8 shift_amount); - U128 VectorLogicalShiftRight(size_t esize, const U128& a, u8 shift_amount); - U128 VectorLogicalVShift(size_t esize, const U128& a, const U128& b); - U128 VectorMaxSigned(size_t esize, const U128& a, const U128& b); - U128 VectorMaxUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorMinSigned(size_t esize, const U128& a, const U128& b); - U128 VectorMinUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorMultiply(size_t esize, const U128& a, const U128& b); - U128 VectorMultiplySignedWiden(size_t esize, const U128& a, const U128& b); - U128 VectorMultiplyUnsignedWiden(size_t esize, const U128& a, const U128& b); - U128 VectorNarrow(size_t original_esize, const U128& a); - U128 VectorNot(const U128& a); - U128 VectorOr(const U128& a, const U128& b); - U128 VectorPairedAdd(size_t esize, const U128& a, const U128& b); - U128 VectorPairedAddLower(size_t esize, const U128& a, const U128& b); - U128 VectorPairedAddSignedWiden(size_t original_esize, const U128& a); - U128 VectorPairedAddUnsignedWiden(size_t original_esize, const U128& a); - U128 VectorPairedMaxSigned(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMaxUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMinSigned(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMinUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMaxSignedLower(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMaxUnsignedLower(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMinSignedLower(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMinUnsignedLower(size_t esize, const U128& a, const U128& b); - U128 VectorPolynomialMultiply(const U128& a, const U128& b); - U128 VectorPolynomialMultiplyLong(size_t esize, const U128& a, const U128& b); - U128 VectorPopulationCount(const U128& a); - U128 VectorReverseBits(const U128& a); - U128 VectorReverseElementsInHalfGroups(size_t esize, const U128& a); - U128 VectorReverseElementsInWordGroups(size_t esize, const U128& a); - U128 VectorReverseElementsInLongGroups(size_t esize, const U128& a); - U128 VectorReduceAdd(size_t esize, const U128& a); - U128 VectorRotateLeft(size_t esize, const U128& a, u8 amount); - U128 VectorRotateRight(size_t esize, const U128& a, u8 amount); - U128 VectorRotateWholeVectorRight(const U128& a, u8 amount); - U128 VectorRoundingHalvingAddSigned(size_t esize, const U128& a, const U128& b); - U128 VectorRoundingHalvingAddUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorRoundingShiftLeftSigned(size_t esize, const U128& a, const U128& b); - U128 VectorRoundingShiftLeftUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorSignExtend(size_t original_esize, const U128& a); - U128 VectorSignedAbsoluteDifference(size_t esize, const U128& a, const U128& b); - UpperAndLower VectorSignedMultiply(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedAbs(size_t esize, const U128& a); - U128 VectorSignedSaturatedAccumulateUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedDoublingMultiplyHigh(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedDoublingMultiplyHighRounding(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedDoublingMultiplyLong(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedNarrowToSigned(size_t original_esize, const U128& a); - U128 VectorSignedSaturatedNarrowToUnsigned(size_t original_esize, const U128& a); - U128 VectorSignedSaturatedNeg(size_t esize, const U128& a); - U128 VectorSignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedShiftLeftUnsigned(size_t esize, const U128& a, u8 shift_amount); - U128 VectorSub(size_t esize, const U128& a, const U128& b); - Table VectorTable(std::vector values); - Table VectorTable(std::vector values); - U64 VectorTableLookup(const U64& defaults, const Table& table, const U64& indices); - U128 VectorTableLookup(const U128& defaults, const Table& table, const U128& indices); - U128 VectorTranspose(size_t esize, const U128& a, const U128& b, bool part); - U128 VectorUnsignedAbsoluteDifference(size_t esize, const U128& a, const U128& b); - U128 VectorUnsignedRecipEstimate(const U128& a); - U128 VectorUnsignedRecipSqrtEstimate(const U128& a); - U128 VectorUnsignedSaturatedAccumulateSigned(size_t esize, const U128& a, const U128& b); - U128 VectorUnsignedSaturatedNarrow(size_t esize, const U128& a); - U128 VectorUnsignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b); - U128 VectorZeroExtend(size_t original_esize, const U128& a); - U128 VectorZeroUpper(const U128& a); - U128 ZeroVector(); + U1 MostSignificantBit(const U32& value) { + return Inst(Opcode::MostSignificantBit, value); + } - U16U32U64 FPAbs(const U16U32U64& a); - U32U64 FPAdd(const U32U64& a, const U32U64& b); - NZCV FPCompare(const U32U64& a, const U32U64& b, bool exc_on_qnan); - U32U64 FPDiv(const U32U64& a, const U32U64& b); - U32U64 FPMax(const U32U64& a, const U32U64& b); - U32U64 FPMaxNumeric(const U32U64& a, const U32U64& b); - U32U64 FPMin(const U32U64& a, const U32U64& b); - U32U64 FPMinNumeric(const U32U64& a, const U32U64& b); - U32U64 FPMul(const U32U64& a, const U32U64& b); - U16U32U64 FPMulAdd(const U16U32U64& addend, const U16U32U64& op1, const U16U32U64& op2); - U16U32U64 FPMulSub(const U16U32U64& minuend, const U16U32U64& op1, const U16U32U64& op2); - U32U64 FPMulX(const U32U64& a, const U32U64& b); - U16U32U64 FPNeg(const U16U32U64& a); - U16U32U64 FPRecipEstimate(const U16U32U64& a); - U16U32U64 FPRecipExponent(const U16U32U64& a); - U16U32U64 FPRecipStepFused(const U16U32U64& a, const U16U32U64& b); - U16U32U64 FPRoundInt(const U16U32U64& a, FP::RoundingMode rounding, bool exact); - U16U32U64 FPRSqrtEstimate(const U16U32U64& a); - U16U32U64 FPRSqrtStepFused(const U16U32U64& a, const U16U32U64& b); - U32U64 FPSqrt(const U32U64& a); - U32U64 FPSub(const U32U64& a, const U32U64& b); - U16 FPDoubleToHalf(const U64& a, FP::RoundingMode rounding); - U32 FPDoubleToSingle(const U64& a, FP::RoundingMode rounding); - U64 FPHalfToDouble(const U16& a, FP::RoundingMode rounding); - U32 FPHalfToSingle(const U16& a, FP::RoundingMode rounding); - U16 FPSingleToHalf(const U32& a, FP::RoundingMode rounding); - U64 FPSingleToDouble(const U32& a, FP::RoundingMode rounding); - U16 FPToFixedS16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U32 FPToFixedS32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U64 FPToFixedS64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U16 FPToFixedU16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U32 FPToFixedU32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U64 FPToFixedU64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U32 FPSignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U32 FPUnsignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U64 FPSignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U64 FPUnsignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); + U1 IsZero(const U32& value) { + return Inst(Opcode::IsZero32, value); + } - U128 FPVectorAbs(size_t esize, const U128& a); - U128 FPVectorAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorDiv(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorFromHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled = true); - U128 FPVectorFromSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true); - U128 FPVectorFromUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true); - U128 FPVectorGreater(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorGreaterEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMax(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMaxNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMin(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMinNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMul(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMulAdd(size_t esize, const U128& addend, const U128& op1, const U128& op2, bool fpcr_controlled = true); - U128 FPVectorMulX(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorNeg(size_t esize, const U128& a); - U128 FPVectorPairedAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorPairedAddLower(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorRecipEstimate(size_t esize, const U128& a, bool fpcr_controlled = true); - U128 FPVectorRecipStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorRoundInt(size_t esize, const U128& operand, FP::RoundingMode rounding, bool exact, bool fpcr_controlled = true); - U128 FPVectorRSqrtEstimate(size_t esize, const U128& a, bool fpcr_controlled = true); - U128 FPVectorRSqrtStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorSqrt(size_t esize, const U128& a, bool fpcr_controlled = true); - U128 FPVectorSub(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorToHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled = true); - U128 FPVectorToSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true); - U128 FPVectorToUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true); + U1 IsZero(const U64& value) { + return Inst(Opcode::IsZero64, value); + } - void Breakpoint(); - void CallHostFunction(void (*fn)(void)); - void CallHostFunction(void (*fn)(u64), const U64& arg1); - void CallHostFunction(void (*fn)(u64, u64), const U64& arg1, const U64& arg2); - void CallHostFunction(void (*fn)(u64, u64, u64), const U64& arg1, const U64& arg2, const U64& arg3); + U1 IsZero(const U32U64& value) { + if (value.GetType() == Type::U32) { + return Inst(Opcode::IsZero32, value); + } else { + return Inst(Opcode::IsZero64, value); + } + } - void SetTerm(const Terminal& terminal); + U1 TestBit(const U32U64& value, const U8& bit) { + if (value.GetType() == Type::U32) { + return Inst(Opcode::TestBit, IndeterminateExtendToLong(value), bit); + } else { + return Inst(Opcode::TestBit, value, bit); + } + } + + U32 ConditionalSelect(Cond cond, const U32& a, const U32& b) { + return Inst(Opcode::ConditionalSelect32, Value{cond}, a, b); + } + + U64 ConditionalSelect(Cond cond, const U64& a, const U64& b) { + return Inst(Opcode::ConditionalSelect64, Value{cond}, a, b); + } + + NZCV ConditionalSelect(Cond cond, const NZCV& a, const NZCV& b) { + return Inst(Opcode::ConditionalSelectNZCV, Value{cond}, a, b); + } + + U32U64 ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::ConditionalSelect32, Value{cond}, a, b); + } else { + return Inst(Opcode::ConditionalSelect64, Value{cond}, a, b); + } + } + + U1 GetCFlagFromNZCV(const NZCV& nzcv) { + return Inst(Opcode::GetCFlagFromNZCV, nzcv); + } + + NZCV NZCVFromPackedFlags(const U32& a) { + return Inst(Opcode::NZCVFromPackedFlags, a); + } + + NZCV NZCVFrom(const Value& value) { + return Inst(Opcode::GetNZCVFromOp, value); + } + + ResultAndCarry LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in) { + const auto result = Inst(Opcode::LogicalShiftLeft32, value_in, shift_amount, carry_in); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } + + ResultAndCarry LogicalShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { + const auto result = Inst(Opcode::LogicalShiftRight32, value_in, shift_amount, carry_in); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } + + ResultAndCarry ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { + const auto result = Inst(Opcode::ArithmeticShiftRight32, value_in, shift_amount, carry_in); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } + + ResultAndCarry RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { + const auto result = Inst(Opcode::RotateRight32, value_in, shift_amount, carry_in); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } + + ResultAndCarry RotateRightExtended(const U32& value_in, const U1& carry_in) { + const auto result = Inst(Opcode::RotateRightExtended, value_in, carry_in); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } + + U32U64 LogicalShiftLeft(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::LogicalShiftLeft32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::LogicalShiftLeft64, value_in, shift_amount); + } + } + + U32U64 LogicalShiftRight(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::LogicalShiftRight32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::LogicalShiftRight64, value_in, shift_amount); + } + } + + U32U64 ArithmeticShiftRight(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::ArithmeticShiftRight32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::ArithmeticShiftRight64, value_in, shift_amount); + } + } + + U32U64 RotateRight(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::RotateRight32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::RotateRight64, value_in, shift_amount); + } + } + + U32U64 LogicalShiftLeftMasked(const U32U64& value_in, const U32U64& shift_amount) { + ASSERT(value_in.GetType() == shift_amount.GetType()); + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::LogicalShiftLeftMasked32, value_in, shift_amount); + } else { + return Inst(Opcode::LogicalShiftLeftMasked64, value_in, shift_amount); + } + } + + U32U64 LogicalShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount) { + ASSERT(value_in.GetType() == shift_amount.GetType()); + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::LogicalShiftRightMasked32, value_in, shift_amount); + } else { + return Inst(Opcode::LogicalShiftRightMasked64, value_in, shift_amount); + } + } + + U32U64 ArithmeticShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount) { + ASSERT(value_in.GetType() == shift_amount.GetType()); + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::ArithmeticShiftRightMasked32, value_in, shift_amount); + } else { + return Inst(Opcode::ArithmeticShiftRightMasked64, value_in, shift_amount); + } + } + + U32U64 RotateRightMasked(const U32U64& value_in, const U32U64& shift_amount) { + ASSERT(value_in.GetType() == shift_amount.GetType()); + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::RotateRightMasked32, value_in, shift_amount); + } else { + return Inst(Opcode::RotateRightMasked64, value_in, shift_amount); + } + } + + U32U64 AddWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Add32, a, b, carry_in); + } else { + return Inst(Opcode::Add64, a, b, carry_in); + } + } + + U32U64 Add(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Add32, a, b, Imm1(0)); + } else { + return Inst(Opcode::Add64, a, b, Imm1(0)); + } + } + + U32U64 SubWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Sub32, a, b, carry_in); + } else { + return Inst(Opcode::Sub64, a, b, carry_in); + } + } + + U32U64 Sub(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Sub32, a, b, Imm1(1)); + } else { + return Inst(Opcode::Sub64, a, b, Imm1(1)); + } + } + + U32U64 Mul(const U32U64& a, const U32U64& b) { + if (a.GetType() == Type::U32) { + return Inst(Opcode::Mul32, a, b); + } + + return Inst(Opcode::Mul64, a, b); + } + + U64 UnsignedMultiplyHigh(const U64& a, const U64& b) { + return Inst(Opcode::UnsignedMultiplyHigh64, a, b); + } + + U64 SignedMultiplyHigh(const U64& a, const U64& b) { + return Inst(Opcode::SignedMultiplyHigh64, a, b); + } + + U32U64 UnsignedDiv(const U32U64& a, const U32U64& b) { + if (a.GetType() == Type::U32) { + return Inst(Opcode::UnsignedDiv32, a, b); + } + + return Inst(Opcode::UnsignedDiv64, a, b); + } + + U32U64 SignedDiv(const U32U64& a, const U32U64& b) { + if (a.GetType() == Type::U32) { + return Inst(Opcode::SignedDiv32, a, b); + } + + return Inst(Opcode::SignedDiv64, a, b); + } + + U32U64 And(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::And32, a, b); + } else { + return Inst(Opcode::And64, a, b); + } + } + + U32U64 AndNot(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::AndNot32, a, b); + } else { + return Inst(Opcode::AndNot64, a, b); + } + } + + U32U64 Eor(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Eor32, a, b); + } else { + return Inst(Opcode::Eor64, a, b); + } + } + + U32U64 Or(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Or32, a, b); + } else { + return Inst(Opcode::Or64, a, b); + } + } + + U32U64 Not(const U32U64& a) { + if (a.GetType() == Type::U32) { + return Inst(Opcode::Not32, a); + } else { + return Inst(Opcode::Not64, a); + } + } + + U64 SignExtendToLong(const UAny& a) { + switch (a.GetType()) { + case Type::U8: + return Inst(Opcode::SignExtendByteToLong, a); + case Type::U16: + return Inst(Opcode::SignExtendHalfToLong, a); + case Type::U32: + return Inst(Opcode::SignExtendWordToLong, a); + case Type::U64: + return U64(a); + default: + UNREACHABLE(); + } + } + + U32 SignExtendToWord(const UAny& a) { + switch (a.GetType()) { + case Type::U8: + return Inst(Opcode::SignExtendByteToWord, a); + case Type::U16: + return Inst(Opcode::SignExtendHalfToWord, a); + case Type::U32: + return U32(a); + case Type::U64: + return Inst(Opcode::LeastSignificantWord, a); + default: + UNREACHABLE(); + } + } + + U64 SignExtendWordToLong(const U32& a) { + return Inst(Opcode::SignExtendWordToLong, a); + } + + U32 SignExtendHalfToWord(const U16& a) { + return Inst(Opcode::SignExtendHalfToWord, a); + } + + U32 SignExtendByteToWord(const U8& a) { + return Inst(Opcode::SignExtendByteToWord, a); + } + + U64 ZeroExtendToLong(const UAny& a) { + switch (a.GetType()) { + case Type::U8: + return Inst(Opcode::ZeroExtendByteToLong, a); + case Type::U16: + return Inst(Opcode::ZeroExtendHalfToLong, a); + case Type::U32: + return Inst(Opcode::ZeroExtendWordToLong, a); + case Type::U64: + return U64(a); + default: + UNREACHABLE(); + } + } + + U32 ZeroExtendToWord(const UAny& a) { + switch (a.GetType()) { + case Type::U8: + return Inst(Opcode::ZeroExtendByteToWord, a); + case Type::U16: + return Inst(Opcode::ZeroExtendHalfToWord, a); + case Type::U32: + return U32(a); + case Type::U64: + return Inst(Opcode::LeastSignificantWord, a); + default: + UNREACHABLE(); + } + } + + U128 ZeroExtendToQuad(const UAny& a) { + return Inst(Opcode::ZeroExtendLongToQuad, ZeroExtendToLong(a)); + } + + U64 ZeroExtendWordToLong(const U32& a) { + return Inst(Opcode::ZeroExtendWordToLong, a); + } + + U32 ZeroExtendHalfToWord(const U16& a) { + return Inst(Opcode::ZeroExtendHalfToWord, a); + } + + U32 ZeroExtendByteToWord(const U8& a) { + return Inst(Opcode::ZeroExtendByteToWord, a); + } + + U32 IndeterminateExtendToWord(const UAny& a) { + // TODO: Implement properly + return ZeroExtendToWord(a); + } + + U64 IndeterminateExtendToLong(const UAny& a) { + // TODO: Implement properly + return ZeroExtendToLong(a); + } + + U32 ByteReverseWord(const U32& a) { + return Inst(Opcode::ByteReverseWord, a); + } + + U16 ByteReverseHalf(const U16& a) { + return Inst(Opcode::ByteReverseHalf, a); + } + + U64 ByteReverseDual(const U64& a) { + return Inst(Opcode::ByteReverseDual, a); + } + + U32U64 CountLeadingZeros(const U32U64& a) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::CountLeadingZeros32, a); + } + + return Inst(Opcode::CountLeadingZeros64, a); + } + + U32U64 ExtractRegister(const U32U64& a, const U32U64& b, const U8& lsb) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::ExtractRegister32, a, b, lsb); + } + + return Inst(Opcode::ExtractRegister64, a, b, lsb); + } + + U32U64 ReplicateBit(const U32U64& a, u8 bit) { + if (a.GetType() == IR::Type::U32) { + ASSERT(bit < 32); + return Inst(Opcode::ReplicateBit32, a, Imm8(bit)); + } + + ASSERT(bit < 64); + return Inst(Opcode::ReplicateBit64, a, Imm8(bit)); + } + + U32U64 MaxSigned(const U32U64& a, const U32U64& b) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::MaxSigned32, a, b); + } + + return Inst(Opcode::MaxSigned64, a, b); + } + + U32U64 MaxUnsigned(const U32U64& a, const U32U64& b) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::MaxUnsigned32, a, b); + } + + return Inst(Opcode::MaxUnsigned64, a, b); + } + + U32U64 MinSigned(const U32U64& a, const U32U64& b) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::MinSigned32, a, b); + } + + return Inst(Opcode::MinSigned64, a, b); + } + + U32U64 MinUnsigned(const U32U64& a, const U32U64& b) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::MinUnsigned32, a, b); + } + + return Inst(Opcode::MinUnsigned64, a, b); + } + + ResultAndOverflow SignedSaturatedAddWithFlag(const U32& a, const U32& b) { + const auto result = Inst(Opcode::SignedSaturatedAddWithFlag32, a, b); + const auto overflow = Inst(Opcode::GetOverflowFromOp, result); + return {result, overflow}; + } + + ResultAndOverflow SignedSaturatedSubWithFlag(const U32& a, const U32& b) { + const auto result = Inst(Opcode::SignedSaturatedSubWithFlag32, a, b); + const auto overflow = Inst(Opcode::GetOverflowFromOp, result); + return {result, overflow}; + } + + ResultAndOverflow SignedSaturation(const U32& a, size_t bit_size_to_saturate_to) { + ASSERT(bit_size_to_saturate_to >= 1 && bit_size_to_saturate_to <= 32); + const auto result = Inst(Opcode::SignedSaturation, a, Imm8(static_cast(bit_size_to_saturate_to))); + const auto overflow = Inst(Opcode::GetOverflowFromOp, result); + return {result, overflow}; + } + + ResultAndOverflow UnsignedSaturation(const U32& a, size_t bit_size_to_saturate_to) { + ASSERT(bit_size_to_saturate_to <= 31); + const auto result = Inst(Opcode::UnsignedSaturation, a, Imm8(static_cast(bit_size_to_saturate_to))); + const auto overflow = Inst(Opcode::GetOverflowFromOp, result); + return {result, overflow}; + } + + UAny SignedSaturatedAdd(const UAny& a, const UAny& b) { + ASSERT(a.GetType() == b.GetType()); + const auto result = [&]() -> IR::UAny { + switch (a.GetType()) { + case IR::Type::U8: + return Inst(Opcode::SignedSaturatedAdd8, a, b); + case IR::Type::U16: + return Inst(Opcode::SignedSaturatedAdd16, a, b); + case IR::Type::U32: + return Inst(Opcode::SignedSaturatedAdd32, a, b); + case IR::Type::U64: + return Inst(Opcode::SignedSaturatedAdd64, a, b); + default: + return IR::UAny{}; + } + }(); + return result; + } + + UAny SignedSaturatedDoublingMultiplyReturnHigh(const UAny& a, const UAny& b) { + ASSERT(a.GetType() == b.GetType()); + const auto result = [&]() -> IR::UAny { + switch (a.GetType()) { + case IR::Type::U16: + return Inst(Opcode::SignedSaturatedDoublingMultiplyReturnHigh16, a, b); + case IR::Type::U32: + return Inst(Opcode::SignedSaturatedDoublingMultiplyReturnHigh32, a, b); + default: + UNREACHABLE(); + } + }(); + return result; + } + + UAny SignedSaturatedSub(const UAny& a, const UAny& b) { + ASSERT(a.GetType() == b.GetType()); + const auto result = [&]() -> IR::UAny { + switch (a.GetType()) { + case IR::Type::U8: + return Inst(Opcode::SignedSaturatedSub8, a, b); + case IR::Type::U16: + return Inst(Opcode::SignedSaturatedSub16, a, b); + case IR::Type::U32: + return Inst(Opcode::SignedSaturatedSub32, a, b); + case IR::Type::U64: + return Inst(Opcode::SignedSaturatedSub64, a, b); + default: + return IR::UAny{}; + } + }(); + return result; + } + + UAny UnsignedSaturatedAdd(const UAny& a, const UAny& b) { + ASSERT(a.GetType() == b.GetType()); + const auto result = [&]() -> IR::UAny { + switch (a.GetType()) { + case IR::Type::U8: + return Inst(Opcode::UnsignedSaturatedAdd8, a, b); + case IR::Type::U16: + return Inst(Opcode::UnsignedSaturatedAdd16, a, b); + case IR::Type::U32: + return Inst(Opcode::UnsignedSaturatedAdd32, a, b); + case IR::Type::U64: + return Inst(Opcode::UnsignedSaturatedAdd64, a, b); + default: + return IR::UAny{}; + } + }(); + return result; + } + + UAny UnsignedSaturatedSub(const UAny& a, const UAny& b) { + ASSERT(a.GetType() == b.GetType()); + const auto result = [&]() -> IR::UAny { + switch (a.GetType()) { + case IR::Type::U8: + return Inst(Opcode::UnsignedSaturatedSub8, a, b); + case IR::Type::U16: + return Inst(Opcode::UnsignedSaturatedSub16, a, b); + case IR::Type::U32: + return Inst(Opcode::UnsignedSaturatedSub32, a, b); + case IR::Type::U64: + return Inst(Opcode::UnsignedSaturatedSub64, a, b); + default: + return IR::UAny{}; + } + }(); + return result; + } + + U128 VectorSignedSaturatedAdd(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedAdd8, a, b); + case 16: + return Inst(Opcode::VectorSignedSaturatedAdd16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedAdd32, a, b); + case 64: + return Inst(Opcode::VectorSignedSaturatedAdd64, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorSignedSaturatedSub(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedSub8, a, b); + case 16: + return Inst(Opcode::VectorSignedSaturatedSub16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedSub32, a, b); + case 64: + return Inst(Opcode::VectorSignedSaturatedSub64, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorUnsignedSaturatedAdd(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorUnsignedSaturatedAdd8, a, b); + case 16: + return Inst(Opcode::VectorUnsignedSaturatedAdd16, a, b); + case 32: + return Inst(Opcode::VectorUnsignedSaturatedAdd32, a, b); + case 64: + return Inst(Opcode::VectorUnsignedSaturatedAdd64, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorUnsignedSaturatedSub(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorUnsignedSaturatedSub8, a, b); + case 16: + return Inst(Opcode::VectorUnsignedSaturatedSub16, a, b); + case 32: + return Inst(Opcode::VectorUnsignedSaturatedSub32, a, b); + case 64: + return Inst(Opcode::VectorUnsignedSaturatedSub64, a, b); + default: + UNREACHABLE(); + } + } + + ResultAndGE PackedAddU8(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddU8, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedAddS8(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddS8, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedAddU16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddU16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedAddS16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddS16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubU8(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubU8, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubS8(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubS8, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubU16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubU16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubS16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubS16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedAddSubU16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddSubU16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedAddSubS16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddSubS16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubAddU16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubAddU16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubAddS16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubAddS16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + U32 PackedHalvingAddU8(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddU8, a, b); + } + + U32 PackedHalvingAddS8(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddS8, a, b); + } + + U32 PackedHalvingSubU8(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubU8, a, b); + } + + U32 PackedHalvingSubS8(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubS8, a, b); + } + + U32 PackedHalvingAddU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddU16, a, b); + } + + U32 PackedHalvingAddS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddS16, a, b); + } + + U32 PackedHalvingSubU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubU16, a, b); + } + + U32 PackedHalvingSubS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubS16, a, b); + } + + U32 PackedHalvingAddSubU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddSubU16, a, b); + } + + U32 PackedHalvingAddSubS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddSubS16, a, b); + } + + U32 PackedHalvingSubAddU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubAddU16, a, b); + } + + U32 PackedHalvingSubAddS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubAddS16, a, b); + } + + U32 PackedSaturatedAddU8(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedAddU8, a, b); + } + + U32 PackedSaturatedAddS8(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedAddS8, a, b); + } + + U32 PackedSaturatedSubU8(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedSubU8, a, b); + } + + U32 PackedSaturatedSubS8(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedSubS8, a, b); + } + + U32 PackedSaturatedAddU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedAddU16, a, b); + } + + U32 PackedSaturatedAddS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedAddS16, a, b); + } + + U32 PackedSaturatedSubU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedSubU16, a, b); + } + + U32 PackedSaturatedSubS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedSubS16, a, b); + } + + U32 PackedAbsDiffSumU8(const U32& a, const U32& b) { + return Inst(Opcode::PackedAbsDiffSumU8, a, b); + } + + U32 PackedSelect(const U32& ge, const U32& a, const U32& b) { + return Inst(Opcode::PackedSelect, ge, a, b); + } + + U32 CRC32Castagnoli8(const U32& a, const U32& b) { + return Inst(Opcode::CRC32Castagnoli8, a, b); + } + + U32 CRC32Castagnoli16(const U32& a, const U32& b) { + return Inst(Opcode::CRC32Castagnoli16, a, b); + } + + U32 CRC32Castagnoli32(const U32& a, const U32& b) { + return Inst(Opcode::CRC32Castagnoli32, a, b); + } + + U32 CRC32Castagnoli64(const U32& a, const U64& b) { + return Inst(Opcode::CRC32Castagnoli64, a, b); + } + + U32 CRC32ISO8(const U32& a, const U32& b) { + return Inst(Opcode::CRC32ISO8, a, b); + } + + U32 CRC32ISO16(const U32& a, const U32& b) { + return Inst(Opcode::CRC32ISO16, a, b); + } + + U32 CRC32ISO32(const U32& a, const U32& b) { + return Inst(Opcode::CRC32ISO32, a, b); + } + + U32 CRC32ISO64(const U32& a, const U64& b) { + return Inst(Opcode::CRC32ISO64, a, b); + } + + U128 AESDecryptSingleRound(const U128& a) { + return Inst(Opcode::AESDecryptSingleRound, a); + } + + U128 AESEncryptSingleRound(const U128& a) { + return Inst(Opcode::AESEncryptSingleRound, a); + } + + U128 AESInverseMixColumns(const U128& a) { + return Inst(Opcode::AESInverseMixColumns, a); + } + + U128 AESMixColumns(const U128& a) { + return Inst(Opcode::AESMixColumns, a); + } + + U8 SM4AccessSubstitutionBox(const U8& a) { + return Inst(Opcode::SM4AccessSubstitutionBox, a); + } + + U128 SHA256Hash(const U128& x, const U128& y, const U128& w, bool part1) { + return Inst(Opcode::SHA256Hash, x, y, w, Imm1(part1)); + } + + U128 SHA256MessageSchedule0(const U128& x, const U128& y) { + return Inst(Opcode::SHA256MessageSchedule0, x, y); + } + + U128 SHA256MessageSchedule1(const U128& x, const U128& y, const U128& z) { + return Inst(Opcode::SHA256MessageSchedule1, x, y, z); + } + + UAny VectorGetElement(size_t esize, const U128& a, size_t index) { + ASSERT_MSG(esize * index < 128, "Invalid index"); + switch (esize) { + case 8: + return Inst(Opcode::VectorGetElement8, a, Imm8(static_cast(index))); + case 16: + return Inst(Opcode::VectorGetElement16, a, Imm8(static_cast(index))); + case 32: + return Inst(Opcode::VectorGetElement32, a, Imm8(static_cast(index))); + case 64: + return Inst(Opcode::VectorGetElement64, a, Imm8(static_cast(index))); + default: + UNREACHABLE(); + } + } + + U128 VectorSetElement(size_t esize, const U128& a, size_t index, const IR::UAny& elem) { + ASSERT_MSG(esize * index < 128, "Invalid index"); + switch (esize) { + case 8: + return Inst(Opcode::VectorSetElement8, a, Imm8(static_cast(index)), elem); + case 16: + return Inst(Opcode::VectorSetElement16, a, Imm8(static_cast(index)), elem); + case 32: + return Inst(Opcode::VectorSetElement32, a, Imm8(static_cast(index)), elem); + case 64: + return Inst(Opcode::VectorSetElement64, a, Imm8(static_cast(index)), elem); + default: + UNREACHABLE(); + } + } + + U128 VectorAbs(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorAbs8, a); + case 16: + return Inst(Opcode::VectorAbs16, a); + case 32: + return Inst(Opcode::VectorAbs32, a); + case 64: + return Inst(Opcode::VectorAbs64, a); + } + UNREACHABLE(); + } + + U128 VectorAdd(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorAdd8, a, b); + case 16: + return Inst(Opcode::VectorAdd16, a, b); + case 32: + return Inst(Opcode::VectorAdd32, a, b); + case 64: + return Inst(Opcode::VectorAdd64, a, b); + } + UNREACHABLE(); + } + + U128 VectorAnd(const U128& a, const U128& b) { + return Inst(Opcode::VectorAnd, a, b); + } + + U128 VectorAndNot(const U128& a, const U128& b) { + return Inst(Opcode::VectorAndNot, a, b); + } + + U128 VectorArithmeticShiftRight(size_t esize, const U128& a, u8 shift_amount) { + switch (esize) { + case 8: + return Inst(Opcode::VectorArithmeticShiftRight8, a, Imm8(shift_amount)); + case 16: + return Inst(Opcode::VectorArithmeticShiftRight16, a, Imm8(shift_amount)); + case 32: + return Inst(Opcode::VectorArithmeticShiftRight32, a, Imm8(shift_amount)); + case 64: + return Inst(Opcode::VectorArithmeticShiftRight64, a, Imm8(shift_amount)); + } + UNREACHABLE(); + } + + U128 VectorArithmeticVShift(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorArithmeticVShift8, a, b); + case 16: + return Inst(Opcode::VectorArithmeticVShift16, a, b); + case 32: + return Inst(Opcode::VectorArithmeticVShift32, a, b); + case 64: + return Inst(Opcode::VectorArithmeticVShift64, a, b); + } + UNREACHABLE(); + } + + U128 VectorBroadcastLower(size_t esize, const UAny& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorBroadcastLower8, U8(a)); + case 16: + return Inst(Opcode::VectorBroadcastLower16, U16(a)); + case 32: + return Inst(Opcode::VectorBroadcastLower32, U32(a)); + } + UNREACHABLE(); + } + + U128 VectorBroadcast(size_t esize, const UAny& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorBroadcast8, U8(a)); + case 16: + return Inst(Opcode::VectorBroadcast16, U16(a)); + case 32: + return Inst(Opcode::VectorBroadcast32, U32(a)); + case 64: + return Inst(Opcode::VectorBroadcast64, U64(a)); + } + UNREACHABLE(); + } + + U128 VectorBroadcastElementLower(size_t esize, const U128& a, size_t index) { + ASSERT_MSG(esize * index < 128, "Invalid index"); + switch (esize) { + case 8: + return Inst(Opcode::VectorBroadcastElementLower8, a, u8(index)); + case 16: + return Inst(Opcode::VectorBroadcastElementLower16, a, u8(index)); + case 32: + return Inst(Opcode::VectorBroadcastElementLower32, a, u8(index)); + } + UNREACHABLE(); + } + + U128 VectorBroadcastElement(size_t esize, const U128& a, size_t index) { + ASSERT_MSG(esize * index < 128, "Invalid index"); + switch (esize) { + case 8: + return Inst(Opcode::VectorBroadcastElement8, a, u8(index)); + case 16: + return Inst(Opcode::VectorBroadcastElement16, a, u8(index)); + case 32: + return Inst(Opcode::VectorBroadcastElement32, a, u8(index)); + case 64: + return Inst(Opcode::VectorBroadcastElement64, a, u8(index)); + } + UNREACHABLE(); + } + + U128 VectorCountLeadingZeros(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorCountLeadingZeros8, a); + case 16: + return Inst(Opcode::VectorCountLeadingZeros16, a); + case 32: + return Inst(Opcode::VectorCountLeadingZeros32, a); + } + UNREACHABLE(); + } + + U128 VectorDeinterleaveEven(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorDeinterleaveEven8, a, b); + case 16: + return Inst(Opcode::VectorDeinterleaveEven16, a, b); + case 32: + return Inst(Opcode::VectorDeinterleaveEven32, a, b); + case 64: + return Inst(Opcode::VectorDeinterleaveEven64, a, b); + } + UNREACHABLE(); + } + + U128 VectorDeinterleaveOdd(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorDeinterleaveOdd8, a, b); + case 16: + return Inst(Opcode::VectorDeinterleaveOdd16, a, b); + case 32: + return Inst(Opcode::VectorDeinterleaveOdd32, a, b); + case 64: + return Inst(Opcode::VectorDeinterleaveOdd64, a, b); + } + UNREACHABLE(); + } + + U128 VectorDeinterleaveEvenLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorDeinterleaveEvenLower8, a, b); + case 16: + return Inst(Opcode::VectorDeinterleaveEvenLower16, a, b); + case 32: + return Inst(Opcode::VectorDeinterleaveEvenLower32, a, b); + } + UNREACHABLE(); + } + + U128 VectorDeinterleaveOddLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorDeinterleaveOddLower8, a, b); + case 16: + return Inst(Opcode::VectorDeinterleaveOddLower16, a, b); + case 32: + return Inst(Opcode::VectorDeinterleaveOddLower32, a, b); + } + UNREACHABLE(); + } + + U128 VectorEor(const U128& a, const U128& b) { + return Inst(Opcode::VectorEor, a, b); + } + + U128 VectorEqual(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorEqual8, a, b); + case 16: + return Inst(Opcode::VectorEqual16, a, b); + case 32: + return Inst(Opcode::VectorEqual32, a, b); + case 64: + return Inst(Opcode::VectorEqual64, a, b); + case 128: + return Inst(Opcode::VectorEqual128, a, b); + } + UNREACHABLE(); + } + + U128 VectorExtract(const U128& a, const U128& b, size_t position) { + ASSERT(position <= 128); + return Inst(Opcode::VectorExtract, a, b, Imm8(static_cast(position))); + } + + U128 VectorExtractLower(const U128& a, const U128& b, size_t position) { + ASSERT(position <= 64); + return Inst(Opcode::VectorExtractLower, a, b, Imm8(static_cast(position))); + } + + U128 VectorGreaterSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorGreaterS8, a, b); + case 16: + return Inst(Opcode::VectorGreaterS16, a, b); + case 32: + return Inst(Opcode::VectorGreaterS32, a, b); + case 64: + return Inst(Opcode::VectorGreaterS64, a, b); + } + UNREACHABLE(); + } + + U128 VectorGreaterEqualSigned(size_t esize, const U128& a, const U128& b) { + return VectorOr(VectorGreaterSigned(esize, a, b), VectorEqual(esize, a, b)); + } + + U128 VectorGreaterEqualUnsigned(size_t esize, const U128& a, const U128& b) { + return VectorEqual(esize, VectorMaxUnsigned(esize, a, b), a); + } + + U128 VectorGreaterUnsigned(size_t esize, const U128& a, const U128& b) { + return VectorNot(VectorEqual(esize, VectorMinUnsigned(esize, a, b), a)); + } + + U128 VectorHalvingAddSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorHalvingAddS8, a, b); + case 16: + return Inst(Opcode::VectorHalvingAddS16, a, b); + case 32: + return Inst(Opcode::VectorHalvingAddS32, a, b); + } + UNREACHABLE(); + } + + U128 VectorHalvingAddUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorHalvingAddU8, a, b); + case 16: + return Inst(Opcode::VectorHalvingAddU16, a, b); + case 32: + return Inst(Opcode::VectorHalvingAddU32, a, b); + } + UNREACHABLE(); + } + + U128 VectorHalvingSubSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorHalvingSubS8, a, b); + case 16: + return Inst(Opcode::VectorHalvingSubS16, a, b); + case 32: + return Inst(Opcode::VectorHalvingSubS32, a, b); + } + UNREACHABLE(); + } + + U128 VectorHalvingSubUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorHalvingSubU8, a, b); + case 16: + return Inst(Opcode::VectorHalvingSubU16, a, b); + case 32: + return Inst(Opcode::VectorHalvingSubU32, a, b); + } + UNREACHABLE(); + } + + U128 VectorInterleaveLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorInterleaveLower8, a, b); + case 16: + return Inst(Opcode::VectorInterleaveLower16, a, b); + case 32: + return Inst(Opcode::VectorInterleaveLower32, a, b); + case 64: + return Inst(Opcode::VectorInterleaveLower64, a, b); + } + UNREACHABLE(); + } + + U128 VectorInterleaveUpper(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorInterleaveUpper8, a, b); + case 16: + return Inst(Opcode::VectorInterleaveUpper16, a, b); + case 32: + return Inst(Opcode::VectorInterleaveUpper32, a, b); + case 64: + return Inst(Opcode::VectorInterleaveUpper64, a, b); + } + UNREACHABLE(); + } + + U128 VectorLessEqualSigned(size_t esize, const U128& a, const U128& b) { + return VectorNot(VectorGreaterSigned(esize, a, b)); + } + + U128 VectorLessEqualUnsigned(size_t esize, const U128& a, const U128& b) { + return VectorEqual(esize, VectorMinUnsigned(esize, a, b), a); + } + + U128 VectorLessSigned(size_t esize, const U128& a, const U128& b) { + return VectorNot(VectorOr(VectorGreaterSigned(esize, a, b), VectorEqual(esize, a, b))); + } + + U128 VectorLessUnsigned(size_t esize, const U128& a, const U128& b) { + return VectorNot(VectorEqual(esize, VectorMaxUnsigned(esize, a, b), a)); + } + + U128 VectorLogicalShiftLeft(size_t esize, const U128& a, u8 shift_amount) { + switch (esize) { + case 8: + return Inst(Opcode::VectorLogicalShiftLeft8, a, Imm8(shift_amount)); + case 16: + return Inst(Opcode::VectorLogicalShiftLeft16, a, Imm8(shift_amount)); + case 32: + return Inst(Opcode::VectorLogicalShiftLeft32, a, Imm8(shift_amount)); + case 64: + return Inst(Opcode::VectorLogicalShiftLeft64, a, Imm8(shift_amount)); + } + UNREACHABLE(); + } + + U128 VectorLogicalShiftRight(size_t esize, const U128& a, u8 shift_amount) { + switch (esize) { + case 8: + return Inst(Opcode::VectorLogicalShiftRight8, a, Imm8(shift_amount)); + case 16: + return Inst(Opcode::VectorLogicalShiftRight16, a, Imm8(shift_amount)); + case 32: + return Inst(Opcode::VectorLogicalShiftRight32, a, Imm8(shift_amount)); + case 64: + return Inst(Opcode::VectorLogicalShiftRight64, a, Imm8(shift_amount)); + } + UNREACHABLE(); + } + + U128 VectorLogicalVShift(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorLogicalVShift8, a, b); + case 16: + return Inst(Opcode::VectorLogicalVShift16, a, b); + case 32: + return Inst(Opcode::VectorLogicalVShift32, a, b); + case 64: + return Inst(Opcode::VectorLogicalVShift64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMaxSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMaxS8, a, b); + case 16: + return Inst(Opcode::VectorMaxS16, a, b); + case 32: + return Inst(Opcode::VectorMaxS32, a, b); + case 64: + return Inst(Opcode::VectorMaxS64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMaxUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMaxU8, a, b); + case 16: + return Inst(Opcode::VectorMaxU16, a, b); + case 32: + return Inst(Opcode::VectorMaxU32, a, b); + case 64: + return Inst(Opcode::VectorMaxU64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMinSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMinS8, a, b); + case 16: + return Inst(Opcode::VectorMinS16, a, b); + case 32: + return Inst(Opcode::VectorMinS32, a, b); + case 64: + return Inst(Opcode::VectorMinS64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMinUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMinU8, a, b); + case 16: + return Inst(Opcode::VectorMinU16, a, b); + case 32: + return Inst(Opcode::VectorMinU32, a, b); + case 64: + return Inst(Opcode::VectorMinU64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMultiply(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMultiply8, a, b); + case 16: + return Inst(Opcode::VectorMultiply16, a, b); + case 32: + return Inst(Opcode::VectorMultiply32, a, b); + case 64: + return Inst(Opcode::VectorMultiply64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMultiplySignedWiden(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMultiplySignedWiden8, a, b); + case 16: + return Inst(Opcode::VectorMultiplySignedWiden16, a, b); + case 32: + return Inst(Opcode::VectorMultiplySignedWiden32, a, b); + } + UNREACHABLE(); + } + + U128 VectorMultiplyUnsignedWiden(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMultiplyUnsignedWiden8, a, b); + case 16: + return Inst(Opcode::VectorMultiplyUnsignedWiden16, a, b); + case 32: + return Inst(Opcode::VectorMultiplyUnsignedWiden32, a, b); + } + UNREACHABLE(); + } + + U128 VectorNarrow(size_t original_esize, const U128& a) { + switch (original_esize) { + case 16: + return Inst(Opcode::VectorNarrow16, a); + case 32: + return Inst(Opcode::VectorNarrow32, a); + case 64: + return Inst(Opcode::VectorNarrow64, a); + } + UNREACHABLE(); + } + + U128 VectorNot(const U128& a) { + return Inst(Opcode::VectorNot, a); + } + + U128 VectorOr(const U128& a, const U128& b) { + return Inst(Opcode::VectorOr, a, b); + } + + U128 VectorPairedAdd(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedAdd8, a, b); + case 16: + return Inst(Opcode::VectorPairedAdd16, a, b); + case 32: + return Inst(Opcode::VectorPairedAdd32, a, b); + case 64: + return Inst(Opcode::VectorPairedAdd64, a, b); + } + UNREACHABLE(); + } + + U128 VectorPairedAddLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedAddLower8, a, b); + case 16: + return Inst(Opcode::VectorPairedAddLower16, a, b); + case 32: + return Inst(Opcode::VectorPairedAddLower32, a, b); + } + UNREACHABLE(); + } + + U128 VectorPairedAddSignedWiden(size_t original_esize, const U128& a) { + switch (original_esize) { + case 8: + return Inst(Opcode::VectorPairedAddSignedWiden8, a); + case 16: + return Inst(Opcode::VectorPairedAddSignedWiden16, a); + case 32: + return Inst(Opcode::VectorPairedAddSignedWiden32, a); + } + UNREACHABLE(); + } + + U128 VectorPairedAddUnsignedWiden(size_t original_esize, const U128& a) { + switch (original_esize) { + case 8: + return Inst(Opcode::VectorPairedAddUnsignedWiden8, a); + case 16: + return Inst(Opcode::VectorPairedAddUnsignedWiden16, a); + case 32: + return Inst(Opcode::VectorPairedAddUnsignedWiden32, a); + } + UNREACHABLE(); + } + + U128 VectorPairedMaxSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMaxS8, a, b); + case 16: + return Inst(Opcode::VectorPairedMaxS16, a, b); + case 32: + return Inst(Opcode::VectorPairedMaxS32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMaxUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMaxU8, a, b); + case 16: + return Inst(Opcode::VectorPairedMaxU16, a, b); + case 32: + return Inst(Opcode::VectorPairedMaxU32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMinSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMinS8, a, b); + case 16: + return Inst(Opcode::VectorPairedMinS16, a, b); + case 32: + return Inst(Opcode::VectorPairedMinS32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMinUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMinU8, a, b); + case 16: + return Inst(Opcode::VectorPairedMinU16, a, b); + case 32: + return Inst(Opcode::VectorPairedMinU32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMaxSignedLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMaxLowerS8, a, b); + case 16: + return Inst(Opcode::VectorPairedMaxLowerS16, a, b); + case 32: + return Inst(Opcode::VectorPairedMaxLowerS32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMaxUnsignedLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMaxLowerU8, a, b); + case 16: + return Inst(Opcode::VectorPairedMaxLowerU16, a, b); + case 32: + return Inst(Opcode::VectorPairedMaxLowerU32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMinSignedLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMinLowerS8, a, b); + case 16: + return Inst(Opcode::VectorPairedMinLowerS16, a, b); + case 32: + return Inst(Opcode::VectorPairedMinLowerS32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMinUnsignedLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMinLowerU8, a, b); + case 16: + return Inst(Opcode::VectorPairedMinLowerU16, a, b); + case 32: + return Inst(Opcode::VectorPairedMinLowerU32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPolynomialMultiply(const U128& a, const U128& b) { + return Inst(Opcode::VectorPolynomialMultiply8, a, b); + } + + U128 VectorPolynomialMultiplyLong(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPolynomialMultiplyLong8, a, b); + case 64: + return Inst(Opcode::VectorPolynomialMultiplyLong64, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPopulationCount(const U128& a) { + return Inst(Opcode::VectorPopulationCount, a); + } + + U128 VectorReverseBits(const U128& a) { + return Inst(Opcode::VectorReverseBits, a); + } + + U128 VectorReverseElementsInHalfGroups(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorReverseElementsInHalfGroups8, a); + default: + UNREACHABLE(); + } + } + + U128 VectorReverseElementsInWordGroups(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorReverseElementsInWordGroups8, a); + case 16: + return Inst(Opcode::VectorReverseElementsInWordGroups16, a); + default: + UNREACHABLE(); + } + } + + U128 VectorReverseElementsInLongGroups(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorReverseElementsInLongGroups8, a); + case 16: + return Inst(Opcode::VectorReverseElementsInLongGroups16, a); + case 32: + return Inst(Opcode::VectorReverseElementsInLongGroups32, a); + default: + UNREACHABLE(); + } + } + + U128 VectorReduceAdd(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorReduceAdd8, a); + case 16: + return Inst(Opcode::VectorReduceAdd16, a); + case 32: + return Inst(Opcode::VectorReduceAdd32, a); + case 64: + return Inst(Opcode::VectorReduceAdd64, a); + } + + UNREACHABLE(); + } + + U128 VectorRotateLeft(size_t esize, const U128& a, u8 amount) { + ASSERT(amount < esize); + + if (amount == 0) { + return a; + } + + return VectorOr(VectorLogicalShiftLeft(esize, a, amount), + VectorLogicalShiftRight(esize, a, static_cast(esize - amount))); + } + + U128 VectorRotateRight(size_t esize, const U128& a, u8 amount) { + ASSERT(amount < esize); + + if (amount == 0) { + return a; + } + + return VectorOr(VectorLogicalShiftRight(esize, a, amount), + VectorLogicalShiftLeft(esize, a, static_cast(esize - amount))); + } + + U128 VectorRotateWholeVectorRight(const U128& a, u8 amount) { + ASSERT(amount % 32 == 0); + return Inst(Opcode::VectorRotateWholeVectorRight, a, Imm8(amount)); + } + + U128 VectorRoundingHalvingAddSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorRoundingHalvingAddS8, a, b); + case 16: + return Inst(Opcode::VectorRoundingHalvingAddS16, a, b); + case 32: + return Inst(Opcode::VectorRoundingHalvingAddS32, a, b); + } + + UNREACHABLE(); + } + + U128 VectorRoundingHalvingAddUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorRoundingHalvingAddU8, a, b); + case 16: + return Inst(Opcode::VectorRoundingHalvingAddU16, a, b); + case 32: + return Inst(Opcode::VectorRoundingHalvingAddU32, a, b); + } + + UNREACHABLE(); + } + + U128 VectorRoundingShiftLeftSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorRoundingShiftLeftS8, a, b); + case 16: + return Inst(Opcode::VectorRoundingShiftLeftS16, a, b); + case 32: + return Inst(Opcode::VectorRoundingShiftLeftS32, a, b); + case 64: + return Inst(Opcode::VectorRoundingShiftLeftS64, a, b); + } + + UNREACHABLE(); + } + + U128 VectorRoundingShiftLeftUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorRoundingShiftLeftU8, a, b); + case 16: + return Inst(Opcode::VectorRoundingShiftLeftU16, a, b); + case 32: + return Inst(Opcode::VectorRoundingShiftLeftU32, a, b); + case 64: + return Inst(Opcode::VectorRoundingShiftLeftU64, a, b); + } + + UNREACHABLE(); + } + + U128 VectorSignExtend(size_t original_esize, const U128& a) { + switch (original_esize) { + case 8: + return Inst(Opcode::VectorSignExtend8, a); + case 16: + return Inst(Opcode::VectorSignExtend16, a); + case 32: + return Inst(Opcode::VectorSignExtend32, a); + case 64: + return Inst(Opcode::VectorSignExtend64, a); + } + UNREACHABLE(); + } + + U128 VectorSignedAbsoluteDifference(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedAbsoluteDifference8, a, b); + case 16: + return Inst(Opcode::VectorSignedAbsoluteDifference16, a, b); + case 32: + return Inst(Opcode::VectorSignedAbsoluteDifference32, a, b); + } + UNREACHABLE(); + } + + UpperAndLower VectorSignedMultiply(size_t esize, const U128& a, const U128& b) { + const Value multiply = [&] { + switch (esize) { + case 16: + return Inst(Opcode::VectorSignedMultiply16, a, b); + case 32: + return Inst(Opcode::VectorSignedMultiply32, a, b); + } + UNREACHABLE(); + }(); + + return { + Inst(Opcode::GetUpperFromOp, multiply), + Inst(Opcode::GetLowerFromOp, multiply), + }; + } + + U128 VectorSignedSaturatedAbs(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedAbs8, a); + case 16: + return Inst(Opcode::VectorSignedSaturatedAbs16, a); + case 32: + return Inst(Opcode::VectorSignedSaturatedAbs32, a); + case 64: + return Inst(Opcode::VectorSignedSaturatedAbs64, a); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedAccumulateUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned8, a, b); + case 16: + return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned32, a, b); + case 64: + return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned64, a, b); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedDoublingMultiplyHigh(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 16: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHigh16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHigh32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorSignedSaturatedDoublingMultiplyHighRounding(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 16: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHighRounding16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHighRounding32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorSignedSaturatedDoublingMultiplyLong(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 16: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyLong16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyLong32, a, b); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedNarrowToSigned(size_t original_esize, const U128& a) { + switch (original_esize) { + case 16: + return Inst(Opcode::VectorSignedSaturatedNarrowToSigned16, a); + case 32: + return Inst(Opcode::VectorSignedSaturatedNarrowToSigned32, a); + case 64: + return Inst(Opcode::VectorSignedSaturatedNarrowToSigned64, a); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedNarrowToUnsigned(size_t original_esize, const U128& a) { + switch (original_esize) { + case 16: + return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned16, a); + case 32: + return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned32, a); + case 64: + return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned64, a); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedNeg(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedNeg8, a); + case 16: + return Inst(Opcode::VectorSignedSaturatedNeg16, a); + case 32: + return Inst(Opcode::VectorSignedSaturatedNeg32, a); + case 64: + return Inst(Opcode::VectorSignedSaturatedNeg64, a); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedShiftLeft8, a, b); + case 16: + return Inst(Opcode::VectorSignedSaturatedShiftLeft16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedShiftLeft32, a, b); + case 64: + return Inst(Opcode::VectorSignedSaturatedShiftLeft64, a, b); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedShiftLeftUnsigned(size_t esize, const U128& a, u8 shift_amount) { + ASSERT(shift_amount < esize); + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned8, a, Imm8(shift_amount)); + case 16: + return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned16, a, Imm8(shift_amount)); + case 32: + return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned32, a, Imm8(shift_amount)); + case 64: + return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned64, a, Imm8(shift_amount)); + } + UNREACHABLE(); + } + + U128 VectorSub(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSub8, a, b); + case 16: + return Inst(Opcode::VectorSub16, a, b); + case 32: + return Inst(Opcode::VectorSub32, a, b); + case 64: + return Inst(Opcode::VectorSub64, a, b); + } + UNREACHABLE(); + } + + Table VectorTable(std::vector values) { + ASSERT(values.size() >= 1 && values.size() <= 4); + values.resize(4); + return Inst
(Opcode::VectorTable, values[0], values[1], values[2], values[3]); + } + + Table VectorTable(std::vector values) { + ASSERT(values.size() >= 1 && values.size() <= 4); + values.resize(4); + return Inst
(Opcode::VectorTable, values[0], values[1], values[2], values[3]); + } + + U64 VectorTableLookup(const U64& defaults, const Table& table, const U64& indices) { + ASSERT(table.GetInst()->GetArg(0).GetType() == Type::U64); + return Inst(Opcode::VectorTableLookup64, defaults, table, indices); + } + + U128 VectorTableLookup(const U128& defaults, const Table& table, const U128& indices) { + ASSERT(table.GetInst()->GetArg(0).GetType() == Type::U128); + return Inst(Opcode::VectorTableLookup128, defaults, table, indices); + } + + U128 VectorTranspose(size_t esize, const U128& a, const U128& b, bool part) { + switch (esize) { + case 8: + return Inst(Opcode::VectorTranspose8, a, b, Imm1(part)); + case 16: + return Inst(Opcode::VectorTranspose16, a, b, Imm1(part)); + case 32: + return Inst(Opcode::VectorTranspose32, a, b, Imm1(part)); + case 64: + return Inst(Opcode::VectorTranspose64, a, b, Imm1(part)); + } + UNREACHABLE(); + } + + U128 VectorUnsignedAbsoluteDifference(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorUnsignedAbsoluteDifference8, a, b); + case 16: + return Inst(Opcode::VectorUnsignedAbsoluteDifference16, a, b); + case 32: + return Inst(Opcode::VectorUnsignedAbsoluteDifference32, a, b); + } + UNREACHABLE(); + } + + U128 VectorUnsignedRecipEstimate(const U128& a) { + return Inst(Opcode::VectorUnsignedRecipEstimate, a); + } + + U128 VectorUnsignedRecipSqrtEstimate(const U128& a) { + return Inst(Opcode::VectorUnsignedRecipSqrtEstimate, a); + } + + U128 VectorUnsignedSaturatedAccumulateSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned8, a, b); + case 16: + return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned16, a, b); + case 32: + return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned32, a, b); + case 64: + return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned64, a, b); + } + UNREACHABLE(); + } + + U128 VectorUnsignedSaturatedNarrow(size_t esize, const U128& a) { + switch (esize) { + case 16: + return Inst(Opcode::VectorUnsignedSaturatedNarrow16, a); + case 32: + return Inst(Opcode::VectorUnsignedSaturatedNarrow32, a); + case 64: + return Inst(Opcode::VectorUnsignedSaturatedNarrow64, a); + } + UNREACHABLE(); + } + + U128 VectorUnsignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorUnsignedSaturatedShiftLeft8, a, b); + case 16: + return Inst(Opcode::VectorUnsignedSaturatedShiftLeft16, a, b); + case 32: + return Inst(Opcode::VectorUnsignedSaturatedShiftLeft32, a, b); + case 64: + return Inst(Opcode::VectorUnsignedSaturatedShiftLeft64, a, b); + } + UNREACHABLE(); + } + + U128 VectorZeroExtend(size_t original_esize, const U128& a) { + switch (original_esize) { + case 8: + return Inst(Opcode::VectorZeroExtend8, a); + case 16: + return Inst(Opcode::VectorZeroExtend16, a); + case 32: + return Inst(Opcode::VectorZeroExtend32, a); + case 64: + return Inst(Opcode::VectorZeroExtend64, a); + } + UNREACHABLE(); + } + + U128 VectorZeroUpper(const U128& a) { + return Inst(Opcode::VectorZeroUpper, a); + } + + U128 ZeroVector() { + return Inst(Opcode::ZeroVector); + } + + U16U32U64 FPAbs(const U16U32U64& a) { + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPAbs16, a); + case Type::U32: + return Inst(Opcode::FPAbs32, a); + case Type::U64: + return Inst(Opcode::FPAbs64, a); + default: + UNREACHABLE(); + } + } + + U32U64 FPAdd(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPAdd32, a, b); + case Type::U64: + return Inst(Opcode::FPAdd64, a, b); + default: + UNREACHABLE(); + } + } + + NZCV FPCompare(const U32U64& a, const U32U64& b, bool exc_on_qnan) { + ASSERT(a.GetType() == b.GetType()); + + const IR::U1 exc_on_qnan_imm = Imm1(exc_on_qnan); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPCompare32, a, b, exc_on_qnan_imm); + case Type::U64: + return Inst(Opcode::FPCompare64, a, b, exc_on_qnan_imm); + default: + UNREACHABLE(); + } + } + + U32U64 FPDiv(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPDiv32, a, b); + case Type::U64: + return Inst(Opcode::FPDiv64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPMax(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMax32, a, b); + case Type::U64: + return Inst(Opcode::FPMax64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPMaxNumeric(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMaxNumeric32, a, b); + case Type::U64: + return Inst(Opcode::FPMaxNumeric64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPMin(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMin32, a, b); + case Type::U64: + return Inst(Opcode::FPMin64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPMinNumeric(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMinNumeric32, a, b); + case Type::U64: + return Inst(Opcode::FPMinNumeric64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPMul(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMul32, a, b); + case Type::U64: + return Inst(Opcode::FPMul64, a, b); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPMulAdd(const U16U32U64& a, const U16U32U64& b, const U16U32U64& c) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPMulAdd16, a, b, c); + case Type::U32: + return Inst(Opcode::FPMulAdd32, a, b, c); + case Type::U64: + return Inst(Opcode::FPMulAdd64, a, b, c); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPMulSub(const U16U32U64& a, const U16U32U64& b, const U16U32U64& c) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPMulSub16, a, b, c); + case Type::U32: + return Inst(Opcode::FPMulSub32, a, b, c); + case Type::U64: + return Inst(Opcode::FPMulSub64, a, b, c); + default: + UNREACHABLE(); + } + } + + U32U64 FPMulX(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMulX32, a, b); + case Type::U64: + return Inst(Opcode::FPMulX64, a, b); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPNeg(const U16U32U64& a) { + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPNeg16, a); + case Type::U32: + return Inst(Opcode::FPNeg32, a); + case Type::U64: + return Inst(Opcode::FPNeg64, a); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRecipEstimate(const U16U32U64& a) { + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRecipEstimate16, a); + case Type::U32: + return Inst(Opcode::FPRecipEstimate32, a); + case Type::U64: + return Inst(Opcode::FPRecipEstimate64, a); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRecipExponent(const U16U32U64& a) { + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRecipExponent16, a); + case Type::U32: + return Inst(Opcode::FPRecipExponent32, a); + case Type::U64: + return Inst(Opcode::FPRecipExponent64, a); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRecipStepFused(const U16U32U64& a, const U16U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRecipStepFused16, a, b); + case Type::U32: + return Inst(Opcode::FPRecipStepFused32, a, b); + case Type::U64: + return Inst(Opcode::FPRecipStepFused64, a, b); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRoundInt(const U16U32U64& a, FP::RoundingMode rounding, bool exact) { + const u8 rounding_value = static_cast(rounding); + const IR::U1 exact_imm = Imm1(exact); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRoundInt16, a, rounding_value, exact_imm); + case Type::U32: + return Inst(Opcode::FPRoundInt32, a, rounding_value, exact_imm); + case Type::U64: + return Inst(Opcode::FPRoundInt64, a, rounding_value, exact_imm); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRSqrtEstimate(const U16U32U64& a) { + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRSqrtEstimate16, a); + case Type::U32: + return Inst(Opcode::FPRSqrtEstimate32, a); + case Type::U64: + return Inst(Opcode::FPRSqrtEstimate64, a); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRSqrtStepFused(const U16U32U64& a, const U16U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRSqrtStepFused16, a, b); + case Type::U32: + return Inst(Opcode::FPRSqrtStepFused32, a, b); + case Type::U64: + return Inst(Opcode::FPRSqrtStepFused64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPSqrt(const U32U64& a) { + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPSqrt32, a); + case Type::U64: + return Inst(Opcode::FPSqrt64, a); + default: + UNREACHABLE(); + } + } + + U32U64 FPSub(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPSub32, a, b); + case Type::U64: + return Inst(Opcode::FPSub64, a, b); + default: + UNREACHABLE(); + } + } + + U16 FPDoubleToHalf(const U64& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPDoubleToHalf, a, Imm8(static_cast(rounding))); + } + + U32 FPDoubleToSingle(const U64& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPDoubleToSingle, a, Imm8(static_cast(rounding))); + } + + U64 FPHalfToDouble(const U16& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPHalfToDouble, a, Imm8(static_cast(rounding))); + } + + U32 FPHalfToSingle(const U16& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPHalfToSingle, a, Imm8(static_cast(rounding))); + } + + U64 FPSingleToDouble(const U32& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPSingleToDouble, a, Imm8(static_cast(rounding))); + } + + U16 FPSingleToHalf(const U32& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPSingleToHalf, a, Imm8(static_cast(rounding))); + } + + U16 FPToFixedS16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 16); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedS16, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedS16, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedS16, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U32 FPToFixedS32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 32); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedS32, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedS32, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedS32, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U64 FPToFixedS64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 64); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedS64, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedS64, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedS64, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U16 FPToFixedU16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 16); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedU16, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedU16, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedU16, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U32 FPToFixedU32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 32); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedU32, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedU32, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedU32, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U64 FPToFixedU64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 64); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedU64, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedU64, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedU64, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U32 FPSignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); + + const IR::U8 fbits_imm = Imm8(static_cast(fbits)); + const IR::U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPFixedS16ToSingle, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPFixedS32ToSingle, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPFixedS64ToSingle, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U32 FPUnsignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); + + const IR::U8 fbits_imm = Imm8(static_cast(fbits)); + const IR::U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPFixedU16ToSingle, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPFixedU32ToSingle, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPFixedU64ToSingle, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U64 FPSignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); + + const IR::U8 fbits_imm = Imm8(static_cast(fbits)); + const IR::U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPFixedS16ToDouble, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPFixedS32ToDouble, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPFixedS64ToDouble, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U64 FPUnsignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); + + const IR::U8 fbits_imm = Imm8(static_cast(fbits)); + const IR::U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPFixedU16ToDouble, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPFixedU32ToDouble, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPFixedU64ToDouble, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U128 FPVectorAbs(size_t esize, const U128& a) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorAbs16, a); + case 32: + return Inst(Opcode::FPVectorAbs32, a); + case 64: + return Inst(Opcode::FPVectorAbs64, a); + } + UNREACHABLE(); + } + + U128 FPVectorAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorAdd32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorAdd64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorDiv(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorDiv32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorDiv64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorEqual16, a, b, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorEqual32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorEqual64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorFromHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(esize == 32); + return Inst(Opcode::FPVectorFromHalf32, a, Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + } + + U128 FPVectorFromSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(fbits <= esize); + switch (esize) { + case 32: + return Inst(Opcode::FPVectorFromSignedFixed32, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorFromSignedFixed64, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorFromUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(fbits <= esize); + switch (esize) { + case 32: + return Inst(Opcode::FPVectorFromUnsignedFixed32, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorFromUnsignedFixed64, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorGreater(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorGreater32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorGreater64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorGreaterEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorGreaterEqual32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorGreaterEqual64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMax(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMax32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMax64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMaxNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMaxNumeric32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMaxNumeric64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMin(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMin32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMin64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMinNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMinNumeric32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMinNumeric64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMul(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMul32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMul64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMulAdd(size_t esize, const U128& a, const U128& b, const U128& c, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorMulAdd16, a, b, c, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorMulAdd32, a, b, c, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMulAdd64, a, b, c, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMulX(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMulX32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMulX64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorNeg(size_t esize, const U128& a) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorNeg16, a); + case 32: + return Inst(Opcode::FPVectorNeg32, a); + case 64: + return Inst(Opcode::FPVectorNeg64, a); + } + UNREACHABLE(); + } + + U128 FPVectorPairedAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorPairedAdd32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorPairedAdd64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorPairedAddLower(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorPairedAddLower32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorPairedAddLower64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorRecipEstimate(size_t esize, const U128& a, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorRecipEstimate16, a, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorRecipEstimate32, a, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorRecipEstimate64, a, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorRecipStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorRecipStepFused16, a, b, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorRecipStepFused32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorRecipStepFused64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorRoundInt(size_t esize, const U128& operand, FP::RoundingMode rounding, bool exact, bool fpcr_controlled = true) { + const IR::U8 rounding_imm = Imm8(static_cast(rounding)); + const IR::U1 exact_imm = Imm1(exact); + + switch (esize) { + case 16: + return Inst(Opcode::FPVectorRoundInt16, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorRoundInt32, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorRoundInt64, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorRSqrtEstimate(size_t esize, const U128& a, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorRSqrtEstimate16, a, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorRSqrtEstimate32, a, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorRSqrtEstimate64, a, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorRSqrtStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorRSqrtStepFused16, a, b, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorRSqrtStepFused32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorRSqrtStepFused64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorSqrt(size_t esize, const U128& a, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorSqrt32, a, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorSqrt64, a, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorSub(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorSub32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorSub64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorToHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(esize == 32); + return Inst(Opcode::FPVectorToHalf32, a, Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + } + + U128 FPVectorToSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(fbits <= esize); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (esize) { + case 16: + return Inst(Opcode::FPVectorToSignedFixed16, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorToSignedFixed32, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorToSignedFixed64, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + } + + UNREACHABLE(); + } + + U128 FPVectorToUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(fbits <= esize); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (esize) { + case 16: + return Inst(Opcode::FPVectorToUnsignedFixed16, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorToUnsignedFixed32, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorToUnsignedFixed64, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + } + + UNREACHABLE(); + } + + void Breakpoint() { + Inst(Opcode::Breakpoint); + } + + void CallHostFunction(void (*fn)(void)) { + Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), Value{}, Value{}, Value{}); + } + + void CallHostFunction(void (*fn)(u64), const U64& arg1) { + Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, Value{}, Value{}); + } + + void CallHostFunction(void (*fn)(u64, u64), const U64& arg1, const U64& arg2) { + Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, arg2, Value{}); + } + + void CallHostFunction(void (*fn)(u64, u64, u64), const U64& arg1, const U64& arg2, const U64& arg3) { + Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, arg2, arg3); + } + + void SetTerm(const Terminal& terminal) { + block.SetTerminal(terminal); + } void SetInsertionPointBefore(IR::Inst* new_insertion_point) { insertion_point = IR::Block::iterator{*new_insertion_point}; From 7b23cd0df4c2ecb13c3b032906950495b187c940 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 14 Jul 2025 00:54:00 +0200 Subject: [PATCH 10/41] [dynarmic] fix userconfig casting warn (#55) Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/55 Co-authored-by: lizzie Co-committed-by: lizzie --- .../dynarmic/src/dynarmic/interface/A32/config.h | 9 ++++++--- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 11 +++++++---- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 15 +++++++++------ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/externals/dynarmic/src/dynarmic/interface/A32/config.h b/externals/dynarmic/src/dynarmic/interface/A32/config.h index 033967dc00..11fe2236a2 100644 --- a/externals/dynarmic/src/dynarmic/interface/A32/config.h +++ b/externals/dynarmic/src/dynarmic/interface/A32/config.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * SPDX-License-Identifier: 0BSD @@ -159,9 +162,6 @@ struct UserConfig { /// Maximum size is limited by the maximum length of a x86_64 / arm64 jump. std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes - /// Processor ID - std::uint32_t processor_id = 0; - /// Masks out the first N bits in host pointers from the page table. /// The intention behind this is to allow users of Dynarmic to pack attributes in the /// same integer and update the pointer attribute pair atomically. @@ -172,6 +172,9 @@ struct UserConfig { /// There are minor behavioural differences between versions. ArchVersion arch_version = ArchVersion::v8; + /// Processor ID + std::uint8_t processor_id = 0; + /// Determines if we should detect memory accesses via page_table that straddle are /// misaligned. Accesses that straddle page boundaries will fallback to the relevant /// memory callback. diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index d21aa5aacf..afbf178349 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -201,7 +204,7 @@ std::shared_ptr ArmDynarmic32::MakeJit(Common::PageTable* pa } // Multi-process state - config.processor_id = m_core_index; + config.processor_id = std::uint8_t(m_core_index); config.global_monitor = &m_exclusive_monitor.monitor; // Timing @@ -210,9 +213,9 @@ std::shared_ptr ArmDynarmic32::MakeJit(Common::PageTable* pa // Code cache size #ifdef ARCHITECTURE_arm64 - config.code_cache_size = 128_MiB; + config.code_cache_size = std::uint32_t(128_MiB); #else - config.code_cache_size = 512_MiB; + config.code_cache_size = std::uint32_t(512_MiB); #endif // Allow memory fault handling to work @@ -223,7 +226,7 @@ std::shared_ptr ArmDynarmic32::MakeJit(Common::PageTable* pa // null_jit if (!page_table) { // Don't waste too much memory on null_jit - config.code_cache_size = 8_MiB; + config.code_cache_size = std::uint32_t(8_MiB); } // Safe optimizations diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index c251482182..99a80644ad 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -232,7 +235,7 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa // Memory if (page_table) { config.page_table = reinterpret_cast(page_table->pointers.data()); - config.page_table_address_space_bits = address_space_bits; + config.page_table_address_space_bits = std::uint32_t(address_space_bits); config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; config.silently_mirror_page_table = false; config.absolute_offset_page_table = true; @@ -242,7 +245,7 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa config.fastmem_pointer = page_table->fastmem_arena ? std::optional{reinterpret_cast(page_table->fastmem_arena)} : std::nullopt; - config.fastmem_address_space_bits = address_space_bits; + config.fastmem_address_space_bits = std::uint32_t(address_space_bits); config.silently_mirror_fastmem = false; config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt; @@ -250,7 +253,7 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa } // Multi-process state - config.processor_id = m_core_index; + config.processor_id = std::uint8_t(m_core_index); config.global_monitor = &m_exclusive_monitor.monitor; // System registers @@ -269,9 +272,9 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa // Code cache size #ifdef ARCHITECTURE_arm64 - config.code_cache_size = 128_MiB; + config.code_cache_size = std::uint32_t(128_MiB); #else - config.code_cache_size = 512_MiB; + config.code_cache_size = std::uint32_t(512_MiB); #endif // Allow memory fault handling to work @@ -282,7 +285,7 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa // null_jit if (!page_table) { // Don't waste too much memory on null_jit - config.code_cache_size = 8_MiB; + config.code_cache_size = std::uint32_t(8_MiB); } // Safe optimizations From f99488fe3e4c0eb783b5fa8aeff42f753c38a0e9 Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 14 Jul 2025 01:29:57 +0200 Subject: [PATCH 11/41] [desktop] feat: install firmware from ZIP (#52) Closes #12 Adds a menu option to install firmware from a packed ZIP. This PR additionally lays the groundwork to add data import/export via ZIP. In the future, a qt_common subproject should be added to handle common Qt tasks such as this. Furthermore, to decrease dependency complexity, this also introduces CPM, a wrapper around FetchContent. In theory, this should also lay the groundwork for #8 as well. Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/52 --- .ci/patch/0001-quazip-strict.patch | 80 ++++++++++++++++++ .gitignore | 1 - CMakeModules/CPM.cmake | 24 ++++++ src/yuzu/CMakeLists.txt | 21 +++++ src/yuzu/main.cpp | 128 +++++++++++++++++++++++------ src/yuzu/main.h | 3 + src/yuzu/main.ui | 24 ++++-- tools/update-cpm.sh | 3 + 8 files changed, 252 insertions(+), 32 deletions(-) create mode 100644 .ci/patch/0001-quazip-strict.patch create mode 100644 CMakeModules/CPM.cmake create mode 100755 tools/update-cpm.sh diff --git a/.ci/patch/0001-quazip-strict.patch b/.ci/patch/0001-quazip-strict.patch new file mode 100644 index 0000000000..8283497230 --- /dev/null +++ b/.ci/patch/0001-quazip-strict.patch @@ -0,0 +1,80 @@ +diff --git a/quazip/quazipdir.cpp b/quazip/quazipdir.cpp +index d43f1c1..eb24bf1 100644 +--- a/quazip/quazipdir.cpp ++++ b/quazip/quazipdir.cpp +@@ -293,8 +293,8 @@ bool QuaZipDirComparator::operator()(const QuaZipFileInfo64 &info1, + } + + template +-bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, +- QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const ++bool QuaZipDirPrivate::entryInfoList(QStringList _nameFilters, ++ QDir::Filters _filter, QDir::SortFlags sort, TFileInfoList &result) const + { + QString basePath = simplePath(); + if (!basePath.isEmpty()) +@@ -305,12 +305,12 @@ bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, + if (!zip->goToFirstFile()) { + return zip->getZipError() == UNZ_OK; + } +- QDir::Filters fltr = filter; ++ QDir::Filters fltr = _filter; + if (fltr == QDir::NoFilter) + fltr = this->filter; + if (fltr == QDir::NoFilter) + fltr = QDir::AllEntries; +- QStringList nmfltr = nameFilters; ++ QStringList nmfltr = _nameFilters; + if (nmfltr.isEmpty()) + nmfltr = this->nameFilters; + QSet dirsFound; +diff --git a/quazip/quazipfile.cpp b/quazip/quazipfile.cpp +index 4a5f2f9..f7865f5 100644 +--- a/quazip/quazipfile.cpp ++++ b/quazip/quazipfile.cpp +@@ -241,14 +241,14 @@ void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs + p->caseSensitivity=cs; + } + +-void QuaZipFilePrivate::setZipError(int zipError) const ++void QuaZipFilePrivate::setZipError(int _zipError) const + { + QuaZipFilePrivate *fakeThis = const_cast(this); // non-const +- fakeThis->zipError=zipError; +- if(zipError==UNZ_OK) ++ fakeThis->zipError = _zipError; ++ if(_zipError == UNZ_OK) + q->setErrorString(QString()); + else +- q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(zipError)); ++ q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(_zipError)); + } + + bool QuaZipFile::open(OpenMode mode) +diff --git a/quazip/unzip.c b/quazip/unzip.c +index a39365d..ee7b487 100644 +--- a/quazip/unzip.c ++++ b/quazip/unzip.c +@@ -1054,7 +1054,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { +- uLong uL; ++ uLong _uL; + + if(file_info.uncompressed_size == (ZPOS64_T)0xFFFFFFFFu) + { +@@ -1078,7 +1078,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, + if(file_info.disk_num_start == 0xFFFFFFFFu) + { + /* Disk Start Number */ +- if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) ++ if (unz64local_getLong(&s->z_filefunc, s->filestream, &_uL) != UNZ_OK) + err=UNZ_ERRNO; + } + +@@ -2151,3 +2151,4 @@ int ZEXPORT unzClearFlags(unzFile file, unsigned flags) + s->flags &= ~flags; + return UNZ_OK; + } ++ diff --git a/.gitignore b/.gitignore index 9aaf549512..4417d3f132 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,3 @@ Thumbs.db eden-windows-msvc artifacts *.AppImage* -*.patch diff --git a/CMakeModules/CPM.cmake b/CMakeModules/CPM.cmake new file mode 100644 index 0000000000..84748734ce --- /dev/null +++ b/CMakeModules/CPM.cmake @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: MIT +# +# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors + +set(CPM_DOWNLOAD_VERSION 0.42.0) +set(CPM_HASH_SUM "2020b4fc42dba44817983e06342e682ecfc3d2f484a581f11cc5731fbe4dce8a") + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} +) + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 0dc6f562b8..b5125b19a3 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -494,4 +494,25 @@ if (YUZU_ROOM) target_link_libraries(yuzu PRIVATE yuzu-room) endif() +# Extra deps +set(BUILD_SHARED_LIBS OFF) + +include(CPM) +set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) + +CPMAddPackage( + URI "gh:stachenov/quazip@1.5" + PATCHES + ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch +) + +if (NOT MSVC) + target_compile_options(QuaZip PRIVATE + -Wno-error=shadow + -Wno-error=missing-declarations + ) +endif() + +target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) + create_target_directory_groups(yuzu) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 081f08cf52..634c11bdce 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -12,6 +12,8 @@ #include "core/tools/renderdoc.h" #include "frontend_common/firmware_manager.h" +#include + #ifdef __APPLE__ #include // for chdir #endif @@ -1683,7 +1685,8 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Discord, &GMainWindow::OnOpenDiscord); connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); - connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware); + connect_menu(ui->action_Firmware_From_Folder, &GMainWindow::OnInstallFirmware); + connect_menu(ui->action_Firmware_From_ZIP, &GMainWindow::OnInstallFirmwareFromZIP); connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys); connect_menu(ui->action_About, &GMainWindow::OnAbout); } @@ -1714,7 +1717,8 @@ void GMainWindow::UpdateMenuState() { action->setEnabled(emulation_running); } - ui->action_Install_Firmware->setEnabled(!emulation_running); + ui->action_Firmware_From_Folder->setEnabled(!emulation_running); + ui->action_Firmware_From_ZIP->setEnabled(!emulation_running); ui->action_Install_Keys->setEnabled(!emulation_running); for (QAction* action : applet_actions) { @@ -4239,26 +4243,8 @@ void GMainWindow::OnVerifyInstalledContents() { } } -void GMainWindow::OnInstallFirmware() { - // Don't do this while emulation is running, that'd probably be a bad idea. - if (emu_thread != nullptr && emu_thread->IsRunning()) { - return; - } - - // Check for installed keys, error out, suggest restart? - if (!ContentManager::AreKeysPresent()) { - QMessageBox::information( - this, tr("Keys not installed"), - tr("Install decryption keys and restart eden before attempting to install firmware.")); - return; - } - - const QString firmware_source_location = QFileDialog::getExistingDirectory( - this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); - if (firmware_source_location.isEmpty()) { - return; - } - +void GMainWindow::InstallFirmware(const QString &location, bool recursive) +{ QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(100); @@ -4272,11 +4258,11 @@ void GMainWindow::OnInstallFirmware() { return progress.wasCanceled(); }; - LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString()); + LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString()); // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in // there.) - std::filesystem::path firmware_source_path = firmware_source_location.toStdString(); + std::filesystem::path firmware_source_path = location.toStdString(); if (!Common::FS::IsDir(firmware_source_path)) { progress.close(); return; @@ -4294,7 +4280,12 @@ void GMainWindow::OnInstallFirmware() { QtProgressCallback(100, 10); - Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + if (recursive) { + Common::FS::IterateDirEntriesRecursively(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + } else { + Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + } + if (out.size() <= 0) { progress.close(); QMessageBox::warning(this, tr("Firmware install failed"), @@ -4377,6 +4368,93 @@ void GMainWindow::OnInstallFirmware() { OnCheckFirmware(); } +void GMainWindow::OnInstallFirmware() { + // Don't do this while emulation is running, that'd probably be a bad idea. + if (emu_thread != nullptr && emu_thread->IsRunning()) { + return; + } + + // Check for installed keys, error out, suggest restart? + if (!ContentManager::AreKeysPresent()) { + QMessageBox::information( + this, tr("Keys not installed"), + tr("Install decryption keys and restart Eden before attempting to install firmware.")); + return; + } + + const QString firmware_source_location = QFileDialog::getExistingDirectory( + this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); + if (firmware_source_location.isEmpty()) { + return; + } + + InstallFirmware(firmware_source_location); +} + +void GMainWindow::OnInstallFirmwareFromZIP() +{ + // Don't do this while emulation is running, that'd probably be a bad idea. + if (emu_thread != nullptr && emu_thread->IsRunning()) { + return; + } + + // Check for installed keys, error out, suggest restart? + if (!ContentManager::AreKeysPresent()) { + QMessageBox::information( + this, tr("Keys not installed"), + tr("Install decryption keys and restart Eden before attempting to install firmware.")); + return; + } + + const QString firmware_zip_location = QFileDialog::getOpenFileName( + this, tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)")); + if (firmware_zip_location.isEmpty()) { + return; + } + + namespace fs = std::filesystem; + fs::path tmp{std::filesystem::temp_directory_path()}; + + if (!std::filesystem::create_directories(tmp / "eden" / "firmware")) { + goto unzipFailed; + } + + { + tmp /= "eden"; + tmp /= "firmware"; + + QString qCacheDir = QString::fromStdString(tmp.string()); + + QFile zip(firmware_zip_location); + + QStringList result = JlCompress::extractDir(&zip, qCacheDir); + if (result.isEmpty()) { + goto unzipFailed; + } + + // In this case, it has to be done recursively, since sometimes people + // will pack it into a subdirectory after dumping + InstallFirmware(qCacheDir, true); + + std::error_code ec; + std::filesystem::remove_all(tmp, ec); + + if (ec) { + QMessageBox::warning(this, tr("Firmware cleanup failed"), + tr("Failed to clean up extracted firmware cache.\n" + "Check write permissions in the system temp directory and try again.\nOS reported error: %1") + .arg(ec.message())); + } + + return; + } +unzipFailed: + QMessageBox::critical(this, tr("Firmware unzip failed"), + tr("Check write permissions in the system temp directory and try again.")); + return; + +} + void GMainWindow::OnInstallDecryptionKeys() { // Don't do this while emulation is running. if (emu_thread != nullptr && emu_thread->IsRunning()) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1f2582098a..7e7c00ec0b 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -394,6 +394,7 @@ private slots: void OnOpenLogFolder(); void OnVerifyInstalledContents(); void OnInstallFirmware(); + void OnInstallFirmwareFromZIP(); void OnInstallDecryptionKeys(); void OnAbout(); void OnToggleFilterBar(); @@ -614,6 +615,8 @@ private: std::string arguments, const bool needs_title); + void InstallFirmware(const QString& location, bool recursive = false); + protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index e35b7afa5a..970f8d2901 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -182,8 +182,15 @@ + + + Install Firmware + + + + - + @@ -484,11 +491,6 @@ Open &Controller Menu - - - Install Firmware - - Install Decryption Keys @@ -545,6 +547,16 @@ &Log Folder + + + + + From Folder + + + + + From ZIP diff --git a/tools/update-cpm.sh b/tools/update-cpm.sh new file mode 100755 index 0000000000..30e400209d --- /dev/null +++ b/tools/update-cpm.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +wget -O CMakeModules/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake From 492903cc7a6c21fb83bfd61e13ea71c785376d4c Mon Sep 17 00:00:00 2001 From: Aleksandr Popovich Date: Sun, 13 Jul 2025 20:00:38 -0400 Subject: [PATCH 12/41] [cmake] force quazip to use qt 6 Signed-off-by: Aleksandr Popovich --- src/yuzu/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index b5125b19a3..88f25aa42e 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -500,6 +500,7 @@ set(BUILD_SHARED_LIBS OFF) include(CPM) set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) +set(QUAZIP_QT_MAJOR_VERSION 6) CPMAddPackage( URI "gh:stachenov/quazip@1.5" PATCHES From 2be7df287a65d9185dbfc2f3b68cf09775efd874 Mon Sep 17 00:00:00 2001 From: Aleksandr Popovich Date: Mon, 14 Jul 2025 02:18:33 +0200 Subject: [PATCH 13/41] [android] Fix crash caused by unreferenced driver (#58) Previously, if the user selected a per-game driver and that driver was deleted from the global menu, it would cause a crash, it was because of a mismatch between FileNotFoundException and NoSuchFileException. To avoid the inconsistency I just made the check for if a file exists or not to be separate. Signed-off-by: Aleksandr Popovich Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/58 Co-authored-by: Aleksandr Popovich Co-committed-by: Aleksandr Popovich --- .../main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt index 81943e9235..99f7fd81fe 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -177,6 +180,10 @@ object GpuDriverHelper { * @return A non-null [GpuDriverMetadata] instance that may have null members */ fun getMetadataFromZip(driver: File): GpuDriverMetadata { + if (!driver.exists()) { + return GpuDriverMetadata() + } + try { ZipFile(driver).use { zf -> val entries = zf.entries() From be59b4f15f3b59ccbf4b8ad0e40e8cb16de3bcc1 Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 14 Jul 2025 04:48:39 +0200 Subject: [PATCH 14/41] [cmake] Patch QuaZip for windows fix (#60) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/60 --- .ci/patch/0002-quazip-fetchcontent.patch | 13 +++++++++++++ src/yuzu/CMakeLists.txt | 1 + 2 files changed, 14 insertions(+) create mode 100644 .ci/patch/0002-quazip-fetchcontent.patch diff --git a/.ci/patch/0002-quazip-fetchcontent.patch b/.ci/patch/0002-quazip-fetchcontent.patch new file mode 100644 index 0000000000..3554b7dbb6 --- /dev/null +++ b/.ci/patch/0002-quazip-fetchcontent.patch @@ -0,0 +1,13 @@ +diff --git a/cmake/clone-repo.cmake b/cmake/clone-repo.cmake +index 2ffb4b2..77974dc 100644 +--- a/cmake/clone-repo.cmake ++++ b/cmake/clone-repo.cmake +@@ -26,7 +26,7 @@ macro(clone_repo name url) + FetchContent_GetProperties(${name} POPULATED ${name_lower}_POPULATED) + + if(NOT ${name_lower}_POPULATED) +- FetchContent_Populate(${name}) ++ FetchContent_MakeAvailable(${name}) + endif() + + set(${name_upper}_SOURCE_DIR ${${name_lower}_SOURCE_DIR}) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 88f25aa42e..cf96b6e876 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -505,6 +505,7 @@ CPMAddPackage( URI "gh:stachenov/quazip@1.5" PATCHES ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch + ${CMAKE_SOURCE_DIR}/.ci/patch/0002-quazip-fetchcontent.patch ) if (NOT MSVC) From 2e092010e62d2810efc3dbe7b4d4b3d3cf873cdc Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 14 Jul 2025 06:10:25 +0200 Subject: [PATCH 15/41] [cmake] Disable bzip2 requirement for quazip (#63) caused windows builds to fail Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/63 Co-authored-by: crueter Co-committed-by: crueter --- src/yuzu/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cf96b6e876..09738b9e03 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -499,8 +499,11 @@ set(BUILD_SHARED_LIBS OFF) include(CPM) set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) +set(CPM_USE_LOCAL_PACKAGES ON) set(QUAZIP_QT_MAJOR_VERSION 6) +set(QUAZIP_BZIP2 OFF) + CPMAddPackage( URI "gh:stachenov/quazip@1.5" PATCHES From a8564a09b7b2aac98a0319d40f8e5bc6984f8f1d Mon Sep 17 00:00:00 2001 From: SDK-Chan Date: Mon, 14 Jul 2025 22:30:07 +0200 Subject: [PATCH 16/41] [host1x] FreeBSD: Fix random crashes due to CUDA/VAAPI check sideeffects (#64) FreeBSD doesn't support NVDEC, CUDA, and partially supports VAAPI (mostly for firefox). Implementing VAAPI for other use cases would be a little bit complicated so, I chose to switch it off for FreeBSD. This PR ensures that FFmpeg will always default to software decoding on FreeBSD, but should remain the same functionalities for other OS's. The results are slight CPU increases while decoding in software mode, but still neglectable and they don't really harm performance. Co-authored-by: MaranBr Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/64 Co-authored-by: SDK-Chan Co-committed-by: SDK-Chan --- src/video_core/host1x/ffmpeg/ffmpeg.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp index 9b718f2591..5321c8c98c 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp @@ -26,13 +26,14 @@ namespace { constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12; constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P; constexpr std::array PreferredGpuDecoders = { - AV_HWDEVICE_TYPE_CUDA, #ifdef _WIN32 + AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2, +#elif defined(__FreeBSD__) + AV_HWDEVICE_TYPE_VDPAU, #elif defined(__unix__) AV_HWDEVICE_TYPE_VAAPI, - AV_HWDEVICE_TYPE_VDPAU, #endif AV_HWDEVICE_TYPE_VULKAN, }; From e9ca3f4c069b4ed4386d7086bc676c83e4334adc Mon Sep 17 00:00:00 2001 From: MaranBr Date: Mon, 14 Jul 2025 22:30:54 +0200 Subject: [PATCH 17/41] [host1x] Fix FFmpeg crash on Linux (#37) This fixes the FFmpeg crash on Linux / Steam Deck. Credit to Maufeat for AVERROR_EOF check. Co-authored-by: MaranBr Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/37 Co-authored-by: MaranBr Co-committed-by: MaranBr --- .ci/linux/build.sh | 13 +++++++++---- .ci/windows/build.sh | 6 +++++- src/video_core/host1x/ffmpeg/ffmpeg.cpp | 6 ++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.ci/linux/build.sh b/.ci/linux/build.sh index aa15333ac2..093b30a7ea 100755 --- a/.ci/linux/build.sh +++ b/.ci/linux/build.sh @@ -36,15 +36,16 @@ case "$1" in ARCH=armv9 ARCH_FLAGS="-march=armv9-a -mtune=generic -w" ;; + *) + echo "Invalid target $1 specified, must be one of amd64, steamdeck, allyx, rog-ally, legacy, aarch64, armv9" + exit 1 + ;; esac export ARCH_FLAGS="$ARCH_FLAGS -O3" -NPROC="$2" if [ -z "$NPROC" ]; then NPROC="$(nproc)" -else - shift fi if [ "$1" != "" ]; then shift; fi @@ -72,11 +73,15 @@ else MULTIMEDIA=ON fi +if [ -z "$BUILD_TYPE" ]; then + export BUILD_TYPE="Release" +fi + export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@) mkdir -p build && cd build cmake .. -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DENABLE_QT_TRANSLATION=ON \ -DUSE_DISCORD_PRESENCE=ON \ -DCMAKE_CXX_FLAGS="$ARCH_FLAGS" \ diff --git a/.ci/windows/build.sh b/.ci/windows/build.sh index 667fd316fa..d0c697655a 100644 --- a/.ci/windows/build.sh +++ b/.ci/windows/build.sh @@ -17,6 +17,10 @@ else export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" -DYUZU_USE_BUNDLED_QT=OFF) fi +if [ -z "$BUILD_TYPE" ]; then + export BUILD_TYPE="Release" +fi + if [ "$WINDEPLOYQT" == "" ]; then echo "You must supply the WINDEPLOYQT environment variable." exit 1 @@ -38,7 +42,7 @@ export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@) mkdir -p build && cd build cmake .. -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DENABLE_QT_TRANSLATION=ON \ -DUSE_DISCORD_PRESENCE=ON \ -DYUZU_USE_BUNDLED_SDL2=OFF \ diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp index 5321c8c98c..d6eff2bdd7 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp @@ -216,18 +216,16 @@ bool DecoderContext::OpenContext(const Decoder& decoder) { bool DecoderContext::SendPacket(const Packet& packet) { m_temp_frame = std::make_shared(); - - if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) { + if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0 && ret != AVERROR_EOF) { LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret)); return false; } - return true; } std::shared_ptr DecoderContext::ReceiveFrame() { auto ReceiveImpl = [&](AVFrame* frame) -> bool { - if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) { + if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0 && ret != AVERROR_EOF) { LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret)); return false; } From d7574b2878d07dd2def968093c4596b85a961f99 Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 14 Jul 2025 20:51:20 -0400 Subject: [PATCH 18/41] [android] Update app icon background thx antabaka Signed-off-by: crueter --- externals/sirit/CMakeLists.txt | 2 +- .../app/src/main/res/drawable/ic_icon_bg.png | Bin 0 -> 25025 bytes .../app/src/main/res/drawable/ic_icon_bg.xml | 751 ------------------ .../src/main/res/drawable/ic_icon_bg_orig.png | Bin 0 -> 199682 bytes 4 files changed, 1 insertion(+), 752 deletions(-) create mode 100644 src/android/app/src/main/res/drawable/ic_icon_bg.png delete mode 100644 src/android/app/src/main/res/drawable/ic_icon_bg.xml create mode 100644 src/android/app/src/main/res/drawable/ic_icon_bg_orig.png diff --git a/externals/sirit/CMakeLists.txt b/externals/sirit/CMakeLists.txt index d98a8b5ba5..782ce8f660 100644 --- a/externals/sirit/CMakeLists.txt +++ b/externals/sirit/CMakeLists.txt @@ -1,6 +1,6 @@ # This file has been adapted from dynarmic -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.12) project(sirit CXX) # Determine if we're built as a subproject (using add_subdirectory) diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg.png b/src/android/app/src/main/res/drawable/ic_icon_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..30b29a32d89a07ff887e556e9db024112dd3f718 GIT binary patch literal 25025 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png b/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png new file mode 100644 index 0000000000000000000000000000000000000000..9ebc6ce17c6517140a3e4e672e56ba96a30fa715 GIT binary patch literal 199682 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ȵ+YKgZEvo$;Z1J!su)VDJsXypc@DqQn#VYJ6O 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Ð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%0AxX2da7!o-fCk%q3j=`lFxD z9xTV%khqKVyr{eSsOf?EyLZb=`(Q|OraZn)2p+(v+^(`taWJ-A*|`fBhCQ_Pwnr~U zr(}fEfnvZx`ym^{cQ!|FhhhDG)pq7gcDmjra8CdB zvar;2b8znI93y7HcVOkg3@h`t2cw_66@6Jd42H{u`Q6}@i}Kx>oWEB*HRvz5(VG`G z=)agk+$EnEL&ztD?-skZ?3#?A=r9MWwTfrMe}%2^LDWe2_D{cZ8sU}dlBO!*0{?cI z^T1CvYsu3de}Pc1mu)WSgp1TJZR?Ru!$0zR=C} zvu~0rc7gaKFOt)0|KJ{cs!PkbM+o}N-am(PlYKH1RsTllk&Wu? zu$gL|(&iS(D|{R7#_1TQ`1n7c{de=7`spI;&1!u-AEMSA^>RI1SfA6p-&B>u1mS;I z(<5}$z-ZD(Nb>?2GVq^W?jPS>r7JANzWTY2CiF8@7<>A(hcfq+@V>s=8s)N71vJ2) zzFOyhY@KNwl|*cfYKjMSdXBmoTV_N`j%zONeOfwSP{sD-7?xql*N^tU2mmsTfLC;x zau0)|M!r_xed6O?;OuzFuLSJv&9`-R18I&UM*Xk1|Of3PUt z^9{pd!@r3&;T=lfRes_cm*{Q~vp!Ue<+nj-b;E7z9KmMz)BNf0uVG~>N{Ck`44$Kl z>Y6+Yh&{k8$ywsnp6Y6I)sKpEdry;f=-(IPxUfLQd*c#mDQwq1loa)N*5iG;y?uI_ zdpt!nJVi>9{p07Qgw%fb`tR!OBq#6N)?l`ew2W`jc-JPp6+$EF{2)?%M*T#E!HtA( zj}{t=;qE_l)A08d+2HF7{4nY4zu;}Q;HzYTYT-d7@eDdsR?TdT%$b&D%J$Ix<}Ps8 zkJ?;b*50e<`Y%$0q@({lj&mI&;M&~_E@)e>)F(6l+&Q=Oip{X4i@#{cm`8gGydAN# zee54#KB#JQb2Qx>DHk>!#+qT6c~f8AR3FaSF`=xRzbAkUVqnYP#XPue7vvEMLGr>@1e`;Ei%M~Zu@c(}~P zUvvgG%B1zN6^)0wx53Bazf18b+*GV;_MH?UAA69#J6*)>Z9;%QxMXmqH$k*g(OdW@ z=!`uNDpKT*yS42U4#@ml^u%c)XQFMME6e$dk#mMgl{fj0NETGo^{9ALS}cCOkBlE2 z3_t&reM9(>);}`p7{Y(wR)7QCTD@Gk(yirKpmoW`Y9*OX=S8`bIaqf;34~75M zts#^;eHHYM=I<=?NtvM|4xt{qEk*}oHLya7gCFPlmseIwc76T1|D(kOu52uPgIWlIDjt^(UvNm!SxmmIf95i(YW@2(F9UD+((^#S zybYMRS}0YNcS~jR&A(qHSv8Ha!}G&XLWtThQJ45YS6W6B*E}Z~Vc60uU;j%6+D#c&F{I;)_ z!Lf`{6W;UGq*i#|wD)O?Dfb2Do^F9h>Q;~4w(D*4(m4CRTu{L>T>rOaN&StNpJih! z+9iZ<*5ay~;N?I%56S@)Pbb7z)x%wJ5%(Ot#r^}SZ&*q@&cg_8&t0L~Fc9RJqT&4k ziSC%V<+-p>C|0nyvf{S3p?U!>uGUo~dn<|+XyHHYqL_qBvT|J9gzB9KHR-q-nc({$ zW6`d2cG!g<1#G-Mh9WXwjK{VNMgH*@hNCpCcRi}oz#0Dxtw~Ev;FM7dZNayWCYGG% zm7Hp4`$rMek3mE+Sm=_m&Nln_b`ay<4e7z!C$6`h;LpXht+qMa=FDqO!CT8Xe3Gwx ze~oX{q5T6ECIsi2oLkltKazK}H~HFP_kHP#uT*bMw->Ol!kByD871lX^qXqz($Rkx zQkg~HL(3?F<}hysWs8%gDjtJRtmff7IiY2Piu#v(O$&Buv@L1=lj)UhczhMkZOlNS zx=j337@_hMVb2}(Vmo+2J9}d)T@gEp{g0G-=y$>vQ&#YD0(@ys&)`Z$ z=9Y|CRNVHDF*MhPc^W>xx4O0*Q1cz*e=Es7#rQSQEf!wCt@dLe!SomZu*G;xtLR+f z$e{OPqj&n-+8DN1U$WUpR7|ce{@+Xm)!*+6<)!AzP%K5)e)x!A2Yc-M=2!RL%ATdL zT?SP?wWM#)371nC{?lusFR^~HWlf5$n|^4*rtAyE8v3a(+|dNz>A4`z-Qb-mGgH0?}~sX&tJsldzDF4c-tzvh#R!m2N=*WGJcRKh?Ed|<|M(R);0Im-PkgG9I*B3 z38}&9^gJh7-hW#>h2jE!!W8ehztjtBV^6UX_UpzU(g z$Sd$h0`znjRd%mt`eU^bg9`UQzS_7)hkjaMiJ`~oM z>J5@|GLm~a?jPbNq0nw~?JkbMLWuxdQvj83&{N@nLD8{+RI~Q35V2 zp3ID8@b?OLD657mfB6*(1n2O6PzLCxbY*Fj(oR|@rL#R8pO1c5l5t~3!aM|8*>(gV z{xr5el&n&uG4pqKG`uV6@jRX9UX`K4IUclZ46`pqwSE#xT0ykoo64mNz{?94cb7^C z%I03ez4g_l0E<@OS@{$>BKs^t7lfZ7IYh6C{lo8ogx=IW?Cw}6`an^&)9CDPwgPQw>mbM#AL zCmOva!mA?8VQNPh{}UR9uO-?=-qu%Dud^8ue|xnsy;YCg&1dVo-9OLVBc6^g=#5q0 z{N%w=fRFr(80c6Q^J6fTlxzMX?GYOsU;X)Krh?j2p|+A6U3N#}#;3u7$cpz_9R_o_ z+L8ZTpM)hmH!0Tcu|*TU^~H*N=cUHqd%cE}NwnGGT^@DM2d@(NBF?&WmUT}{fO~g{ zmzRF=|C78I2tm=flV*Ia)h@M+*6^7KULv|(w=rqtzQ+~)@?Pg9#kQ2tv(C8Eu`4%1 z5jVQfam@E*PicZu9s7+1t%0~C;S#TZR+8r+Du0xRc0T{{(*?+h%1``UaS}7Hb+v8% z1}8d^C-Rg{e!ZXKKb7M6A?PdOhY6p~&?$Us1e)rB`BFTU$Aa5%^;rEW#qD_JK(}6AZUxGTdA&I}M+XO1j;eGHWRk<6XY;Z9mQr)^*vA z3a)=xBzF39Fs(+8kB|7`OMJlsQetqpMB`gmU7P?m4WHuq;V%xH8J~}?8w=e}5oSWw zu?Z!$-fOkNMzlsL<|f9j-1|%43G2LU?1la$+w(W?v`It{J8ff2nU{wo_KW&W_z-aWc@P-RuM|=ABojnM`BuZp#@S1tWVC)We zV;!ax8LG=I2R$zS7|cTa0uK(Yf}0=;fJ+;0mphU-l8uXVG%#qJ}25qW|Iz^NjYyvvs`gkkK>-Bc3 z`af}U`;E)i!+6wjR&%b=z@%T2@e139%Ka>QzC%~8!X#Yjf)mSsc~|BccATa@0Z$p|cV!>$DjKSbHLLvwxb z^fgPqbCOF(9f{celp`RX1oK3-nI6eQgTn@#61#3)S3S zx(O9~^U@X>U$da>^D*+Td6{&T@w!v2O>^9{*(w+5eA~aB_3f0dP2siyPM;WM@Hw6z z{0&%e-H+`!@1R%yFN8{0l%n<&%roKd<|;Hs`Py>}KkxGulgMvYBRYJMvUw~ncQuTD zWuSH}{vG3As|Yi2>Y9Z@9wMSddGkKp`j@@NOLk&oRowNcN`$52;3P^69F*)g%z*La zGxweiwC3~9k9ewktKCxNy`m16mbiLfn0D^{Ug`DU6HxO(@cn1h6dDJl`Q(M=iDmaD z5Vk|zE8tI#q5|Hm^hE=|cw%PMrrG6-H!;_pqf0V>ges`_gu&1MLw0bWQW&eCcC>)q zhU!H0^k71Z%^SBt9P-B{P7b2bphEtmbfsV(`FpozF8`Cf%z1XcP55ozO|{n`<41ox z`IlLIi%r7kD_E*TqE%(-q6Y%Tk^6jQJ3_07t-#D}>l8xKNBG|+7bHbF=pRZ9Qb$NV z-|h}#XuX5yHQpe87@xZ(+K!&RDD)rSyeU(7Ej*QNQ+`R`M36YW;UxZkF&Z^kpTW2X zYEfR?-8;j0+NL(9pg=t;=${Xnpdb3tkanlTG5&RhZVz_FwZ;e?=@c)RF!+qW2(Gk} zM6VvqK>r^KwfgB^SRc|YI6G;qP?UifadUJO7g96zDhLBs8PkW_WMHeI!&|=y{58$w z^>}U~6oDh?Re{^OZNDyv%&)OJW!E$su5e^o_YT@cWz_q7X zicXJ*>9xzX!@d0d$G>oj2|RcExAk;+b?p_^n(*Olk@L!%E#)za*ea)T!_VwkGaJO$ zFU$WzE?H=t+c6i*)=-bGFL(#xc(u5dqJf+Rs)Anu|2H0pB1G_B8wm}XAK%;E@V_oK z({ZN>y`j4pi74#)HKf4Zz8>tH9FqAr^aHtLvwoIIMs<9t@F_E(f14I~2YzP3!vUY3 zLF|6Ma(`jST5U@In&sj*w%kTf*?(*(`ZycgDlR102`?s8IR#>f=affX)hx|AU$}ob z@sIQwwvVwUj|~ctxO>U;v0tUJjNbWq<2^1t1{qjK3q@>URHo$L{it&eXHVsfEx==C zuwBKR9tT}4d#k<$#v`h}M&aAaxYfSsClCGYjOeGEz_~4Uc-m={{#Hw>92?|^xj2S@ o1Bqpq6JJP~wZ6#?bMD@w_(6Z>4$R6emh_#f(oMyD1!Tbg0QOINiU0rr literal 0 HcmV?d00001 From a538126eb7a0ce1e3457922a49ecc2406b8d30b1 Mon Sep 17 00:00:00 2001 From: crueter Date: Tue, 15 Jul 2025 22:24:40 +0200 Subject: [PATCH 19/41] [cmake, desktop] Fix <6.9 build error and quazip fetching (#67) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/67 --- CMakeLists.txt | 2 +- src/yuzu/CMakeLists.txt | 24 +----------------------- src/yuzu/externals/CMakeLists.txt | 31 +++++++++++++++++++++++++++++++ src/yuzu/main.cpp | 2 +- 4 files changed, 34 insertions(+), 25 deletions(-) create mode 100644 src/yuzu/externals/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b8f60089d..f8e8516dbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" OFF) else() - option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON) + option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON) endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 09738b9e03..bab6a6c4f5 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -495,29 +495,7 @@ if (YUZU_ROOM) endif() # Extra deps -set(BUILD_SHARED_LIBS OFF) - -include(CPM) -set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) -set(CPM_USE_LOCAL_PACKAGES ON) - -set(QUAZIP_QT_MAJOR_VERSION 6) -set(QUAZIP_BZIP2 OFF) - -CPMAddPackage( - URI "gh:stachenov/quazip@1.5" - PATCHES - ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch - ${CMAKE_SOURCE_DIR}/.ci/patch/0002-quazip-fetchcontent.patch -) - -if (NOT MSVC) - target_compile_options(QuaZip PRIVATE - -Wno-error=shadow - -Wno-error=missing-declarations - ) -endif() - +add_subdirectory(externals) target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) create_target_directory_groups(yuzu) diff --git a/src/yuzu/externals/CMakeLists.txt b/src/yuzu/externals/CMakeLists.txt new file mode 100644 index 0000000000..ac17308e09 --- /dev/null +++ b/src/yuzu/externals/CMakeLists.txt @@ -0,0 +1,31 @@ +# Disable tests in all externals supporting the standard option name +set(BUILD_TESTING OFF) + +# Build only static externals +set(BUILD_SHARED_LIBS OFF) + +# QuaZip +set(QUAZIP_QT_MAJOR_VERSION 6) +set(QUAZIP_BZIP2 OFF) + +include(CPM) +set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) +set(CPM_USE_LOCAL_PACKAGES ON) + +CPMAddPackage( + NAME QuaZip-Qt6 + VERSION 1.3 + GIT_REPOSITORY "https://github.com/stachenov/quazip.git" + GIT_TAG v1.5 + PATCHES + ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch + ${CMAKE_SOURCE_DIR}/.ci/patch/0002-quazip-fetchcontent.patch +) + +if (NOT MSVC AND NOT "QuaZip-Qt6" IN_LIST CPM_PACKAGES) + message(STATUS "QUAZIP DIR: ${CPM_PACKAGES}") + target_compile_options(QuaZip PRIVATE + -Wno-error=shadow + -Wno-error=missing-declarations + ) +endif() diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 634c11bdce..6a575cfa87 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4443,7 +4443,7 @@ void GMainWindow::OnInstallFirmwareFromZIP() QMessageBox::warning(this, tr("Firmware cleanup failed"), tr("Failed to clean up extracted firmware cache.\n" "Check write permissions in the system temp directory and try again.\nOS reported error: %1") - .arg(ec.message())); + .arg(QString::fromStdString(ec.message()))); } return; From 108daeeb397fa23ab02ccef93460bd6bc68e4ba9 Mon Sep 17 00:00:00 2001 From: crueter Date: Wed, 16 Jul 2025 23:17:34 +0200 Subject: [PATCH 20/41] [cmake] Fix QuaZip once and for all (#71) (and core5compat) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/71 --- .ci/patch/0002-quazip-fetchcontent.patch | 13 - .../quazip/0001-strict.patch | 0 .patch/quazip/0002-oldstyle.patch | 26 ++ .patch/quazip/0003-predecls.patch | 19 + .patch/quazip/0004-qt6-only.patch | 400 ++++++++++++++++++ CMakeLists.txt | 12 +- CMakeModules/CopyYuzuQt6Deps.cmake | 1 + CMakeModules/DownloadExternals.cmake | 20 +- src/yuzu/CMakeLists.txt | 6 +- src/yuzu/externals/CMakeLists.txt | 23 +- 10 files changed, 482 insertions(+), 38 deletions(-) delete mode 100644 .ci/patch/0002-quazip-fetchcontent.patch rename .ci/patch/0001-quazip-strict.patch => .patch/quazip/0001-strict.patch (100%) create mode 100644 .patch/quazip/0002-oldstyle.patch create mode 100644 .patch/quazip/0003-predecls.patch create mode 100644 .patch/quazip/0004-qt6-only.patch diff --git a/.ci/patch/0002-quazip-fetchcontent.patch b/.ci/patch/0002-quazip-fetchcontent.patch deleted file mode 100644 index 3554b7dbb6..0000000000 --- a/.ci/patch/0002-quazip-fetchcontent.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/cmake/clone-repo.cmake b/cmake/clone-repo.cmake -index 2ffb4b2..77974dc 100644 ---- a/cmake/clone-repo.cmake -+++ b/cmake/clone-repo.cmake -@@ -26,7 +26,7 @@ macro(clone_repo name url) - FetchContent_GetProperties(${name} POPULATED ${name_lower}_POPULATED) - - if(NOT ${name_lower}_POPULATED) -- FetchContent_Populate(${name}) -+ FetchContent_MakeAvailable(${name}) - endif() - - set(${name_upper}_SOURCE_DIR ${${name_lower}_SOURCE_DIR}) diff --git a/.ci/patch/0001-quazip-strict.patch b/.patch/quazip/0001-strict.patch similarity index 100% rename from .ci/patch/0001-quazip-strict.patch rename to .patch/quazip/0001-strict.patch diff --git a/.patch/quazip/0002-oldstyle.patch b/.patch/quazip/0002-oldstyle.patch new file mode 100644 index 0000000000..2694128f04 --- /dev/null +++ b/.patch/quazip/0002-oldstyle.patch @@ -0,0 +1,26 @@ +diff --git a/quazip/minizip_crypt.h b/quazip/minizip_crypt.h +index 2e833f7..ea9d277 100644 +--- a/quazip/minizip_crypt.h ++++ b/quazip/minizip_crypt.h +@@ -90,13 +90,14 @@ static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t FAR + # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ + # endif + +-static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) +- const char *passwd; /* password string */ +- unsigned char *buf; /* where to write header */ +- int bufSize; +- unsigned long* pkeys; +- const z_crc_t FAR * pcrc_32_tab; +- unsigned long crcForCrypting; ++static int crypthead( ++ const char *passwd, /* password string */ ++ unsigned char *buf, /* where to write header */ ++ int bufSize, ++ unsigned long* pkeys, ++ const z_crc_t FAR * pcrc_32_tab, ++ unsigned long crcForCrypting ++) + { + int n; /* index in random header */ + int t; /* temporary */ diff --git a/.patch/quazip/0003-predecls.patch b/.patch/quazip/0003-predecls.patch new file mode 100644 index 0000000000..ec3414c82a --- /dev/null +++ b/.patch/quazip/0003-predecls.patch @@ -0,0 +1,19 @@ +diff --git a/quazip/zip.c b/quazip/zip.c +index 7788b88..f4e21aa 100644 +--- a/quazip/zip.c ++++ b/quazip/zip.c +@@ -645,6 +645,14 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib + return relativeOffset; + } + ++// compilers hate this ONE SIMPLE TRICK! ++static int LoadCentralDirectoryRecord(zip64_internal* pziinit); ++static int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local, uLong version_to_extract); ++static int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip); ++static int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip); ++static int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip); ++static int Write_GlobalComment(zip64_internal* zi, const char* global_comment); ++ + int LoadCentralDirectoryRecord(zip64_internal* pziinit) + { + int err=ZIP_OK; diff --git a/.patch/quazip/0004-qt6-only.patch b/.patch/quazip/0004-qt6-only.patch new file mode 100644 index 0000000000..8906df2472 --- /dev/null +++ b/.patch/quazip/0004-qt6-only.patch @@ -0,0 +1,400 @@ +"Debloats" QuaZip by removing some unneeded stuff (Qt <6, bzip2, emscripten...) + +This is completely optional. + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index b376fb2..4aac4ec 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -3,64 +3,16 @@ cmake_minimum_required(VERSION 3.15...3.18) + + project(QuaZip VERSION 1.5) + +-include(cmake/clone-repo.cmake) +- + set(QUAZIP_LIB_VERSION ${QuaZip_VERSION}) + set(QUAZIP_LIB_SOVERSION 1.5.0) + +-if(EMSCRIPTEN) +- #option(ZLIB_INCLUDE "Path to include dir" "") +- #option(ZLIB_LIBRARY "Path to library dir" "") +- option(BUILD_SHARED_LIBS "" OFF) +- option(QUAZIP_INSTALL "" OFF) +- option(QUAZIP_USE_QT_ZLIB "" OFF) +- option(QUAZIP_ENABLE_TESTS "Build QuaZip tests" OFF) +-else() +- option(BUILD_SHARED_LIBS "" ON) +- option(QUAZIP_INSTALL "" ON) +- option(QUAZIP_USE_QT_ZLIB "" OFF) +- option(QUAZIP_ENABLE_TESTS "Build QuaZip tests" OFF) +-endif() ++option(BUILD_SHARED_LIBS "" ON) ++option(QUAZIP_INSTALL "" ON) ++option(QUAZIP_ENABLE_TESTS "Build QuaZip tests" OFF) + + OPTION(ZLIB_CONST "Sets ZLIB_CONST preprocessor definition" OFF) + +-# Make BZIP2 optional +-option(QUAZIP_BZIP2 "Enables BZIP2 compression" ON) +-option(QUAZIP_BZIP2_STDIO "Output BZIP2 errors to stdio" ON) +- +-option(QUAZIP_FETCH_LIBS "Enables fetching third-party libraries if not found" ${WIN32}) +-option(QUAZIP_FORCE_FETCH_LIBS "Enables fetching third-party libraries always" OFF) +- +-if (QUAZIP_USE_QT_ZLIB AND BUILD_SHARED_LIBS) +- message(FATAL_ERROR "Using BUILD_SHARED_LIBS=ON together with QUAZIP_USE_QT_ZLIB=ON is not supported." ) +-endif() +- +-# Set the default value of `${QUAZIP_QT_MAJOR_VERSION}`. +-# We search quietly for Qt6, Qt5 and Qt4 in that order. +-# Qt6 and Qt5 provide config files for CMake. +-# Qt4 relies on `FindQt4.cmake`. +-find_package( +- QT NAMES Qt6 Qt5 +- QUIET COMPONENTS Core +-) +-if (NOT QT_FOUND) +- find_package(Qt4 QUIET COMPONENTS QtCore) +- if (Qt4_FOUND) +- set(QT_VERSION_MAJOR 4) +- else() +- # If neither 6, 5 nor 4 are found, we default to 5. +- # The setup will fail further down. +- set(QT_VERSION_MAJOR 5) +- endif() +-endif() +- +-set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}") +- +-if (QUAZIP_QT_MAJOR_VERSION EQUAL 6) +- set(CMAKE_CXX_STANDARD 17) +-else() +- set(CMAKE_CXX_STANDARD 14) +-endif() ++set(CMAKE_CXX_STANDARD 17) + + if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RELEASE) +@@ -77,92 +29,17 @@ set(QUAZIP_LIB_TARGET_NAME QuaZip) + set(QUAZIP_DIR_NAME QuaZip-Qt${QUAZIP_QT_MAJOR_VERSION}-${QUAZIP_LIB_VERSION}) + set(QUAZIP_PACKAGE_NAME QuaZip-Qt${QUAZIP_QT_MAJOR_VERSION}) + +-message(STATUS "QUAZIP_QT_MAJOR_VERSION set to ${QUAZIP_QT_MAJOR_VERSION}") +-message(STATUS "CMAKE_CXX_STANDARD set to ${CMAKE_CXX_STANDARD}") +- +-if(QUAZIP_QT_MAJOR_VERSION EQUAL 6) +- find_package(Qt6 REQUIRED COMPONENTS Core Core5Compat +- OPTIONAL_COMPONENTS Network Test) +- message(STATUS "Found Qt version ${Qt6_VERSION} at ${Qt6_DIR}") +- set(QUAZIP_QT_ZLIB_COMPONENT BundledZLIB) +- set(QUAZIP_QT_ZLIB_HEADER_COMPONENT ZlibPrivate) +- set(QUAZIP_LIB_LIBRARIES Qt6::Core Qt6::Core5Compat) +- set(QUAZIP_TEST_QT_LIBRARIES Qt6::Core Qt6::Core5Compat Qt6::Network Qt6::Test) +- set(QUAZIP_PKGCONFIG_REQUIRES "zlib, Qt6Core") +-elseif(QUAZIP_QT_MAJOR_VERSION EQUAL 5) +- find_package(Qt5 REQUIRED COMPONENTS Core +- OPTIONAL_COMPONENTS Network Test) +- message(STATUS "Found Qt version ${Qt5_VERSION} at ${Qt5_DIR}") +- set(QUAZIP_QT_ZLIB_COMPONENT Zlib) +- set(QUAZIP_LIB_LIBRARIES Qt5::Core) +- set(QUAZIP_TEST_QT_LIBRARIES Qt5::Core Qt5::Network Qt5::Test) +- set(QUAZIP_PKGCONFIG_REQUIRES "zlib, Qt5Core") +-elseif(QUAZIP_QT_MAJOR_VERSION EQUAL 4) +- find_package(Qt4 4.5.0 REQUIRED COMPONENTS QtCore +- OPTIONAL_COMPONENTS QtNetwork QtTest) +- set(QUAZIP_QT_ZLIB_COMPONENT Zlib) +- set(QUAZIP_LIB_LIBRARIES Qt4::QtCore) +- set(QUAZIP_TEST_QT_LIBRARIES Qt4::QtCore Qt4::QtNetwork Qt4::QtTest) +- set(QUAZIP_PKGCONFIG_REQUIRES "zlib, QtCore") +-else() +- message(FATAL_ERROR "Qt version ${QUAZIP_QT_MAJOR_VERSION} is not supported") +-endif() +- +-message(STATUS "Using Qt version ${QUAZIP_QT_MAJOR_VERSION}") +- +-set(QUAZIP_QT_ZLIB_USED OFF) +-if(QUAZIP_USE_QT_ZLIB) +- find_package(Qt${QUAZIP_QT_MAJOR_VERSION} OPTIONAL_COMPONENTS ${QUAZIP_QT_ZLIB_COMPONENT}) +- set(QUAZIP_QT_ZLIB_COMPONENT_FOUND Qt${QUAZIP_QT_MAJOR_VERSION}${QUAZIP_QT_ZLIB_COMPONENT}_FOUND) +- if (DEFINED QUAZIP_QT_ZLIB_HEADER_COMPONENT) +- find_package(Qt${QUAZIP_QT_MAJOR_VERSION} OPTIONAL_COMPONENTS ${QUAZIP_QT_ZLIB_HEADER_COMPONENT}) +- set(QUAZIP_QT_ZLIB_HEADER_COMPONENT_FOUND Qt${QUAZIP_QT_MAJOR_VERSION}${QUAZIP_QT_ZLIB_HEADER_COMPONENT}_FOUND) +- else() +- set(QUAZIP_QT_ZLIB_HEADER_COMPONENT_FOUND ON) +- endif() +- if(QUAZIP_QT_ZLIB_COMPONENT_FOUND AND QUAZIP_QT_ZLIB_HEADER_COMPONENT_FOUND) +- message(STATUS "Qt component ${QUAZIP_QT_ZLIB_COMPONENT} found") +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} Qt${QUAZIP_QT_MAJOR_VERSION}::${QUAZIP_QT_ZLIB_COMPONENT}) +- if(DEFINED QUAZIP_QT_ZLIB_HEADER_COMPONENT) +- message(STATUS "Qt component ${QUAZIP_QT_ZLIB_HEADER_COMPONENT} found") +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} Qt${QUAZIP_QT_MAJOR_VERSION}::${QUAZIP_QT_ZLIB_HEADER_COMPONENT}) +- endif() +- set(QUAZIP_QT_ZLIB_USED ON) +- else() +- message(FATAL_ERROR "QUAZIP_USE_QT_ZLIB was set but bundled zlib was not found. Terminating to prevent accidental linking to system libraries.") +- endif() +-endif() +- +-if(QUAZIP_QT_ZLIB_USED AND QUAZIP_QT_ZLIB_COMPONENT STREQUAL BundledZLIB) +- # Qt's new BundledZLIB uses z-prefix in zlib +- add_compile_definitions(Z_PREFIX) +-endif() +- +-if(NOT QUAZIP_QT_ZLIB_USED) +- +- if(EMSCRIPTEN) +- if(NOT DEFINED ZLIB_LIBRARY) +- message(WARNING "ZLIB_LIBRARY is not set") +- endif() ++find_package(Qt6 REQUIRED COMPONENTS Core Core5Compat ++ OPTIONAL_COMPONENTS Network Test) ++message(STATUS "Found Qt version ${Qt6_VERSION} at ${Qt6_DIR}") ++set(QUAZIP_QT_ZLIB_COMPONENT BundledZLIB) ++set(QUAZIP_QT_ZLIB_HEADER_COMPONENT ZlibPrivate) ++set(QUAZIP_LIB_LIBRARIES Qt6::Core Qt6::Core5Compat) ++set(QUAZIP_TEST_QT_LIBRARIES Qt6::Core Qt6::Core5Compat Qt6::Network Qt6::Test) ++set(QUAZIP_PKGCONFIG_REQUIRES "zlib, Qt6Core") + +- if(NOT DEFINED ZLIB_INCLUDE) +- message(WARNING "ZLIB_INCLUDE is not set") +- else() +- include_directories(${ZLIB_INCLUDE}) +- endif() +- +- if(NOT DEFINED ZCONF_INCLUDE) +- message(WARNING "ZCONF_INCLUDE is not set") +- else() +- include_directories(${ZCONF_INCLUDE}) +- endif() +- +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} ${ZLIB_LIBRARY}) +- else() +- find_package(ZLIB REQUIRED) +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} ZLIB::ZLIB) +- endif() +-endif() ++find_package(ZLIB REQUIRED) ++set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} ZLIB::ZLIB) + + if (ZLIB_CONST) + add_compile_definitions(ZLIB_CONST) +@@ -173,65 +50,4 @@ set(QUAZIP_INC) + set(QUAZIP_LIB) + set(QUAZIP_LBD) + +-if(QUAZIP_BZIP2) +- # Check if bzip2 is present +- set(QUAZIP_BZIP2 ON) +- +- if(NOT QUAZIP_FORCE_FETCH_LIBS) +- find_package(BZip2 QUIET) +- endif() +- +- if(BZIP2_FOUND AND NOT QUAZIP_FORCE_FETCH_LIBS) +- message(STATUS "Using BZIP2 ${BZIP2_VERSION_STRING}") +- +- list(APPEND QUAZIP_INC ${BZIP2_INCLUDE_DIRS}) +- list(APPEND QUAZIP_LIB ${BZIP2_LIBRARIES}) +- list(APPEND QUAZIP_LBD ${BZIP2_LIBRARY_DIRS}) +- +- set(PC_PRIVATE_LIBS "${PC_PRIVATE_LIBS} -lbzip2") +- elseif(QUAZIP_FETCH_LIBS) +- clone_repo(bzip2 https://sourceware.org/git/bzip2.git) +- +- # BZip2 repository does not support cmake so we have to create +- # the bzip2 library ourselves +- set(BZIP2_SRC +- ${BZIP2_SOURCE_DIR}/blocksort.c +- ${BZIP2_SOURCE_DIR}/bzlib.c +- ${BZIP2_SOURCE_DIR}/compress.c +- ${BZIP2_SOURCE_DIR}/crctable.c +- ${BZIP2_SOURCE_DIR}/decompress.c +- ${BZIP2_SOURCE_DIR}/huffman.c +- ${BZIP2_SOURCE_DIR}/randtable.c) +- +- set(BZIP2_HDR +- ${BZIP2_SOURCE_DIR}/bzlib.h +- ${BZIP2_SOURCE_DIR}/bzlib_private.h) +- +- add_library(bzip2 STATIC ${BZIP2_SRC} ${BZIP2_HDR}) +- +- if(NOT QUAZIP_BZIP2_STDIO) +- target_compile_definitions(bzip2 PRIVATE -DBZ_NO_STDIO) +- endif() +- +- list(APPEND QUAZIP_DEP bzip2) +- list(APPEND QUAZIP_LIB bzip2) +- list(APPEND QUAZIP_INC ${BZIP2_SOURCE_DIR}) +- else() +- message(STATUS "BZip2 library not found") +- +- set(QUAZIP_BZIP2 OFF) +- endif() +- +- if(QUAZIP_BZIP2) +- find_package(BZip2) +- add_compile_definitions(HAVE_BZIP2) +- endif() +-endif() +- + add_subdirectory(quazip) +- +-if(QUAZIP_ENABLE_TESTS) +- message(STATUS "Building QuaZip tests") +- enable_testing() +- add_subdirectory(qztest) +-endif() +diff --git a/quazip/CMakeLists.txt b/quazip/CMakeLists.txt +index 6cfdf4e..66bc4cb 100644 +--- a/quazip/CMakeLists.txt ++++ b/quazip/CMakeLists.txt +@@ -46,10 +46,6 @@ set(QUAZIP_INCLUDE_PATH ${QUAZIP_DIR_NAME}/quazip) + set(QUAZIP_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake) + set(QUAZIP_PKGCONFIG_NAME quazip${QuaZip_VERSION_MAJOR}-qt${QUAZIP_QT_MAJOR_VERSION}) + +-if(EMSCRIPTEN) +- set(BUILD_SHARED_LIBS OFF) +-endif() +- + add_library(${QUAZIP_LIB_TARGET_NAME} ${QUAZIP_SOURCES}) + add_library(QuaZip::QuaZip ALIAS ${QUAZIP_LIB_TARGET_NAME}) + +diff --git a/quazip/quazip_qt_compat.h b/quazip/quazip_qt_compat.h +index 0dde011..41f9dd1 100644 +--- a/quazip/quazip_qt_compat.h ++++ b/quazip/quazip_qt_compat.h +@@ -14,16 +14,11 @@ + + // Legacy encodings are still everywhere, but the Qt team decided we + // don't need them anymore and moved them out of Core in Qt 6. +-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +-# include +-#else +-# include +-#endif ++#include + + // QSaveFile terribly breaks the is-a idiom (Liskov substitution principle): + // QSaveFile is-a QIODevice, but it makes close() private and aborts + // if you call it through the base class. Hence this ugly hack: +-#if (QT_VERSION >= 0x050100) + #include + inline bool quazip_close(QIODevice *device) { + QSaveFile *file = qobject_cast(device); +@@ -34,74 +29,35 @@ inline bool quazip_close(QIODevice *device) { + device->close(); + return true; + } +-#else +-inline bool quazip_close(QIODevice *device) { +- device->close(); +- return true; +-} +-#endif + +-// this is yet another stupid move and deprecation +-#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + using Qt::SkipEmptyParts; +-#else +-#include +-const auto SkipEmptyParts = QString::SplitBehavior::SkipEmptyParts; +-#endif + + // and yet another... (why didn't they just make qSort delegate to std::sort?) + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) + #include + template + inline void quazip_sort(T begin, T end, C comparator) { + std::sort(begin, end, comparator); + } +-#else +-#include +-template +-inline void quazip_sort(T begin, T end, C comparator) { +- qSort(begin, end, comparator); +-} +-#endif + + // this is a stupid rename... + #include + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + inline QDateTime quazip_ctime(const QFileInfo &fi) { + return fi.birthTime(); + } +-#else +-inline QDateTime quazip_ctime(const QFileInfo &fi) { +- return fi.created(); +-} +-#endif + + // this is just a slightly better alternative + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + inline bool quazip_is_symlink(const QFileInfo &fi) { + return fi.isSymbolicLink(); + } +-#else +-inline bool quazip_is_symlink(const QFileInfo &fi) { +- // also detects *.lnk on Windows, but better than nothing +- return fi.isSymLink(); +-} +-#endif + + // I'm not even sure what this one is, but nevertheless + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + inline QString quazip_symlink_target(const QFileInfo &fi) { + return fi.symLinkTarget(); + } +-#else +-inline QString quazip_symlink_target(const QFileInfo &fi) { +- return fi.readLink(); // What's the difference? I've no idea. +-} +-#endif + + // deprecation + #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) +@@ -125,40 +81,19 @@ inline QDateTime quazip_since_epoch_ntfs() { + + // this is not a deprecation but an improvement, for a change + #include +-#if (QT_VERSION >= 0x040700) + inline quint64 quazip_ntfs_ticks(const QDateTime &time, int fineTicks) { + QDateTime base = quazip_since_epoch_ntfs(); + return base.msecsTo(time) * 10000 + fineTicks; + } +-#else +-inline quint64 quazip_ntfs_ticks(const QDateTime &time, int fineTicks) { +- QDateTime base = quazip_since_epoch_ntfs(); +- QDateTime utc = time.toUTC(); +- return (static_cast(base.date().daysTo(utc.date())) +- * Q_INT64_C(86400000) +- + static_cast(base.time().msecsTo(utc.time()))) +- * Q_INT64_C(10000) + fineTicks; +-} +-#endif + + // yet another improvement... + #include +-#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) // Yay! Finally a way to get time as qint64! + inline qint64 quazip_to_time64_t(const QDateTime &time) { + return time.toSecsSinceEpoch(); + } +-#else +-inline qint64 quazip_to_time64_t(const QDateTime &time) { +- return static_cast(time.toTime_t()); // 32 bits only, but better than nothing +-} +-#endif + + #include +-// and another stupid move +-#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + const auto quazip_endl = Qt::endl; +-#else +-const auto quazip_endl = endl; +-#endif + + #endif // QUAZIP_QT_COMPAT_H ++ diff --git a/CMakeLists.txt b/CMakeLists.txt index f8e8516dbc..2621822d4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,8 @@ option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF) option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF) +set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries") + option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) @@ -465,12 +467,10 @@ if (ENABLE_QT) list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}") endif() - # QT6 Multimedia pulls in unneeded audio systems (ALSA, Pulseaudio) for FreeBSD - # ALSA is the default sound system on Linux, but FreeBSD uses OSS which works well enough - if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") - find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent) - else() - find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent) + find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent Core5Compat) + + if (YUZU_USE_QT_MULTIMEDIA) + find_package(Qt6 REQUIRED COMPONENTS Multimedia) endif() if (UNIX AND NOT APPLE) diff --git a/CMakeModules/CopyYuzuQt6Deps.cmake b/CMakeModules/CopyYuzuQt6Deps.cmake index 39f88cbc19..93c4fd0170 100644 --- a/CMakeModules/CopyYuzuQt6Deps.cmake +++ b/CMakeModules/CopyYuzuQt6Deps.cmake @@ -20,6 +20,7 @@ function(copy_yuzu_Qt6_deps target_dir) if (MSVC) windows_copy_files(${target_dir} ${Qt6_DLL_DIR} ${DLL_DEST} Qt6Core$<$:d>.* + Qt6Core5Compat$<$:d>.* Qt6Gui$<$:d>.* Qt6Widgets$<$:d>.* Qt6Network$<$:d>.* diff --git a/CMakeModules/DownloadExternals.cmake b/CMakeModules/DownloadExternals.cmake index a82d1d72a3..b354ccfbe3 100644 --- a/CMakeModules/DownloadExternals.cmake +++ b/CMakeModules/DownloadExternals.cmake @@ -94,7 +94,7 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out else() set(host "linux") set(type "desktop") - set(arch "gcc_64") + set(arch "linux_gcc_64") set(arch_path "linux") endif() @@ -133,12 +133,26 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target}) else() set(prefix "${base_path}/${target}/${arch_path}") - set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt3d qt5compat qtactiveqt qtcharts qtconnectivity qtdatavis3d qtgraphs qtgrpc qthttpserver qtimageformats qtlanguageserver qtlocation qtlottie qtmultimedia qtnetworkauth qtpdf qtpositioning qtquick3d qtquick3dphysics qtquickeffectmaker qtquicktimeline qtremoteobjects qtscxml qtsensors qtserialbus qtserialport qtshadertools qtspeech qtvirtualkeyboard qtwebchannel qtwebengine qtwebsockets qtwebview) + set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt5compat) + + if (YUZU_USE_QT_MULTIMEDIA) + set(install_args ${install_args} qtmultimedia) + endif() + + if (YUZU_USE_QT_WEB_ENGINE) + set(install_args ${install_args} qtpositioning qtwebchannel qtwebengine) + endif() + + if (NOT ${YUZU_QT_MIRROR} STREQUAL "") + message(STATUS "Using Qt mirror ${YUZU_QT_MIRROR}") + set(install_args ${install_args} -b ${YUZU_QT_MIRROR}) + endif() endif() + message(STATUS "Install Args ${install_args}") if (NOT EXISTS "${prefix}") message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}") - set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.2.1") + set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.3.0") if (WIN32) set(aqt_path "${base_path}/aqt.exe") if (NOT EXISTS "${aqt_path}") diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index bab6a6c4f5..e0733fbe92 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -462,8 +462,8 @@ if (WIN32 AND NOT YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_GREATER_EQUAL 6) endif() if (YUZU_USE_BUNDLED_QT) - include(CopyYuzuQt6Deps) - copy_yuzu_Qt6_deps(yuzu) + include(CopyYuzuQt6Deps) + copy_yuzu_Qt6_deps(yuzu) endif() if (ENABLE_SDL2) @@ -496,6 +496,6 @@ endif() # Extra deps add_subdirectory(externals) -target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) +target_link_libraries(yuzu PRIVATE QuaZip::QuaZip Qt6::Core5Compat) create_target_directory_groups(yuzu) diff --git a/src/yuzu/externals/CMakeLists.txt b/src/yuzu/externals/CMakeLists.txt index ac17308e09..bd63f4e23b 100644 --- a/src/yuzu/externals/CMakeLists.txt +++ b/src/yuzu/externals/CMakeLists.txt @@ -5,9 +5,6 @@ set(BUILD_TESTING OFF) set(BUILD_SHARED_LIBS OFF) # QuaZip -set(QUAZIP_QT_MAJOR_VERSION 6) -set(QUAZIP_BZIP2 OFF) - include(CPM) set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) set(CPM_USE_LOCAL_PACKAGES ON) @@ -18,14 +15,14 @@ CPMAddPackage( GIT_REPOSITORY "https://github.com/stachenov/quazip.git" GIT_TAG v1.5 PATCHES - ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch - ${CMAKE_SOURCE_DIR}/.ci/patch/0002-quazip-fetchcontent.patch -) + ${CMAKE_SOURCE_DIR}/.patch/quazip/0001-strict.patch + ${CMAKE_SOURCE_DIR}/.patch/quazip/0002-oldstyle.patch + ${CMAKE_SOURCE_DIR}/.patch/quazip/0003-predecls.patch + ${CMAKE_SOURCE_DIR}/.patch/quazip/0004-qt6-only.patch -if (NOT MSVC AND NOT "QuaZip-Qt6" IN_LIST CPM_PACKAGES) - message(STATUS "QUAZIP DIR: ${CPM_PACKAGES}") - target_compile_options(QuaZip PRIVATE - -Wno-error=shadow - -Wno-error=missing-declarations - ) -endif() + # thanks to 0004-qt6-only.patch, this isn't needed, + # but we keep it since the patch is "technically" optional + OPTIONS + "QUAZIP_QT_MAJOR_VERSION 6" + "QUAZIP_BZIP2 OFF" +) From 2aab37b516c67c31dfcb0e6bfe2e721ce1d52248 Mon Sep 17 00:00:00 2001 From: crueter Date: Thu, 17 Jul 2025 21:00:00 +0200 Subject: [PATCH 21/41] [cmake] QuaZip: The Finale (#74) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/74 --- CMakeLists.txt | 2 +- CMakeModules/CopyYuzuQt6Deps.cmake | 1 - CMakeModules/DownloadExternals.cmake | 2 +- src/yuzu/CMakeLists.txt | 2 +- src/yuzu/externals/CMakeLists.txt | 15 ++------------- 5 files changed, 5 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2621822d4f..124a5ac80a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -467,7 +467,7 @@ if (ENABLE_QT) list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}") endif() - find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent Core5Compat) + find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent) if (YUZU_USE_QT_MULTIMEDIA) find_package(Qt6 REQUIRED COMPONENTS Multimedia) diff --git a/CMakeModules/CopyYuzuQt6Deps.cmake b/CMakeModules/CopyYuzuQt6Deps.cmake index 93c4fd0170..39f88cbc19 100644 --- a/CMakeModules/CopyYuzuQt6Deps.cmake +++ b/CMakeModules/CopyYuzuQt6Deps.cmake @@ -20,7 +20,6 @@ function(copy_yuzu_Qt6_deps target_dir) if (MSVC) windows_copy_files(${target_dir} ${Qt6_DLL_DIR} ${DLL_DEST} Qt6Core$<$:d>.* - Qt6Core5Compat$<$:d>.* Qt6Gui$<$:d>.* Qt6Widgets$<$:d>.* Qt6Network$<$:d>.* diff --git a/CMakeModules/DownloadExternals.cmake b/CMakeModules/DownloadExternals.cmake index b354ccfbe3..e65827290d 100644 --- a/CMakeModules/DownloadExternals.cmake +++ b/CMakeModules/DownloadExternals.cmake @@ -133,7 +133,7 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target}) else() set(prefix "${base_path}/${target}/${arch_path}") - set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt5compat) + set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qtbase) if (YUZU_USE_QT_MULTIMEDIA) set(install_args ${install_args} qtmultimedia) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index e0733fbe92..34f2ba455a 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -496,6 +496,6 @@ endif() # Extra deps add_subdirectory(externals) -target_link_libraries(yuzu PRIVATE QuaZip::QuaZip Qt6::Core5Compat) +target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) create_target_directory_groups(yuzu) diff --git a/src/yuzu/externals/CMakeLists.txt b/src/yuzu/externals/CMakeLists.txt index bd63f4e23b..d606e27108 100644 --- a/src/yuzu/externals/CMakeLists.txt +++ b/src/yuzu/externals/CMakeLists.txt @@ -12,17 +12,6 @@ set(CPM_USE_LOCAL_PACKAGES ON) CPMAddPackage( NAME QuaZip-Qt6 VERSION 1.3 - GIT_REPOSITORY "https://github.com/stachenov/quazip.git" - GIT_TAG v1.5 - PATCHES - ${CMAKE_SOURCE_DIR}/.patch/quazip/0001-strict.patch - ${CMAKE_SOURCE_DIR}/.patch/quazip/0002-oldstyle.patch - ${CMAKE_SOURCE_DIR}/.patch/quazip/0003-predecls.patch - ${CMAKE_SOURCE_DIR}/.patch/quazip/0004-qt6-only.patch - - # thanks to 0004-qt6-only.patch, this isn't needed, - # but we keep it since the patch is "technically" optional - OPTIONS - "QUAZIP_QT_MAJOR_VERSION 6" - "QUAZIP_BZIP2 OFF" + GIT_REPOSITORY "https://github.com/crueter/quazip-qt6.git" + GIT_TAG v1.5-qt6 ) From 1a35aef644e89cfe77bf2d6d5967e463e79c53f9 Mon Sep 17 00:00:00 2001 From: crueter Date: Fri, 18 Jul 2025 20:02:13 +0200 Subject: [PATCH 22/41] [android] Better error handling for username collision/validity (#76) also removed the generate button from sw keyboard Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/76 --- CMakeModules/DownloadExternals.cmake | 2 +- .../yuzu/yuzu_emu/dialogs/NetPlayDialog.kt | 35 ++++++------ .../settings/model/view/SettingsItem.kt | 9 +-- .../settings/ui/SettingsDialogFragment.kt | 7 +-- .../yuzu_emu/network/NetDataValidators.kt | 56 +++++++++++++++++++ .../src/main/res/layout/dialog_edit_text.xml | 1 + .../app/src/main/res/values/strings.xml | 11 ++-- 7 files changed, 87 insertions(+), 34 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt diff --git a/CMakeModules/DownloadExternals.cmake b/CMakeModules/DownloadExternals.cmake index e65827290d..3e9bc26d69 100644 --- a/CMakeModules/DownloadExternals.cmake +++ b/CMakeModules/DownloadExternals.cmake @@ -133,7 +133,7 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target}) else() set(prefix "${base_path}/${target}/${arch_path}") - set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qtbase) + set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt_base) if (YUZU_USE_QT_MULTIMEDIA) set(install_args ${install_args} qtmultimedia) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt index ede2cfafa4..cd3e9a4474 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt @@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.databinding.ItemBanListBinding import org.yuzu.yuzu_emu.databinding.ItemButtonNetplayBinding import org.yuzu.yuzu_emu.databinding.ItemTextNetplayBinding import org.yuzu.yuzu_emu.features.settings.model.StringSetting +import org.yuzu.yuzu_emu.network.NetDataValidators import org.yuzu.yuzu_emu.network.NetPlayManager import org.yuzu.yuzu_emu.utils.CompatUtils import org.yuzu.yuzu_emu.utils.GameHelper @@ -102,8 +103,16 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { dismiss() } btnLobbyBrowser.setOnClickListener { - LobbyBrowser(context).show() - dismiss() + if (!NetDataValidators.username()) { + Toast.makeText( + context, + R.string.multiplayer_nickname_invalid, + Toast.LENGTH_LONG + ).show() + } else { + LobbyBrowser(context).show() + dismiss() + } } } } @@ -368,7 +377,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { ) ) { override fun validate(s: String): Boolean { - return s.length in 3..20 + return NetDataValidators.roomName(s) } } @@ -378,7 +387,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_required) ) { override fun validate(s: String): Boolean { - return s.isNotEmpty() + return NetDataValidators.notEmpty(s) } } @@ -388,12 +397,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_token_required) ) { override fun validate(s: String): Boolean { - if (s != context.getString(R.string.multiplayer_public_visibility)) { - return true; - } - - val token = StringSetting.WEB_TOKEN.getString() - return token.matches(Regex("[a-z]{48}")) + return NetDataValidators.roomVisibility(s, context) } } @@ -403,12 +407,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_ip_error) ) { override fun validate(s: String): Boolean { - return try { - InetAddress.getByName(s) - s.length >= 7 - } catch (_: Exception) { - false - } + return NetDataValidators.ipAddress(s) } } @@ -418,7 +417,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_username_error) ) { override fun validate(s: String): Boolean { - return s.length in 4..20 + return NetDataValidators.username(s) } } @@ -428,7 +427,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) { context.getString(R.string.multiplayer_port_error) ) { override fun validate(s: String): Boolean { - return s.toIntOrNull() in 1..65535 + return NetDataValidators.port(s) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index ab35a9180c..a269cab254 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -20,6 +20,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.LongSetting import org.yuzu.yuzu_emu.features.settings.model.ShortSetting import org.yuzu.yuzu_emu.features.settings.model.StringSetting +import org.yuzu.yuzu_emu.network.NetDataValidators import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.NativeConfig @@ -300,9 +301,7 @@ abstract class SettingsItem( val chars = "abcdefghijklmnopqrstuvwxyz" (1..48).map { chars.random() }.joinToString("") }, - validator = { s -> - s?.matches(Regex("[a-z]{48}")) == true - }, + validator = NetDataValidators::token, errorId = R.string.multiplayer_token_error ) ) @@ -312,9 +311,7 @@ abstract class SettingsItem( StringSetting.WEB_USERNAME, titleId = R.string.web_username, descriptionId = R.string.web_username_description, - validator = { s -> - s?.length in 4..20 - }, + validator = NetDataValidators::username, errorId = R.string.multiplayer_username_error ) ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt index e50ebe50f4..aa17d05e34 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt @@ -154,8 +154,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener stringInputBinding.generate.setOnClickListener { stringInputBinding.editText.setText(onGenerate()) } - } else { - stringInputBinding.generate.isVisible = false } val validator = item.validator @@ -179,8 +177,9 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener } override fun afterTextChanged(s: Editable?) { - stringInputBinding.editText.error = - if (validator(s.toString())) null else requireContext().getString(item.errorId) + val isValid = validator(s.toString()) + stringInputBinding.editTextLayout.isErrorEnabled = !isValid + stringInputBinding.editTextLayout.error = if (isValid) null else requireContext().getString(item.errorId) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt new file mode 100644 index 0000000000..b3edf35d8e --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +package org.yuzu.yuzu_emu.network + +import android.content.Context +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.features.settings.model.StringSetting +import java.net.InetAddress + +object NetDataValidators { + fun roomName(s: String): Boolean { + return s.length in 3..20 + } + + fun notEmpty(s: String): Boolean { + return s.isNotEmpty() + } + + fun token(s: String?): Boolean { + return s?.matches(Regex("[a-z]{48}")) == true + } + + fun token(): Boolean { + return token(StringSetting.WEB_TOKEN.getString()) + } + + fun roomVisibility(s: String, context: Context): Boolean { + if (s != context.getString(R.string.multiplayer_public_visibility)) { + return true; + } + + return token() + } + + fun ipAddress(s: String): Boolean { + return try { + InetAddress.getByName(s) + s.length >= 7 + } catch (_: Exception) { + false + } + } + + fun username(s: String?): Boolean { + return s?.matches(Regex("^[ a-zA-Z0-9._-]{4,20}$")) == true + } + + fun username(): Boolean { + return username(StringSetting.WEB_USERNAME.getString()) + } + + fun port(s: String): Boolean { + return s.toIntOrNull() in 1..65535 + } +} \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/dialog_edit_text.xml b/src/android/app/src/main/res/layout/dialog_edit_text.xml index 3612f46eae..bc7864a395 100644 --- a/src/android/app/src/main/res/layout/dialog_edit_text.xml +++ b/src/android/app/src/main/res/layout/dialog_edit_text.xml @@ -27,6 +27,7 @@ android:layout_height="wrap_content" android:layout_marginTop="15dp" android:text="@string/generate" + android:visibility="gone" app:layout_constraintEnd_toEndOf="@+id/edit_text_layout" app:layout_constraintTop_toBottomOf="@+id/edit_text_layout" /> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index e611e66c1f..3814105ea0 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -148,18 +148,18 @@ Failed to join room Name is too short Invalid address - Invalid port! + Invalid port Exit Room Network error Lost connection - Name collision + Username already taken MAC Address collision Console ID collision Wrong version Wrong password Could not connect Room is full - Host banned + You are banned from this room Permission denied No such user Already in room @@ -221,7 +221,8 @@ Required Web Token required, go to Advanced Settings -> System -> Network Invalid IP format - Must be between 4–20 characters + Must be between 4–20 characters, and contain alphanumeric characters, periods, dashes, underscores, and spaces only + Username invalid, ensure it is set properly in System -> Network Must be 48 characters, and lowercase a-z only Must be between 1 and 65535 Cancel @@ -467,7 +468,7 @@ Web Token Web token used for creating public lobbies. It is a 48-character string containing only lowercase a-z. Web Username - Username to be shown in multiplayer lobbies. It must be 4–20 characters. + Username to be shown in multiplayer lobbies. It must be 4–20 characters, containing only alphanumeric characters, dashes, periods, underscores, and spaces. Network From 7cb8a1acd36b565327e1cc0b7e8775a84de3cba8 Mon Sep 17 00:00:00 2001 From: MaranBr Date: Fri, 18 Jul 2025 22:55:05 +0200 Subject: [PATCH 23/41] [host1x] Fix GPU decoding for VP8 video codec (#72) This fixes GPU decoding for the VP8 video codec on supported devices. Co-authored-by: MaranBr Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/72 Co-authored-by: MaranBr Co-committed-by: MaranBr --- src/video_core/host1x/ffmpeg/ffmpeg.cpp | 70 ++++++++++++++++--------- src/video_core/host1x/ffmpeg/ffmpeg.h | 1 - 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp index d6eff2bdd7..3170f53884 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp @@ -23,32 +23,48 @@ namespace FFmpeg { namespace { -constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12; -constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P; constexpr std::array PreferredGpuDecoders = { -#ifdef _WIN32 +#if defined (_WIN32) AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2, #elif defined(__FreeBSD__) AV_HWDEVICE_TYPE_VDPAU, #elif defined(__unix__) + AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_VAAPI, + AV_HWDEVICE_TYPE_VDPAU, #endif AV_HWDEVICE_TYPE_VULKAN, }; AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) { - for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { - if (*p == codec_context->pix_fmt) { - return codec_context->pix_fmt; - } - } + // Check if there is a pixel format supported by the GPU decoder. + const auto desc = av_pix_fmt_desc_get(codec_context->pix_fmt); + if (desc && desc->flags & AV_PIX_FMT_FLAG_HWACCEL) { + for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { + if (*p == codec_context->pix_fmt) { + return codec_context->pix_fmt; + } + } + } - LOG_INFO(HW_GPU, "Could not find compatible GPU AV format, falling back to CPU"); + // Another check to confirm if there is a pixel format supported by specific GPU decoders. + for (int i = 0;; i++) { + const AVCodecHWConfig* config = avcodec_get_hw_config(codec_context->codec, i); + if (!config) { + break; + } + + if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) && (config->device_type == AV_HWDEVICE_TYPE_CUDA || config->device_type == AV_HWDEVICE_TYPE_VAAPI)) { + return config->pix_fmt; + } + } + + // Fallback to CPU decoder. + LOG_INFO(HW_GPU, "Could not find compatible GPU pixel format, falling back to CPU"); av_buffer_unref(&codec_context->hw_device_ctx); - codec_context->pix_fmt = PreferredCpuFormat; return codec_context->pix_fmt; } @@ -58,7 +74,7 @@ std::string AVError(int errnum) { return errbuf; } -} // namespace +} Packet::Packet(std::span data) { m_packet = av_packet_alloc(); @@ -81,16 +97,16 @@ Frame::~Frame() { Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) { const AVCodecID av_codec = [&] { switch (codec) { - case Tegra::Host1x::NvdecCommon::VideoCodec::H264: - return AV_CODEC_ID_H264; - case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: - return AV_CODEC_ID_VP8; - case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: - return AV_CODEC_ID_VP9; - default: - UNIMPLEMENTED_MSG("Unknown codec {}", codec); - return AV_CODEC_ID_NONE; - } + case Tegra::Host1x::NvdecCommon::VideoCodec::H264: + return AV_CODEC_ID_H264; + case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: + return AV_CODEC_ID_VP8; + case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: + return AV_CODEC_ID_VP9; + default: + UNIMPLEMENTED_MSG("Unknown codec {}", codec); + return AV_CODEC_ID_NONE; + } }(); m_codec = avcodec_find_decoder(av_codec); @@ -103,6 +119,7 @@ bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceTyp LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name, av_hwdevice_get_type_name(type)); break; } + if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) { LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); *out_pix_fmt = config->pix_fmt; @@ -215,11 +232,11 @@ bool DecoderContext::OpenContext(const Decoder& decoder) { } bool DecoderContext::SendPacket(const Packet& packet) { - m_temp_frame = std::make_shared(); if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0 && ret != AVERROR_EOF) { LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret)); return false; } + return true; } @@ -237,14 +254,15 @@ std::shared_ptr DecoderContext::ReceiveFrame() { return {}; } - const auto desc = av_pix_fmt_desc_get(intermediate_frame->GetPixelFormat()); - if (m_codec_context->hw_device_ctx && (desc && desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { - m_temp_frame->SetFormat(PreferredGpuFormat); + m_temp_frame = std::make_shared(); + if (m_codec_context->hw_device_ctx) { + m_temp_frame->SetFormat(AV_PIX_FMT_NV12); if (int ret = av_hwframe_transfer_data(m_temp_frame->GetFrame(), intermediate_frame->GetFrame(), 0); ret < 0) { LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret)); return {}; } } else { + m_temp_frame->SetFormat(AV_PIX_FMT_YUV420P); m_temp_frame = std::move(intermediate_frame); } @@ -287,4 +305,4 @@ std::shared_ptr DecodeApi::ReceiveFrame() { return m_decoder_context->ReceiveFrame(); } -} // namespace FFmpeg +} diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.h b/src/video_core/host1x/ffmpeg/ffmpeg.h index 6200fa691d..0864fd0cbe 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.h +++ b/src/video_core/host1x/ffmpeg/ffmpeg.h @@ -26,7 +26,6 @@ extern "C" { #include #include -// Works quite fine, and omits the hacky ffmpeg building for now... #if defined(__FreeBSD__) #include #else From d42d37973305c1331e0e0d187f51adeb29b3d2ed Mon Sep 17 00:00:00 2001 From: crueter Date: Fri, 18 Jul 2025 17:48:26 -0400 Subject: [PATCH 24/41] [desktop] Capitalize app name Signed-off-by: crueter --- src/yuzu/about_dialog.cpp | 4 ++-- src/yuzu/main.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp index 6ef3072894..5b6e32149d 100644 --- a/src/yuzu/about_dialog.cpp +++ b/src/yuzu/about_dialog.cpp @@ -16,9 +16,9 @@ AboutDialog::AboutDialog(QWidget* parent) std::string yuzu_build; if (Common::g_is_dev_build) { - yuzu_build = fmt::format("eden Nightly | {}-{}", description, build_id); + yuzu_build = fmt::format("Eden Nightly | {}-{}", description, build_id); } else { - yuzu_build = fmt::format("eden | {}", description); + yuzu_build = fmt::format("Eden | {}", description); } const auto override_build = fmt::format(fmt::runtime( diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 6a575cfa87..8e3082a58e 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4902,9 +4902,9 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie std::string yuzu_title; if (Common::g_is_dev_build) { - yuzu_title = fmt::format("eden Nightly | {}-{}", description, build_id); + yuzu_title = fmt::format("Eden Nightly | {}-{}", description, build_id); } else { - yuzu_title = fmt::format("eden | {}", description); + yuzu_title = fmt::format("Eden | {}", description); } const auto override_title = From d125994270089057d35af471d13819aeb3649c97 Mon Sep 17 00:00:00 2001 From: crueter Date: Sat, 19 Jul 2025 04:43:11 +0200 Subject: [PATCH 25/41] [android] Fix 0fps errors on DKCR, Subnautica, and Ori 2 (#79) Co-authored-by: Pavel Barabanov Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/79 --- src/video_core/fence_manager.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index 2135f1f2da..a2411af63c 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -73,15 +76,27 @@ public: void SignalFence(std::function&& func) { bool delay_fence = Settings::IsGPULevelHigh(); + #ifdef __ANDROID__ + if (!delay_fence) { + TryReleasePendingFences(); + } + #else if constexpr (!can_async_check) { TryReleasePendingFences(); } + #endif const bool should_flush = ShouldFlush(); CommitAsyncFlushes(); TFence new_fence = CreateFence(!should_flush); + #ifdef __ANDROID__ + if (delay_fence) { + guard.lock(); + } + #else if constexpr (can_async_check) { guard.lock(); } + #endif if (delay_fence) { uncommitted_operations.emplace_back(std::move(func)); } @@ -94,10 +109,17 @@ public: if (should_flush) { rasterizer.FlushCommands(); } + #ifdef __ANDROID__ + if (delay_fence) { + guard.unlock(); + cv.notify_all(); + } + #else if constexpr (can_async_check) { guard.unlock(); cv.notify_all(); } + #endif rasterizer.InvalidateGPUCache(); } From 9dfe3cece0e00390a93f1833b8a44fdee8e86576 Mon Sep 17 00:00:00 2001 From: crueter Date: Sat, 19 Jul 2025 00:12:21 -0400 Subject: [PATCH 26/41] [desktop, core] yuzu -> Eden, eden -> Eden Signed-off-by: crueter --- src/audio_core/sink/cubeb_sink.cpp | 8 +++--- src/common/settings.cpp | 2 +- src/common/settings.h | 2 +- src/common/uuid.h | 6 ++-- .../frontend/applets/software_keyboard.cpp | 4 +-- src/core/hle/service/acc/acc.cpp | 2 +- src/core/hle/service/acc/profile_manager.cpp | 2 +- src/dedicated_room/yuzu_room.cpp | 6 ++-- src/input_common/drivers/sdl_driver.cpp | 2 +- .../nsight_aftermath_tracker.cpp | 2 +- .../vulkan_common/vulkan_wrapper.cpp | 4 +-- src/yuzu/aboutdialog.ui | 7 ++--- src/yuzu/bootmanager.cpp | 4 +-- src/yuzu/configuration/configure_debug.ui | 2 +- src/yuzu/configuration/configure_general.cpp | 2 +- .../configuration/configure_input_advanced.ui | 6 ++-- .../configuration/configure_motion_touch.cpp | 12 ++++---- src/yuzu/configuration/configure_tas.ui | 2 +- .../configure_touchscreen_advanced.ui | 2 +- src/yuzu/configuration/configure_web.ui | 2 +- src/yuzu/configuration/shared_translation.cpp | 4 +-- src/yuzu/main.cpp | 28 +++++++++---------- src/yuzu/main.ui | 2 +- src/yuzu/multiplayer/host_room.cpp | 4 +-- src/yuzu/multiplayer/lobby.cpp | 4 +-- src/yuzu/multiplayer/message.cpp | 4 +-- src/yuzu/uisettings.h | 2 +- src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | 4 +-- .../emu_window/emu_window_sdl2_gl.cpp | 4 +-- .../emu_window/emu_window_sdl2_null.cpp | 4 +-- .../emu_window/emu_window_sdl2_vk.cpp | 4 +-- src/yuzu_cmd/yuzu.cpp | 4 +-- 32 files changed, 72 insertions(+), 75 deletions(-) diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index 49efae8e3e..7f4b1c365a 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp @@ -208,7 +208,7 @@ CubebSink::CubebSink(std::string_view target_device_name) { com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); #endif - if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) { + if (cubeb_init(&ctx, "Eden", nullptr) != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); return; } @@ -304,7 +304,7 @@ std::vector ListCubebSinkDevices(bool capture) { auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); #endif - if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) { + if (cubeb_init(&ctx, "Eden Device Enumerator", nullptr) != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); return {}; } @@ -352,7 +352,7 @@ bool IsCubebSuitable() { #endif // Init cubeb - if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { + if (cubeb_init(&ctx, "Eden Latency Getter", nullptr) != CUBEB_OK) { LOG_ERROR(Audio_Sink, "Cubeb failed to init, it is not suitable."); return false; } @@ -386,7 +386,7 @@ bool IsCubebSuitable() { // Test opening a device with standard parameters cubeb_devid output_device{0}; cubeb_devid input_device{0}; - std::string name{"Yuzu test"}; + std::string name{"Eden test"}; cubeb_stream* stream{nullptr}; if (cubeb_stream_init(ctx, &stream, name.c_str(), input_device, nullptr, output_device, ¶ms, diff --git a/src/common/settings.cpp b/src/common/settings.cpp index f66c24455f..63d46722ea 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -118,7 +118,7 @@ void LogSettings() { LOG_INFO(Config, "{}: {}", name, Common::FS::PathToUTF8String(path)); }; - LOG_INFO(Config, "yuzu Configuration:"); + LOG_INFO(Config, "Eden Configuration:"); for (auto& [category, settings] : values.linkage.by_category) { for (const auto& setting : settings) { if (setting->Id() == values.eden_token.Id()) { diff --git a/src/common/settings.h b/src/common/settings.h index 551e66c57e..1bf460c8b0 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -564,7 +564,7 @@ struct Values { linkage, 0, "rng_seed", Category::System, Specialization::Hex, true, true, &rng_seed_enabled}; Setting device_name{ - linkage, "eden", "device_name", Category::System, Specialization::Default, true, true}; + linkage, "Eden", "device_name", Category::System, Specialization::Default, true, true}; Setting current_user{linkage, 0, "current_user", Category::System}; diff --git a/src/common/uuid.h b/src/common/uuid.h index 81bfefbbbe..60c5d75f12 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h @@ -76,13 +76,13 @@ struct UUID { u128 AsU128() const; /** - * Creates a default UUID "yuzu Default UID". + * Creates a default UUID "Eden Default UID". * - * @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID". + * @returns A UUID with its bytes set to the ASCII values of "Eden Default UID". */ static constexpr UUID MakeDefault() { return UUID{ - {'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'}, + {'E', 'd', 'e', 'n', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'}, }; } diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index d00da8ac9e..bd0ec64f47 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -65,7 +65,7 @@ void DefaultSoftwareKeyboardApplet::ShowNormalKeyboard() const { LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to show the normal software keyboard."); - SubmitNormalText(u"yuzu"); + SubmitNormalText(u"Eden"); } void DefaultSoftwareKeyboardApplet::ShowTextCheckDialog( @@ -100,7 +100,7 @@ void DefaultSoftwareKeyboardApplet::ShowInlineKeyboard( appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button, appear_parameters.enable_return_button, appear_parameters.disable_cancel_button); - std::thread([this] { SubmitInlineText(u"yuzu"); }).detach(); + std::thread([this] { SubmitInlineText(u"Eden"); }).detach(); } void DefaultSoftwareKeyboardApplet::HideInlineKeyboard() const { diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 4562217d5e..51a542e5e6 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -969,7 +969,7 @@ void Module::Interface::InitializeApplicationInfoV2(HLERequestContext& ctx) { void Module::Interface::BeginUserRegistration(HLERequestContext& ctx) { const auto user_id = Common::UUID::MakeRandom(); - profile_manager->CreateNewUser(user_id, "yuzu"); + profile_manager->CreateNewUser(user_id, "Eden"); LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString()); diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 80a2b0b72b..b4cc35b5c0 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -49,7 +49,7 @@ ProfileManager::ProfileManager() { // Create an user if none are present if (user_count == 0) { - CreateNewUser(UUID::MakeRandom(), "eden"); + CreateNewUser(UUID::MakeRandom(), "Eden"); WriteUserSaveFile(); } diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp index 156bff62dc..880431fa85 100644 --- a/src/dedicated_room/yuzu_room.cpp +++ b/src/dedicated_room/yuzu_room.cpp @@ -68,14 +68,13 @@ static void PrintHelp(const char* argv0) { "-a, --web-api-url yuzu Web API url\n" "-b, --ban-list-file The file for storing the room ban list\n" "-l, --log-file The file for storing the room log\n" - "-e, --enable-mods Allow Community Moderators to moderate on your room\n" "-h, --help Display this help and exit\n" "-v, --version Output version information and exit\n", argv0); } static void PrintVersion() { - LOG_INFO(Network, "yuzu dedicated room {} {} Libnetwork: {}", Common::g_scm_branch, + LOG_INFO(Network, "Eden dedicated room {} {} Libnetwork: {}", Common::g_scm_branch, Common::g_scm_desc, Network::network_version); } @@ -229,7 +228,6 @@ void LaunchRoom(int argc, char** argv, bool called_by_option) {"web-api-url", required_argument, 0, 'a'}, {"ban-list-file", required_argument, 0, 'b'}, {"log-file", required_argument, 0, 'l'}, - {"enable-mods", no_argument, 0, 'e'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'}, // Entry option @@ -369,7 +367,7 @@ void LaunchRoom(int argc, char** argv, bool called_by_option) std::make_unique(Settings::values.web_api_url.GetValue()); #else LOG_INFO(Network, - "yuzu Web Services is not available with this build: validation is disabled."); + "Eden Web Services is not available with this build: validation is disabled."); verify_backend = std::make_unique(); #endif } else { diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 78f458afe3..ef898527ec 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -489,7 +489,7 @@ void SDLDriver::CloseJoysticks() { SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) { // Set our application name. Currently passed to DBus by SDL and visible to the user through // their desktop environment. - SDL_SetHint(SDL_HINT_APP_NAME, "yuzu"); + SDL_SetHint(SDL_HINT_APP_NAME, "Eden"); if (!Settings::values.enable_raw_input) { // Disable raw input. When enabled this setting causes SDL to die when a web applet opens diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp index a185f0a52b..9d57e3635f 100644 --- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp @@ -186,7 +186,7 @@ void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_ void NsightAftermathTracker::OnCrashDumpDescriptionCallback( PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description) { - add_description(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "yuzu"); + add_description(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "Eden"); } void NsightAftermathTracker::GpuCrashDumpCallback(const void* gpu_crash_dump, diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 0caf415212..c703abe40e 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -437,9 +437,9 @@ Instance Instance::Create(u32 version, Span layers, Span - <html><head/><body><p><span style=" font-size:28pt;">eden</span></p></body></html> + <html><head/><body><p><span style=" font-size:28pt;">Eden</span></p></body></html> @@ -101,8 +101,8 @@ p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } -</style></head><body style=" font-family:'Adwaita Sans'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">eden is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+ which is based on the yuzu emulator which ended development back in March 2024. <br /><br />This software should not be used to play games you have not legally obtained.</span></p></body></html> +</style></head><body style=" font-family:'Noto Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:12pt;">Eden is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+ which is based on the yuzu emulator which ended development back in March 2024. <br /><br />This software should not be used to play games you have not legally obtained.</span></p></body></html> Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter @@ -165,7 +165,6 @@ li.checked::marker { content: "\2612"; } - diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index d14c46af86..6852193b1b 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -286,7 +286,7 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, Core::System& system_) : QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)}, system{system_} { - setWindowTitle(QStringLiteral("eden %1 | %2-%3") + setWindowTitle(QStringLiteral("Eden %1 | %2-%3") .arg(QString::fromUtf8(Common::g_build_name), QString::fromUtf8(Common::g_scm_branch), QString::fromUtf8(Common::g_scm_desc))); @@ -1034,7 +1034,7 @@ bool GRenderWindow::InitializeOpenGL() { return true; #else QMessageBox::warning(this, tr("OpenGL not available!"), - tr("eden has not been compiled with OpenGL support.")); + tr("Eden has not been compiled with OpenGL support.")); return false; #endif } diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 5408d485b4..bd7125712c 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -560,7 +560,7 @@ - **This will be reset automatically when eden closes. + **This will be reset automatically when Eden closes. 20 diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 30e5f08d67..918fa15d4d 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -96,7 +96,7 @@ void ConfigureGeneral::SetResetCallback(std::function callback) { void ConfigureGeneral::ResetDefaults() { QMessageBox::StandardButton answer = QMessageBox::question( - this, tr("eden"), + this, tr("Eden"), tr("This reset all settings and remove all per-game configurations. This will not delete " "game directories, profiles, or input profiles. Proceed?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index 2bdd137b08..f489d93965 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui @@ -2656,7 +2656,7 @@ - Requires restarting eden + Requires restarting Eden @@ -2698,7 +2698,7 @@ - Requires restarting eden + Requires restarting Eden @@ -2714,7 +2714,7 @@ - Requires restarting eden + Requires restarting Eden diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index 7a1cee463b..e6d7d58299 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -170,27 +170,27 @@ void ConfigureMotionTouch::OnUDPAddServer() { const int row = udp_server_list_model->rowCount(); if (!ok) { - QMessageBox::warning(this, tr("eden"), tr("Port number has invalid characters")); + QMessageBox::warning(this, tr("Eden"), tr("Port number has invalid characters")); return; } if (port_number < 0 || port_number > 65353) { - QMessageBox::warning(this, tr("eden"), tr("Port has to be in range 0 and 65353")); + QMessageBox::warning(this, tr("Eden"), tr("Port has to be in range 0 and 65353")); return; } if (!re.match(server_text).hasMatch()) { - QMessageBox::warning(this, tr("eden"), tr("IP address is not valid")); + QMessageBox::warning(this, tr("Eden"), tr("IP address is not valid")); return; } // Search for duplicates for (const auto& item : udp_server_list_model->stringList()) { if (item == server_string) { - QMessageBox::warning(this, tr("eden"), tr("This UDP server already exists")); + QMessageBox::warning(this, tr("Eden"), tr("This UDP server already exists")); return; } } // Limit server count to 8 if (row == 8) { - QMessageBox::warning(this, tr("eden"), tr("Unable to add more than 8 servers")); + QMessageBox::warning(this, tr("Eden"), tr("Unable to add more than 8 servers")); return; } @@ -284,7 +284,7 @@ void ConfigureMotionTouch::OnConfigureTouchFromButton() { bool ConfigureMotionTouch::CanCloseDialog() { if (udp_test_in_progress) { - QMessageBox::warning(this, tr("eden"), + QMessageBox::warning(this, tr("Eden"), tr("UDP Test or calibration configuration is in progress.
Please " "wait for them to finish.")); return false; diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui index d24f54b77b..b0d0a39d8b 100644 --- a/src/yuzu/configuration/configure_tas.ui +++ b/src/yuzu/configuration/configure_tas.ui @@ -14,7 +14,7 @@ - <html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the <a href="https://eden-emulator.github.io/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">help page</span></a> on the eden website.</p></body></html> + <html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the <a href="https://eden-emulator.github.io/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">help page</span></a> on the Eden website.</p></body></html> true diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui index 6dce2e0097..afedfdc70d 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.ui +++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui @@ -23,7 +23,7 @@
- Warning: The settings in this page affect the inner workings of eden's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing. + Warning: The settings in this page affect the inner workings of Eden's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing. true diff --git a/src/yuzu/configuration/configure_web.ui b/src/yuzu/configuration/configure_web.ui index d440fd0409..964c6a245f 100644 --- a/src/yuzu/configuration/configure_web.ui +++ b/src/yuzu/configuration/configure_web.ui @@ -22,7 +22,7 @@ - eden Web Service + Eden Web Service diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index c743e65ae3..770a16a481 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -420,12 +420,12 @@ std::unique_ptr InitializeTranslations(QWidget* parent) INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), - tr("Ask to select a user profile on each boot, useful if multiple people use eden on " + tr("Ask to select a user profile on each boot, useful if multiple people use Eden on " "the same PC.")); INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"), - tr("This setting pauses eden when focusing other windows.")); + tr("This setting pauses Eden when focusing other windows.")); INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"), diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 8e3082a58e..6b2608375c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -390,13 +390,13 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) const auto description = std::string(Common::g_scm_desc); const auto build_id = std::string(Common::g_build_id); - const auto yuzu_build = fmt::format("eden Development Build | {}-{}", branch_name, description); + const auto yuzu_build = fmt::format("Eden Development Build | {}-{}", branch_name, description); const auto override_build = fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id); const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; const auto processor_count = std::thread::hardware_concurrency(); - LOG_INFO(Frontend, "eden Version: {}", yuzu_build_version); + LOG_INFO(Frontend, "Eden Version: {}", yuzu_build_version); LogRuntimes(); #ifdef ARCHITECTURE_x86_64 const auto& caps = Common::GetCPUCaps(); @@ -1410,7 +1410,7 @@ void GMainWindow::InitializeHotkeys() { LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File")); LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo")); - LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit eden")); + LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit Eden")); LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation")); LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation")); LinkActionShortcut(ui->action_Stop, QStringLiteral("Stop Emulation")); @@ -1969,7 +1969,7 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa tr("You are using the deconstructed ROM directory format for this game, which is an " "outdated format that has been superseded by others such as NCA, NAX, XCI, or " "NSP. Deconstructed ROM directories lack icons, metadata, and update " - "support.

For an explanation of the various Switch formats eden supports,

For an explanation of the various Switch formats Eden supports,
check out our " "wiki. This message will not be shown again.")); } @@ -1984,7 +1984,7 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa case Core::SystemResultStatus::ErrorVideoCore: QMessageBox::critical( this, tr("An error occurred initializing the video core."), - tr("eden has encountered an error while running the video core. " + tr("Eden has encountered an error while running the video core. " "This is usually caused by outdated GPU drivers, including integrated ones. " "Please see the log for more details. " "For more information on accessing the log, please see the following page: " @@ -2076,7 +2076,7 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletParameters params, StartGameType type) { - LOG_INFO(Frontend, "eden starting..."); + LOG_INFO(Frontend, "Eden starting..."); if (params.program_id == 0 || params.program_id > static_cast(Service::AM::AppletProgramId::MaxProgramId)) { @@ -3621,8 +3621,8 @@ void GMainWindow::OnMenuReportCompatibility() { } else { QMessageBox::critical( this, tr("Missing yuzu Account"), - tr("In order to submit a game compatibility test case, you must link your eden " - "account.

To link your eden account, go to Emulation > Configuration " + tr("In order to submit a game compatibility test case, you must set up your web token and " + "username.

To link your eden account, go to Emulation > Configuration " "> " "Web.")); } @@ -3650,7 +3650,7 @@ void GMainWindow::OnOpenQuickstartGuide() { } void GMainWindow::OnOpenFAQ() { - OpenURL(QUrl(QStringLiteral("https://eden-emulator.github.io/"))); + OpenURL(QUrl(QStringLiteral("https://eden-emu.dev"))); } void GMainWindow::OnOpenDiscord() @@ -5260,8 +5260,8 @@ bool GMainWindow::ConfirmClose() { UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) { return true; } - const auto text = tr("Are you sure you want to close eden?"); - return question(this, tr("eden"), text); + const auto text = tr("Are you sure you want to close Eden?"); + return question(this, tr("Eden"), text); } void GMainWindow::closeEvent(QCloseEvent* event) { @@ -5341,7 +5341,7 @@ bool GMainWindow::ConfirmChangeGame() { // Use custom question to link controller navigation return question( - this, tr("eden"), + this, tr("Eden"), tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); } @@ -5350,10 +5350,10 @@ bool GMainWindow::ConfirmForceLockedExit() { if (emu_thread == nullptr) { return true; } - const auto text = tr("The currently running application has requested eden to not exit.\n\n" + const auto text = tr("The currently running application has requested Eden to not exit.\n\n" "Would you like to bypass this and exit anyway?"); - return question(this, tr("eden"), text); + return question(this, tr("Eden"), text); } void GMainWindow::RequestGameExit() { diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 970f8d2901..bcc596cec2 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -269,7 +269,7 @@
- &About eden + &About Eden diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp index 6471ad1632..d8e63da600 100644 --- a/src/yuzu/multiplayer/host_room.cpp +++ b/src/yuzu/multiplayer/host_room.cpp @@ -60,7 +60,7 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, ui->username->setText( QString::fromStdString(UISettings::values.multiplayer_room_nickname.GetValue())); if (ui->username->text().isEmpty() && !Settings::values.eden_username.GetValue().empty()) { - // Use eden Web Service user name as nickname by default + // Use Eden Web Service user name as nickname by default ui->username->setText(QString::fromStdString(Settings::values.eden_username.GetValue())); } ui->room_name->setText( @@ -186,7 +186,7 @@ void HostRoomWindow::Host() { QMessageBox::warning( this, tr("Error"), tr("Failed to announce the room to the public lobby. In order to host a " - "room publicly, you must have a valid eden account configured in " + "room publicly, you must have a valid Eden account configured in " "Emulation -> Configure -> Web. If you do not want to publish a room in " "the public lobby, then select Unlisted instead.\nDebug Message: ") + QString::fromStdString(result.result_string), diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp index 93ff56a652..ed6ba6a15c 100644 --- a/src/yuzu/multiplayer/lobby.cpp +++ b/src/yuzu/multiplayer/lobby.cpp @@ -66,14 +66,14 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, QString::fromStdString(UISettings::values.multiplayer_nickname.GetValue())); // Try find the best nickname by default - if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("eden")) { + if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("Eden")) { if (!Settings::values.eden_username.GetValue().empty()) { ui->nickname->setText( QString::fromStdString(Settings::values.eden_username.GetValue())); } else if (!GetProfileUsername().empty()) { ui->nickname->setText(QString::fromStdString(GetProfileUsername())); } else { - ui->nickname->setText(QStringLiteral("eden")); + ui->nickname->setText(QStringLiteral("Eden")); } } diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp index ede444a1da..10d62bcf3d 100644 --- a/src/yuzu/multiplayer/message.cpp +++ b/src/yuzu/multiplayer/message.cpp @@ -29,12 +29,12 @@ const ConnectionError ErrorManager::UNABLE_TO_CONNECT( const ConnectionError ErrorManager::ROOM_IS_FULL( QT_TR_NOOP("Unable to connect to the room because it is already full.")); const ConnectionError ErrorManager::COULD_NOT_CREATE_ROOM( - QT_TR_NOOP("Creating a room failed. Please retry. Restarting eden might be necessary.")); + QT_TR_NOOP("Creating a room failed. Please retry. Restarting Eden might be necessary.")); const ConnectionError ErrorManager::HOST_BANNED( QT_TR_NOOP("The host of the room has banned you. Speak with the host to unban you " "or try a different room.")); const ConnectionError ErrorManager::WRONG_VERSION( - QT_TR_NOOP("Version mismatch! Please update to the latest version of eden. If the problem " + QT_TR_NOOP("Version mismatch! Please update to the latest version of Eden. If the problem " "persists, contact the room host and ask them to update the server.")); const ConnectionError ErrorManager::WRONG_PASSWORD(QT_TR_NOOP("Incorrect password.")); const ConnectionError ErrorManager::GENERIC_ERROR(QT_TR_NOOP( diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 1ec2f51f15..b713b52fcc 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -242,7 +242,7 @@ const std::array default_hotkeys{{ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Configure Current Game")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+."), std::string(""), Qt::WidgetWithChildrenShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"), std::string("Home+Plus"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"), std::string(""), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit eden")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Eden")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 3974e4b357..41697a22e8 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -223,7 +223,7 @@ void EmuWindow_SDL2::WaitEvent() { const u32 current_time = SDL_GetTicks(); if (current_time > last_time + 2000) { const auto results = system.GetAndResetPerfStats(); - const auto title = fmt::format("eden {} | {}-{} | FPS: {:.0f} ({:.0f}%)", + const auto title = fmt::format("Eden {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc, @@ -238,7 +238,7 @@ void EmuWindow_SDL2::WaitEvent() { void EmuWindow_SDL2::SetWindowIcon() { SDL_RWops* const yuzu_icon_stream = SDL_RWFromConstMem((void*)yuzu_icon, yuzu_icon_size); if (yuzu_icon_stream == nullptr) { - LOG_WARNING(Frontend, "Failed to create eden icon stream."); + LOG_WARNING(Frontend, "Failed to create Eden icon stream."); return; } SDL_Surface* const window_icon = SDL_LoadBMP_RW(yuzu_icon_stream, 1); diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index 2e90be35ae..4b012fe134 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -90,7 +90,7 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste } SDL_GL_SetSwapInterval(0); - std::string window_title = fmt::format("eden {} | {}-{}", Common::g_build_fullname, + std::string window_title = fmt::format("Eden {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc); render_window = SDL_CreateWindow(window_title.c_str(), @@ -138,7 +138,7 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste OnResize(); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); SDL_PumpEvents(); - LOG_INFO(Frontend, "eden Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, + LOG_INFO(Frontend, "Eden Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc); Settings::LogSettings(); } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp index 043cdbf68d..506137bd8a 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp @@ -23,7 +23,7 @@ EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_, bool fullscreen) : EmuWindow_SDL2{input_subsystem_, system_} { - const std::string window_title = fmt::format("eden {} | {}-{} (Vulkan)", Common::g_build_name, + const std::string window_title = fmt::format("Eden {} | {}-{} (Vulkan)", Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc); render_window = SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, @@ -40,7 +40,7 @@ EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subs OnResize(); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); SDL_PumpEvents(); - LOG_INFO(Frontend, "eden Version: {} | {}-{} (Null)", Common::g_build_name, + LOG_INFO(Frontend, "Eden Version: {} | {}-{} (Null)", Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc); } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index f4980932ed..f509652bf6 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -18,7 +18,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_, bool fullscreen) : EmuWindow_SDL2{input_subsystem_, system_} { - const std::string window_title = fmt::format("eden {} | {}-{} (Vulkan)", + const std::string window_title = fmt::format("Eden {} | {}-{} (Vulkan)", Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc); @@ -84,7 +84,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste OnResize(); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); SDL_PumpEvents(); - LOG_INFO(Frontend, "eden Version: {} | {}-{} (Vulkan)", Common::g_build_name, + LOG_INFO(Frontend, "Eden Version: {} | {}-{} (Vulkan)", Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc); } diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index c1d714be77..be30c2931f 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -88,7 +88,7 @@ static void PrintHelp(const char* argv0) { } static void PrintVersion() { - std::cout << "eden " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; + std::cout << "Eden " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; } static void OnStateChanged(const Network::RoomMember::State& state) { @@ -408,7 +408,7 @@ int main(int argc, char** argv) { const u16 error_id = static_cast(load_result) - loader_id; LOG_CRITICAL(Frontend, "While attempting to load the ROM requested, an error occurred. Please " - "refer to the eden wiki for more information or the eden discord for " + "refer to the Eden wiki for more information or the Eden discord for " "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", loader_id, error_id, From ce56a4b83c42f6883038da9eb415482b6c7fe6f6 Mon Sep 17 00:00:00 2001 From: Pavel Barabanov Date: Sun, 20 Jul 2025 19:17:04 +0300 Subject: [PATCH 27/41] updating translations --- .../app/src/main/res/values-ar/strings.xml | 37 ++- .../app/src/main/res/values-ckb/strings.xml | 34 ++- .../app/src/main/res/values-cs/strings.xml | 117 ++++++++-- .../app/src/main/res/values-de/strings.xml | 34 ++- .../app/src/main/res/values-es/strings.xml | 221 +++++++++--------- .../app/src/main/res/values-fa/strings.xml | 35 ++- .../app/src/main/res/values-fr/strings.xml | 37 ++- .../app/src/main/res/values-he/strings.xml | 35 ++- .../app/src/main/res/values-hu/strings.xml | 34 ++- .../app/src/main/res/values-id/strings.xml | 37 ++- .../app/src/main/res/values-it/strings.xml | 35 ++- .../app/src/main/res/values-ja/strings.xml | 34 ++- .../app/src/main/res/values-ko/strings.xml | 37 ++- .../app/src/main/res/values-nb/strings.xml | 34 ++- .../app/src/main/res/values-pl/strings.xml | 34 ++- .../src/main/res/values-pt-rBR/strings.xml | 35 ++- .../src/main/res/values-pt-rPT/strings.xml | 35 ++- .../app/src/main/res/values-ru/strings.xml | 87 ++++--- .../app/src/main/res/values-sr/strings.xml | 33 ++- .../app/src/main/res/values-uk/strings.xml | 32 ++- .../app/src/main/res/values-vi/strings.xml | 36 ++- .../src/main/res/values-zh-rCN/strings.xml | 36 ++- .../src/main/res/values-zh-rTW/strings.xml | 35 ++- .../app/src/main/res/values/strings.xml | 8 +- 24 files changed, 751 insertions(+), 381 deletions(-) diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index e100bafe7e..99bb92666a 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -61,24 +61,25 @@ إظهار إصدار البرنامج الثابت عرض إصدار البرنامج الثابت المثبت - + امتدادات GPU العارض RAII طريقة لإدارة الموارد تلقائيًا في فولكان تضمن الإفراج الصحيح عن الموارد عندما لا تكون هناك حاجة إليها، ولكن قد تسبب تعطل الألعاب المجمعة. - متنوع + وحدة المعالجة المركزية والذاكرة حجاب عدن إعدادات تجريبية لتحسين الأداء والقدرة. قد تسبب هذه الإعدادات شاشات سوداء أو مشاكل أخرى في اللعبة. إعدادات تجريبية - الإعدادات الموجودة في إيدن فيل تجريبية للغاية وقد تسبب مشاكل. إذا لم يتم تشغيل لعبتك، قم بتعطيل أي ملحقات وقم بتغيير الحالة الديناميكية الممتدة إلى 0. + الإعدادات الموجودة في حجاب عدن تجريبية جداً وقد تسبب مشاكل. إذا لم تعمل اللعبة، قم بتعطيل جميع الامتدادات. قيد التطوير: تخطي الإطارات تبديل تخطي الإطارات لتحسين الأداء عن طريق تقليل عدد الإطارات المعروضة. هذه الميزة قيد التطوير وسيتم تمكينها في الإصدارات المستقبلية. تحسين توقيت الإطارات يضمن تسليمًا سلسًا ومتناسقًا للإطارات من خلال مزامنة التوقيت بينها، مما يقلل من التقطيع وعدم انتظام الحركة. مثالي للألعاب التي تعاني من عدم استقرار في توقيت الإطارات أو تقطع دقيق أثناء اللعب. تمكين ذاكرة التخزين المؤقت LRU - تمكين أو تعطيل ذاكرة التخزين المؤقت الأقل استخدامًا (LRU)، مما يزيد الأداء عن طريق توفير استخدام وحدة المعالجة المركزية. بعض الألعاب بها مشاكل معها، خاصة TotK 1.2.1، لذا قم بتعطيلها إذا لم يتم تشغيل اللعبة أو تعطلت بشكل عشوائي. + تمكين أو تعطيل ذاكرة التخزين المؤقت الأقل استخداماً مؤخراً (LRU) لتحسين الأداء عن طريق تقليل استخدام وحدة المعالجة المركزية. بعض الألعاب قد تواجه مشاكل معه، خاصةً TotK 1.2.1، لذا قم بتعطيله إذا لم تعمل اللعبة أو انهارت عشوائياً. الحالة الديناميكية الممتدة - يُمكّن ميزات فولكان لتحسين الأداء وعرض الرسومات وتوفير الموارد عند إنشاء خطوط المعالجة مع الحفاظ على استهلاك منخفض لوحدة المعالجة المركزية/وحدة معالجة الرسومات. قد تزيد هذه الامتدادات من درجة حرارة الجهاز، وقد لا تعمل وحدات معالجة الرسومات من سلسلة A6XX القديمة بشكل صحيح. اضبط على 0 لاستخدام الأشكال المحاكاة التقليدية. + تمكين ميزات فولكان لتحسين الأداء، العرض، وتوفير الموارد أثناء إنشاء خطوط المعالجة مع الحفاظ على استهلاك منخفض لوحدة المعالجة المركزية/وحدة معالجة الرسومات. هذه الامتدادات قد تزيد من حرارة الجهاز، ووحدات معالجة الرسومات من سلسلة A6XX القديمة قد لا تعمل بشكل صحيح. قم بتعطيله لمحاكاة الصيغ المضبوطة. + معطل مزامنة سرعة النواة مزامنة سرعة النواة مع النسبة القصوى للسرعة لتحسين الأداء دون تغيير السرعة الفعلية للعبة. استخدام الستاب التلقائي @@ -186,18 +187,18 @@ فشل في الانضمام إلى الغرفة الاسم قصير جدًا عنوان غير صالح - منفذ غير صالح! + منفذ غير صالح مغادرة الغرفة خطأ في الشبكة فقدان الاتصال - تعارض في الاسم + اسم المستخدم محجوز مسبقاً تعارض في عنوان MAC تعارض في معرف الجهاز إصدار خاطئ كلمة مرور خاطئة تعذر الاتصال الغرفة ممتلئة - المضيف محظور + أنت محظور من هذه الغرفة تم رفض الإذن لا يوجد مثل هذا المستخدم موجود بالفعل في الغرفة @@ -257,7 +258,8 @@ يجب أن يكون بين 3 و20 حرفًا مطلوب تنسيق IP غير صالح - يجب أن يكون بين 4-20 حرفًا + يجب أن يحتوي على 4-20 حرفاً (أحرف وأرقام ونقاط وواصلات وشرطات سفلية ومسافات فقط) + اسم مستخدم غير صالح، تأكد من إعداده بشكل صحيح في النظام → الشبكة يجب أن يكون بين 1 و65535 يجب أن يتكون من 48 حرفًا، وأحرف صغيرة من a-z فقط نوع الغرفة @@ -416,6 +418,15 @@ فحص المحتوى المثبت بحثًا عن تلف مفاتيح التشفير مفقودة لا يمكن فك تشفير البرنامج الثابت والألعاب + برنامج غير صالح + البرنامج الثابت مطلوب لتشغيل بعض الألعاب واستخدام التطبيقات. يعمل إيدن فقط مع البرنامج الثابت 19.0.1 أو الأقدم. + البرنامج الثابت موجود ولكن لا يمكن قراءته. تحقق من مفاتيح التشفير وأعد نسخ البرنامج الثابت إذا لزم الأمر. + البرنامج الثابت جديد جداً أو لا يمكن قراءته. يعمل إيدن فقط مع البرنامج الثابت 19.0.1 أو الأقدم. + فشل تثبيت المفاتيح + تم تثبيت المفاتيح بنجاح + فشل نسخ مفتاح واحد أو أكثر. + تأكد أن ملف المفاتيح يحمل امتداد .keys وحاول مرة أخرى. + فشل تهيئة المفاتيح. تحقق من أن أدوات النسخ محدثة وأعد نسخ المفاتيح. Qlaunch @@ -479,7 +490,7 @@ توليد اسم مستخدم الويب - اسم المستخدم الذي سيظهر في غرف اللعب الجماعي. يجب أن يكون بين 4-20 حرفًا. + اسم المستخدم الذي سيظهر في غرف اللعب الجماعي. يجب أن يحتوي على 4-20 حرفاً (أحرف وأرقام ونقاط وواصلات وشرطات سفلية ومسافات فقط). رمز الويب رمز الويب المستخدم لإنشاء غرف عامة. وهو سلسلة من 48 حرفًا تحتوي فقط على أحرف صغيرة من a-z. الشبكة @@ -646,11 +657,17 @@ جاري التحقق من المحتوى إشعار تثبيت المحتوى المحتوى الذي حددته لا يتطابق مع هذه اللعبة.هل تريد التثبيت على أية حال؟ + الخاص بك ROM تم تشفير + ألعاب البطاقات أو العناوين الرقمية.]]> + prod.keys]]> حدث خطأ أثناء تهيئة مركز الفيديو + قد يكون بسبب تعريف GPU غير متوافق ROM غير قادر على تحميل غير موجود ROM ملف + اللعبة تتطلب برنامجاً ثابتاً + نسخ وتثبيت البرنامج الثابت، أو اضغط "موافق" للمتابعة على أي حال.]]> الخروج من المحاكاة diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml index d13f24d173..03d834d71f 100644 --- a/src/android/app/src/main/res/values-ckb/strings.xml +++ b/src/android/app/src/main/res/values-ckb/strings.xml @@ -62,24 +62,25 @@ نیشاندانی وەشانی فریموێر وەشانی فریموێری نەصبکراو پیشان بدە - + پاشکۆکانی GPU رێندرەر RAII ڕێگایەکی بەڕێوەبردنی سەرچاوەکان بە خۆکار لە ڤولکان کە دڵنیای دەکاتەوە لە ئازادکردنی گونجاوی سەرچاوەکان کاتێک کە چیتر پێویستیان نییە، بەڵام لەوانەیە ببێتە هۆی کەوتنی یارییە کۆکراوەکان. - جۆراوجۆر + CPU و بیرگە حجاب عدن ڕێکخستنە تاقیکارییەکان بۆ باشترکردنی کارایی و توانا. ئەم ڕێکخستنانە لەوانەیە ببێتە هۆی شاشە ڕەشەکان یان کێشەیتری یاری. ڕێکخستنە تاقیکارییەکان - ڕێکخستنەکانی ناو ئیدین ڤێڵ زۆر تاقیکارین و لەوانەیە کێشە دروست بکەن. ئەگەر یاریەکەت نەگەشتە ناوەوە، هەموو زیادکراوەکان ناچالاک بکە و گۆڕینی دۆخی داینامیکی درێژکراو بۆ 0. + ڕێکخستنەکانی ناو پردی عەدن زۆر تاقیکارییە و ڕەنگە کێشە دروست بکات. ئەگەر یارییەکەت کارینەکات، هەموو زیادکراوەکان ناچالاک بکە. قيد التطوير: تخطي الإطارات تێپەڕاندنی فرەیمەکان بکە بۆ باشترکردنی کارایی بە کەمکردنەوەی ژمارەی فرەیمە ڕێندرکراوەکان. ئەم تایبەتمەندییە هێشتا کاردەکرێت و لە وەشانە داهاتووەکاندا چالاکدەکرێت. تحسين توقيت الإطارات يضمن تسليمًا سلسًا ومتناسقًا للإطارات من خلال مزامنة التوقيت بينها، مما يقلل من التقطيع وعدم انتظام الحركة. مثالي للألعاب التي تعاني من عدم استقرار في توقيت الإطارات أو تقطع دقيق أثناء اللعب. تمكين ذاكرة التخزين المؤقت LRU - چالاک یان ناچالاککردنی کاشەی LRU (کەمترین بەکارهێنراوی دوایی)، کارایی زیاد دەکات بە هەڵگرتنی بەکارهێنانی CPU. هەندێک یاری کێشەییان هەیە لەگەڵیدا، بەتایبەتی TotK 1.2.1، بۆیە ئەگەر یاریەکە نەکەوتەوە یان بە هەڕەمەکی داخرا، ناچالاکی بکە. + چالاک یان ناچالاککردنی کاشەی LRU، کارایی باشتر دەکات بە هەڵگرتنی بەکارهێنانی پرۆسەی CPU. هەندێک یاری کێشەی لەگەڵ هەیە، بەتایبەتی TotK 1.2.1، بۆیە بیخەوێنە ئەگەر یاریەکە نەگەڕێت یان بە هەڕەمەکی بشکێت. الحالة الديناميكية الممتدة - کاریگەرییەکانی ڤولکان چالاک دەکات بۆ باشترکردنی کارایی، رێندرکردن، و هەڵگرتنی سەرچاوەکان لە دروستکردنی پایپلاین بە بەکارهێنانی کەمی CPU/GPU. ئەم فراوانکراوانە لەوانەیە پلەی گەرمی ئامێر زیاد بکەن، و کارتە گرافیکییە کۆنەکانی زنجیرەی A6XX لەوانەیە بە شێوەیەکی گونجاو کار نەکەن. بۆ بەکارهێنانی شێوازە کۆنەکانی دروستکراو، بڕەکە بۆ ٠ دابنێ. + کاریگەرییەکانی ڤولکان چالاک بکە بۆ باشترکردنی کارایی، رێندرکردن، و هەڵگرتنی سەرچاوەکان لە دروستکردنی پایپلاین بە هەمان کات کەمترین بەکارهێنانی CPU/GPU بەکاربهێنرێت. ئەم زیادکراوانە ڕەنگە پلەی گەرمی ئامێر زیاد بکەن، و GPU-کانی هێڵی کۆنی A6XX ڕەنگە بە شێوەیەکی گونجاو کارنەکەن. بیخەوێنە بۆ نەخشەکێشانی فۆرماتە پێوانەکراوەکان. + ناچالاک مزامنة سرعة النواة خێرایی تیکەکانی ناوک ڕێکبخە لەگەڵ ڕێژەی خێرایی بەرزترین بۆ باشترکردنی کارایی بەبێ گۆڕینی خێرایی ڕاستەقینەی یارییەکە. استخدام الستاب التلقائي @@ -184,18 +185,18 @@ نەتوانی بچیتە ناو ژوورەوە ناو زۆر کورتە ناونیشانی نادروست - پۆرتی نادروست! + پۆرتی نادروست دەرچوون لە ژوور هەڵەی تۆڕ پەیوەندی لەدەستچوو - ناو دووبارەکراوەتەوە + ناوی بەکارهێنەر پێشتر هەیە ناونیشانی MAC دووبارەکراوەتەوە ناسنامەی کۆنسۆڵ دووبارەکراوەتەوە وەشانی هەڵە تێپەڕەوشەی هەڵە نەتوانی پەیوەندی بکات ژوور پڕە - خاوەن ژوور قەدەغەکراوە + تۆ لەم ژوورە قەدەغە کراوە ڕێگەپێنەدراوە ئەم بەکارهێنەرە بوونی نییە خۆت لە ژوور دەیت @@ -255,7 +256,8 @@ دەبێت لە نێوان 3 بۆ 20 پیت بێت پێویستە فۆرماتی IP نادروستە - دەبێت لە نێوان 4-20 پیتدا بێت + پێویستە ٤-٢٠ پیت بێت (تەنها پیت و ژمارە و خاڵ و خط و خەتەکێشە و بۆشایی) + ناوی بەکارهێنەری نادروست، دڵنیا بەرەوە کە بە شێوەیەکی دروست دانراوە لە سیستەم → تۆڕ دەبێت لە نێوان 1 بۆ 65535 بێت دەبێت 48 پیت بێت، و تەنها پیتی بچووکی a-z جۆری لۆبی @@ -410,6 +412,15 @@ هەموو ناوەڕۆکی دامەزراو پشکنین دەکات بۆ تێکچوون کلیلە کۆدکراوەکان دیار نییە پتەوواڵا و یارییە تاکەکەسییەکان ناتوانرێت کۆد بکرێنەوە + فریموێر نادروستە + فریموێر پێویستە بۆ کارپێکردنی هەندێک یاری و بەکارهێنانی بەرنامەکان. ئێدین تەنها لەگەڵ فریموێری 19.0.1 یان کۆنتر کار دەکات. + فریموێر هەیە بەڵام ناتوانرێت بخوێنرێتەوە. پشکنین بکە بۆ کلیلی شیکردنەوە و دووبارە فریموێرەکە دامپ بکە ئەگەر پێویست بوو. + فریموێر زۆر نوێ یان ناتوانرێت بخوێنرێتەوە. ئێدین تەنها لەگەڵ فریموێری 19.0.1 یان کۆنتر کار دەکات. + سەرکەوتوو نەبوو لە ناساندنی کلیلی + کلیلەکان بە سەرکەوتوویی ناسێندران + یەک یان زیاتر کلیل سەرکەوتوو نەبوو لە ناساندن. + دڵنیا بەرەوە کە فایلی کلیلی تۆ پاشگرەکەی .keys هەیە و هەوڵ بدەرەوە. + کلیلەکان سەرکەوتوو نەبوون لە ناساندن. دڵنیا بەرەوە کە ئامرازەکانی دامپکردنت نوێن و دووبارە کلیلی دامپ بکە. Qlaunch @@ -463,7 +474,7 @@ بەرهەم هێنان ناوی بەکارهێنەری وێب - ناوی بەکارهێنەر کە لە لۆبی یاری فرە یاریکەردا دەر دەکەوێت. دەبێت 4-20 پیت بێت. + ناوی بەکارهێنەر کە لە ژوورەکانی فرە یاریزاندا پیشان دەدرێت. پێویستە ٤-٢٠ پیت بێت (تەنها پیت و ژمارە و خاڵ و خط و خەتەکێشە و بۆشایی). نیشانەی وێب نیشانەی وێب بۆ دروستکردنی ژوورە گشتییەکان. زنجیرەیەکی 48 پیتییە تەنها پیتی a-z بچووک لەخۆدەگرێت. تۆڕ @@ -626,11 +637,14 @@ ڕۆمەکەت کۆدکراوە + یارییە کارتەکان یان ناونیشانە دیجیتاڵییەکان.]]> prod.keys فایلەکەت بۆ ئەوەی بتوانرێت یارییەکان کۆد بکرێنەوە.]]> هەڵەیەک لە دەستپێکردنی ناوەکی ڤیدیۆکەدا ڕوویدا ئەمەش بەزۆری بەهۆی وەگەڕخەرێکی ناتەبای GPU ەوەیە. دامەزراندنی وەگەڕخەری GPU ی تایبەتمەندکراو لەوانەیە ئەم کێشەیە چارەسەر بکات. ناتوانرێت ڕۆم باربکرێت فایلی ڕۆم بوونی نییە + یارییەکە فریموێر پێویستە + فریموێر دامپ بکە و دابنێ, یان پەنجە بنێ سەر "باشە" بۆ بەردەوامبوون هەرچۆنێک بێت.]]> دەرچوون لە ئیمولەیشن diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml index fe8575512e..739ebed3fc 100644 --- a/src/android/app/src/main/res/values-cs/strings.xml +++ b/src/android/app/src/main/res/values-cs/strings.xml @@ -61,24 +61,25 @@ Zobrazit verzi firmwaru Zobrazí nainstalovanou verzi firmwaru - + Rozšíření GPU Renderer RAII Metoda automatické správy prostředků ve Vulkanu, která zajišťuje správné uvolnění prostředků, když již nejsou potřeba, ale může způsobit pády v balených hrách. - Různé + CPU a paměť Edenův závoj Experimentální nastavení pro zlepšení výkonu a schopností. Tato nastavení mohou způsobit černé obrazovky nebo další herní problémy. Experimentální nastavení - Nastavení v Eden\'s Veil jsou vysoce experimentální a mohou způsobit problémy. Pokud se vaše hra nespustí, deaktivujte všechna rozšíření a nastavte Extended Dynamic State na 0. + Nastavení obsažená v Edenově závoji jsou vysoce experimentální a mohou způsobit problémy. Pokud se vaše hra nespustí, deaktivujte všechna rozšíření. WIP: Přeskočení snímků Přepínání přeskočení snímků pro zlepšení výkonu snížením počtu vykreslených snímků. Tato funkce je stále ve vývoji a bude povolena v budoucích verzích. Vylepšené časování snímků Zajišťuje plynulé a konzistentní zobrazování snímků synchronizací jejich časování, čímž snižuje trhání a nerovnoměrné animace. Ideální pro hry, které trpí nestabilitou časování snímků nebo mikrotrháním během hraní. Povolit LRU mezipaměť - Povolte nebo zakažte mezipaměť LRU (Least Recently Used), což zvýší výkon snížením zatížení CPU. Některé hry s ní mají problémy, zejména TotK 1.2.1, proto ji deaktivujte, pokud hra nespustí nebo náhodně padá. + Povolte nebo zakažte mezipaměť LRU, čímž zvýšíte výkon snížením využití procesoru CPU. Některé hry s ní mají problémy, zejména TotK 1.2.1, takže ji deaktivujte, pokud hra neběží nebo náhodně padá. Rozšířený dynamický stav - Umožňuje funkce Vulkan pro lepší výkon, vykreslování a úsporu prostředků při vytváření pipeline při zachování nízkého využití CPU/GPU. Tato rozšíření mohou zvýšit teplotu zařízení a starší GPU řady A6XX nemusí fungovat správně. Nastavte na 0 pro použití zastaralých emulovaných formátů. + Umožňuje funkce Vulkan pro zlepšení výkonu, vykreslování a úsporu zdrojů při vytváření pipeline při zachování nižšího využití CPU/GPU. Tato rozšíření mohou zvýšit teplotu zařízení a starší GPU řady A6XX nemusí správně reagovat. Vypněte pro emulaci měřítkových formátů. + Vypnuto Synchronizovat rychlost jádra Synchronizuje rychlost jádra s maximálním procentem rychlosti, aby se zlepšil výkon bez změny skutečné rychlosti hry. Použít automatický stub @@ -183,18 +184,18 @@ Nepodařilo se připojit k místnosti Jméno je příliš krátké Neplatná adresa - Neplatný port! + Neplatný port Opustit místnost Chyba sítě Ztraceno připojení - Kolize jmen + Uživatelské jméno je již obsazeno Kolize MAC adresy Kolize ID konzole Nesprávná verze Nesprávné heslo Nelze se připojit Místnost je plná - Hostitel zablokován + Jste z této místnosti zabanován Přístup odepřen Uživatel neexistuje Již v místnosti @@ -254,7 +255,8 @@ Musí mít 3 až 20 znaků Povinné Neplatný formát IP - Musí mít 4–20 znaků + Musí mít 4–20 znaků a obsahovat pouze alfanumerické znaky, tečky, pomlčky, podtržítka a mezery + Neplatné uživatelské jméno, ujistěte se, že je správně nastaveno v Systém → Síť Musí být mezi 1 a 65535 Musí mít 48 znaků a pouze malá písmena a-z Typ místnosti @@ -376,6 +378,15 @@ Kontrola poškození obsahu Chybí šifrovací klíče Firmware a retail hry nelze dešifrovat + Neplatný firmware + Firmware je vyžadován pro spuštění některých her a aplikací. Eden funguje pouze s firmwarem 19.0.1 nebo starším. + Firmware byl nahlášen jako přítomný, ale nelze jej přečíst. Zkontrolujte šifrovací klíče a v případě potřeby firmware převezměte znovu. + Firmware je příliš nový nebo nelze přečíst. Eden funguje pouze s firmwarem 19.0.1 nebo starším. + Selhání instalace klíčů + Klíče úspěšně nainstalovány + Jeden nebo více klíčů se nepodařilo zkopírovat. + Ověřte, zda má váš soubor klíčů příponu .keys a zkuste to znovu. + Klíče se nepodařilo inicializovat. Zkontrolujte, zda jsou vaše dumpingové nástroje aktuální, a klíče převezměte znovu. Qlaunch @@ -439,7 +450,7 @@ Generovat Webové uživatelské jméno - Uživatelské jméno zobrazené v multiplayerových místnostech. Musí mít 4–20 znaků. + Uživatelské jméno zobrazené v multiplayerových místnostech. Musí mít 4–20 znaků a obsahovat pouze alfanumerické znaky, pomlčky, tečky, podtržítka a mezery. Webový token Webový token používaný k vytváření veřejných místností. Jedná se o řetězec o délce 48 znaků obsahující pouze malá písmena a-z. Síť @@ -560,40 +571,101 @@ Info + ID programu, vývojář, verze + Nastavení hry + Specifická nastavení + Konfigurace spuštění Cesta + ID programu Vývojář Verze Zkopírovat podrobnosti Modifkace + Módy, aktualizace a DLC + Vymazat shader cache + Odstranění všech shaderů + Může způsobit trhání + Shader cache vymazána Rozšíření: %1$s + Uložená data + Správa uložených dat + Smazat data + Odstranění všech uložených dat + Návrat není možný. Pokračovat? + Data smazána Typ obsahu Aktualizace a DLC Módy a cheaty + Důležité upozornění + Složka musí obsahovat cheats/, romfs/ nebo exefs/. + Neplatná složka + Složka musí obsahovat cheats/, romfs/ nebo exefs/. Rozšíření úspěšně nainstalováno Ověřování obsahu... + Upozornění + Obsah neodpovídá hře. Nainstalovat? + Potvrdit odstranění + Odstranit tento doplněk? + Ověřit integritu + Ověřování... + Integrita potvrzena + Chyba ověření + Soubor může být poškozen + Ověření nelze provést + Integrita nebyla ověřena + Chyba ověření:\n%1$s + + + ROM je zašifrován + herních karet nebo digitálních titulů.]]> + prod.keys pro dešifrování.]]> + Chyba inicializace + Neplatný GPU ovladač + Nelze načíst ROM + ROM neexistuje + Hra vyžaduje firmware + převezměte a nainstalujte firmware, nebo stiskněte "OK" pro pokračování v každém případě.]]> + + + Ukončit emulaci Hotovo + FPS čítač + Teplotní indikátor + Přepnout ovládání + Relativní střed joysticku + D-pad slide + Haptická odezva + Zobrazit překryv + Přepnout vše + Upravit překryv Měřítko Průhlednost + Resetovat překryv + Upravit překryv + Pozastavit emulaci + Obnovit emulaci + Nastavení překryvu Dotyková obrazovka + Zamknout zásuvku + Odemknout zásuvku + Načítání nastavení... + + + Softwarová klávesnice Přerušit Pokračovat Systémový Archív Nenalezen + %s chybí. Získejte systémové archivy.\nPokračování může způsobit chyby. + Systémový archív Ukládací/Načítací chyba Fatální Chyba - - - Japonsko - USA - Evropa - Austrálie - Čína - Korea - Taiwan - - B - GB + Vyskytla se fatální chyba. Podrobnosti v logu.\nPokračování může způsobit chyby. + Vypnutí sníží výkon! + RAM zařízení: %1$s\nDoporučeno: %2$s + %1$s %2$s + Nenalezena spustitelná hra! Vulkan @@ -602,6 +674,7 @@ Normální Vysoká + Extrémní (Pomalé) 0.25X (180p/270p) 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 93881eabb5..63d3e99173 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -62,24 +62,25 @@ Firmware-Version anzeigen Zeigt die installierte Firmware-Version an - + GPU-Erweiterungen Renderer RAII Eine Methode zur automatischen Ressourcenverwaltung in Vulkan, die eine ordnungsgemäße Freigabe von Ressourcen gewährleistet, wenn sie nicht mehr benötigt werden, aber bei gebündelten Spielen Abstürze verursachen kann. - Verschiedenes + CPU und Speicher Edens Schleier Experimentelle Einstellungen zur Verbesserung der Leistung und Funktionalität. Diese Einstellungen können schwarze Bildschirme oder andere Spielprobleme verursachen. Experimentelle Einstellungen - Die Einstellungen in Eden\'s Veil sind hoch experimentell und können Probleme verursachen. Wenn Ihr Spiel nicht startet, deaktivieren Sie alle Erweiterungen und setzen Sie Extended Dynamic State auf 0. + Die in Edens Schleier enthaltenen Einstellungen sind hoch experimentell und können Probleme verursachen. Wenn Ihr Spiel nicht startet, deaktivieren Sie alle Erweiterungen. WIP: Frame Skipping Aktivieren Sie Frame Skipping, um die Leistung durch Reduzierung der gerenderten Frames zu verbessern. Diese Funktion wird noch entwickelt und in zukünftigen Versionen verfügbar sein. Erweiterte Frame-Synchronisation Sorgt für eine gleichmäßige und konsistente Frame-Wiedergabe durch Synchronisierung der Frame-Zeiten, was Ruckeln und ungleichmäßige Animationen reduziert. Ideal für Spiele, die unter instabilen Frame-Zeiten oder Mikrorucklern leiden. LRU-Cache aktivieren - Aktivieren oder deaktivieren Sie den LRU-Cache (Least Recently Used), um die Leistung durch Reduzierung der CPU-Auslastung zu verbessern. Einige Spiele haben Probleme damit, insbesondere TotK 1.2.1, deaktivieren Sie ihn also, wenn das Spiel nicht startet oder zufällig abstürzt. + Aktivieren oder deaktivieren Sie den LRU-Cache, um die Leistung durch Einsparung von CPU-Prozessorauslastung zu verbessern. Einige Spiele haben Probleme damit, insbesondere TotK 1.2.1, deaktivieren Sie es also, wenn das Spiel nicht startet oder zufällig abstürzt. Erweiterter dynamischer Status - Aktiviert Vulkan-Funktionen zur Leistungssteigerung, verbessertem Rendering und Ressourcenschonung bei der Pipeline-Erstellung bei niedriger CPU/GPU-Auslastung. Diese Erweiterungen können die Gerätetemperatur erhöhen, und GPUs der älteren A6XX-Serie funktionieren möglicherweise nicht korrekt. Auf 0 setzen, um emulierte Legacy-Formate zu verwenden. + Aktiviert Vulkan-Funktionen zur Verbesserung der Leistung, des Renderings und zur Einsparung von Ressourcen bei der Pipeline-Erstellung bei gleichzeitig geringer CPU/GPU-Auslastung. Diese Erweiterungen können die Gerätetemperatur erhöhen, und ältere GPUs der A6XX-Serie reagieren möglicherweise nicht richtig. Deaktivieren Sie dies, um skalierte Formate zu emulieren. + Deaktiviert Kern-Geschwindigkeit synchronisieren Synchronisiert die Taktrate des Kerns mit der maximalen Geschwindigkeit, um die Leistung zu verbessern, ohne die tatsächliche Spielgeschwindigkeit zu verändern. Auto-Stub verwenden @@ -184,18 +185,18 @@ Beitritt fehlgeschlagen Name zu kurz Ungültige Adresse - Ungültiger Port! + Ungültiger Port Raum verlassen Netzwerkfehler Verbindung verloren - Namenskonflikt + Benutzername bereits vergeben MAC-Adressenkonflikt Konsole-ID-Konflikt Falsche Version Falsches Passwort Verbindung fehlgeschlagen Raum ist voll - Host gebannt + Sie sind von diesem Raum gebannt Zugriff verweigert Benutzer existiert nicht Bereits im Raum @@ -255,7 +256,8 @@ Muss 3-20 Zeichen lang sein Erforderlich Ungültiges IP-Format - Muss zwischen 4–20 Zeichen lang sein + Muss zwischen 4–20 Zeichen lang sein und nur alphanumerische Zeichen, Punkte, Bindestriche, Unterstriche und Leerzeichen enthalten + Ungültiger Benutzername, stellen Sie sicher, dass er in System → Netzwerk korrekt eingestellt ist Muss zwischen 1-65535 liegen Muss 48 Zeichen lang sein und nur Kleinbuchstaben a-z enthalten Lobby-Typ @@ -409,6 +411,15 @@ Wirklich fortfahren? Überprüft installierte Inhalte auf Fehler Schlüssel fehlen Firmware und Spiele können nicht entschlüsselt werden + Ungültige Firmware + Firmware wird benötigt, um bestimmte Spiele und Applikationen zu nutzen. Eden funktioniert nur mit Firmware 19.0.1 oder älter. + Firmware wurde als vorhanden gemeldet, konnte aber nicht gelesen werden. Überprüfen Sie die Entschlüsselungsschlüssel und dumpen Sie die Firmware bei Bedarf neu. + Firmware ist zu neu oder konnte nicht gelesen werden. Eden funktioniert nur mit Firmware 19.0.1 oder älter. + Schlüsselinstallation fehlgeschlagen + Schlüssel erfolgreich installiert + Ein oder mehrere Schlüssel konnten nicht kopiert werden. + Stellen Sie sicher, dass Ihre Schlüsseldatei die Erweiterung .keys hat, und versuchen Sie es erneut. + Schlüssel konnten nicht initialisiert werden. Stellen Sie sicher, dass Ihre Dumping-Tools auf dem neuesten Stand sind, und dumpen Sie die Schlüssel erneut. Qlaunch @@ -467,7 +478,7 @@ Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die Generieren Web-Benutzername - Benutzername, der in Multiplayer-Lobbys angezeigt wird. Muss 4–20 Zeichen lang sein. + Benutzername, der in Multiplayer-Räumen angezeigt wird. Muss zwischen 4–20 Zeichen lang sein und nur alphanumerische Zeichen, Bindestriche, Punkte, Unterstriche und Leerzeichen enthalten. Web-Token Web-Token zum Erstellen öffentlicher Räume. Es handelt sich um eine 48-stellige Zeichenfolge, die nur Kleinbuchstaben a-z enthält. Netzwerk @@ -682,11 +693,14 @@ Wirklich fortfahren? Integritätsüberprüfung konnte nicht durchgeführt werden Das ROM ist verschlüsselt + Spielkarten oder digitalen Titel.]]> prod.keys Datei installiert ist, damit Spiele entschlüsselt werden können.]]> Bei der Initialisierung des Videokerns ist ein Fehler aufgetreten Dies wird normalerweise durch einen inkompatiblen GPU-Treiber verursacht. Die Installation eines passenden GPU-Treibers kann dieses Problem beheben. ROM konnte nicht geladen werden ROM-Datei existiert nicht + Spiel erfordert Firmware + dumpen und installieren Sie Firmware, oder drücken Sie "OK", um trotzdem zu starten.]]> Emulation beenden 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 a55bf4d935..8cd0992f89 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -62,24 +62,25 @@ Mostrar versión de firmware Muestra la versión de firmware instalada - + Extensiones de GPU Renderizador RAII Un método de gestión automática de recursos en Vulkan que garantiza la liberación adecuada de recursos cuando ya no son necesarios, pero puede causar fallos en juegos empaquetados. - Misceláneos + CPU y memoria Velo de Edén Configuraciones experimentales para mejorar el rendimiento y la capacidad. Estas configuraciones pueden causar pantallas negras u otros problemas en el juego. Configuraciones experimentales - Las configuraciones dentro de Eden\'s Veil son altamente experimentales y pueden causar problemas. Si tu juego no inicia, desactiva cualquier extensión y cambia Extended Dynamic State a 0. + Los ajustes contenidos en el Velo de Eden son altamente experimentales y pueden causar problemas. Si tu juego no inicia, desactiva todas las extensiones. WIP: Salto de fotogramas Activa o desactiva el salto de fotogramas para mejorar el rendimiento reduciendo el número de fotogramas renderizados. Esta función está en desarrollo y se habilitará en futuras versiones. Sincronización de fotogramas mejorada Garantiza una reproducción suave y consistente de fotogramas sincronizando sus tiempos, reduciendo el tartamudeo y animaciones irregulares. Ideal para juegos con problemas de sincronización de fotogramas o microtartamudeos. Habilitar caché LRU - Activa o desactiva la caché LRU (menos usados recientemente), aumentando el rendimiento al ahorrar uso de CPU. Algunos juegos tienen problemas con ella, especialmente TotK 1.2.1, así que desactívala si el juego no arranca o se cierra aleatoriamente. + Activa o desactiva la caché LRU, mejorando el rendimiento al ahorrar uso del proceso de la CPU. Algunos juegos tienen problemas con ella, notablemente TotK 1.2.1, así que desactívala si el juego no inicia o se cierra aleatoriamente. Estado dinámico extendido - Habilita funciones de Vulkan para mejorar el rendimiento, el renderizado y ahorrar recursos en la creación de pipelines manteniendo un uso reducido de CPU/GPU. Estas extensiones pueden aumentar la temperatura del dispositivo, y las GPU de la antigua serie A6XX podrían no funcionar correctamente. Establecer en 0 para usar formatos emulados heredados. + Habilita funciones de Vulkan para mejorar el rendimiento, el renderizado y ahorrar recursos en la creación de pipelines manteniendo un uso bajo de CPU/GPU. Estas extensiones pueden aumentar la temperatura del dispositivo, y las GPU de la antigua línea A6XX pueden no reaccionar correctamente. Desactívalo para emular formatos escalados. + Desactivado Sincronizar velocidad del núcleo Sincroniza la velocidad del núcleo con el porcentaje máximo de velocidad para mejorar el rendimiento sin alterar la velocidad real del juego. Usar Auto Stub @@ -168,104 +169,105 @@ Siempre - چند نفره - اتاق بازی خود را میزبانی کنید یا به یک اتاق موجود بپیوندید - اتاق: %1$s - شناسه کنسول: %1$s - ایجاد - عضویت - مرور اتاق‌های عمومی - نام کاربری - آدرس IP - پورت - اتاق با موفقیت ایجاد شد - با موفقیت به اتاق پیوستید - خطا در ایجاد اتاق - خطا در پیوستن به اتاق - نام خیلی کوتاه است - آدرس نامعتبر - پورت نامعتبر! - خروج از اتاق - خطای شبکه - اتصال قطع شد - تضاد نام - تضاد آدرس MAC - تضاد شناسه کنسول - نسخه نادرست - رمز عبور نادرست - اتصال برقرار نشد - اتاق پر است - میزبان مسدود شده - دسترسی رد شد - کاربری وجود ندارد - قبلاً در اتاق هستید - خطا در ایجاد اتاق - میزبان اخراج شد - خطای ناشناخته - اتاق راه‌اندازی نشده - اتاق بیکار - در حال پیوستن... - به اتاق پیوستید - ناظر اتاق - %1$s پیوست - %1$s ترک کرد - %1$s اخراج شد - %1$s مسدود شد - آدرس آزاد شد - اخراج - ارسال پیام... - رمز عبور - در حال پیوستن... - در حال ایجاد... - نام اتاق - نام اتاق باید بین 3 تا 20 کاراکتر باشد - حداکثر بازیکنان (16) - حداکثر بازیکنان: %d - چت - گزینه‌های بیشتر - آدرس IP کپی شد - آدرس سرور - چت - پیام بنویسید... - ارسال - ارسال پیام - مدیریت - لیست مسدودها - کاربر مسدود شده‌ای وجود ندارد - آزاد کردن کاربر - آزاد کردن - آیا می‌خواهید %1$s را آزاد کنید؟ - مسدود کردن کاربر - اتاق‌های عمومی - اتاق عمومی یافت نشد - رمز عبور لازم است + Multijugador + Crea tu propia sala o únete a una existente para jugar con otras personas + Sala: %1$s + ID de consola: %1$s + Crear + Unirse + Buscar salas públicas + Nombre de usuario + Dirección IP + Puerto + Sala creada con éxito + Te has unido a la sala + Error al crear la sala + Error al unirse a la sala + Nombre demasiado corto + Dirección inválida + Puerto inválido + Salir de la sala + Error de red + Conexión perdida + Nombre de usuario ya en uso + Dirección MAC duplicada + ID de consola duplicada + Versión incorrecta + Contraseña incorrecta + No se pudo conectar + Sala llena + Estás baneado de esta sala + Permiso denegado + Usuario no encontrado + Ya estás en una sala + Error al crear sala + Anfitrión expulsado + Error desconocido + Sala no inicializada + Sala inactiva + Uniéndose a sala + Sala unida + Moderador de sala + %1$s se ha unido + %1$s ha salido + %1$s fue expulsado + %1$s fue baneado + Dirección desbaneada + Expulsar + Escribe un mensaje… + Contraseña + Uniéndose… + Creando… + Nombre de sala + El nombre debe tener entre 3 y 20 caracteres + Jugadores máx. (16) + Jugadores máx: %d + Chat + Más opciones + Dirección IP copiada al portapapeles + Dirección del servidor + Chat + Escribe un mensaje… + Enviar + Enviar mensaje + Moderación + Lista de baneados + No hay usuarios baneados + Desbanear usuario + Desbanear + ¿Seguro que quieres desbanear a %1$s? + Banear usuario + Salas públicas + No se encontraron salas públicas + Contraseña requerida : %1$d/%2$d - بازی - هر بازی - اتاق با رمز عبور - پنهان کردن اتاق‌های پر - پنهان کردن اتاق‌های خالی - برای بررسی مجدد لمس کنید - جستجوی اتاق‌ها... - چند نفره - بازی‌های ترجیحی - بازی ترجیحی - بازی یافت نشد - برای میزبانی اتاق باید یک بازی ترجیحی انتخاب کنید - باید بین 3 تا 20 کاراکتر باشد - الزامی - فرمت IP نامعتبر - Debe tener entre 4–20 caracteres - باید بین 1 تا 65535 باشد - Debe tener 48 caracteres y solo letras minúsculas a-z - Tipo de lobby - Público - No listado - انصراف - تایید - تازه‌سازی - لیست اتاق‌ها - Se requiere un token web, ve a Configuración avanzada -> Sistema -> Red + Juego + Cualquier juego + Sala con contraseña + Ocultar salas llenas + Ocultar salas vacías + Toca refrescar para buscar de nuevo + Buscar salas… + Multijugador + Juegos preferidos + Juego preferido + Tipo de sala + No se encontraron juegos + Debes elegir un Juego Preferido para crear una sala. + Debe tener entre 3 y 20 caracteres + Requerido + Token web requerido, ve a Configuración avanzada -> Sistema -> Red + Formato de IP inválido + Debe tener entre 4-20 caracteres, y solo puede contener caracteres alfanuméricos, puntos, guiones, guiones bajos y espacios + Nombre de usuario inválido, configúralo correctamente en Sistema -> Red + Debe tener 48 caracteres, solo letras minúsculas a-z + Debe ser entre 1 y 65535 + Cancelar + Aceptar + Refrescar + Lista de salas + Pública + No listada Color del tema @@ -422,6 +424,15 @@ Comprueba todo el contenido instalado por si hubiese alguno corrupto Faltan las claves de encriptación El firmware y los juegos no se pueden desencriptar + Firmware inválido + Se requiere firmware para ejecutar ciertos juegos y aplicaciones. Eden solo funciona con firmware 19.0.1 o anterior. + El firmware está presente pero no se pudo leer. Verifique las claves de descifrado y vuelva a volcar el firmware si es necesario. + El firmware es demasiado nuevo o no se pudo leer. Eden solo funciona con firmware 19.0.1 o anterior. + Error al instalar claves + Claves instaladas correctamente + No se pudieron copiar una o más claves. + Verifique que su archivo de claves tenga extensión .keys e intente nuevamente. + Error al inicializar las claves. Verifique que sus herramientas de volcado estén actualizadas y vuelva a volcar las claves. Qlaunch @@ -487,7 +498,7 @@ Generar Nombre de usuario web - Nombre de usuario que se mostrará en los lobbies multijugador. Debe tener entre 4 y 20 caracteres. + Nombre de usuario que se mostrará en las salas multijugador. Debe tener entre 4–20 caracteres y contener solo caracteres alfanuméricos, guiones, puntos, guiones bajos y espacios. Token web Token web utilizado para crear salas públicas. Es una cadena de 48 caracteres que solo contiene letras minúsculas a-z. Red @@ -721,12 +732,14 @@ Su ROM está encriptada - cartuchos de juegos o títulos instalados.]]> + tarjetas de juego o títulos digitales.]]> prod.keys está instalado, para que los juegos sean descifrados.]]> Ocurrió un error al inicializar el núcleo de video, posiblemente debido a una incompatibilidad con el driver seleccionado Esto suele deberse a un driver de GPU incompatible. La instalación de un controlador de GPU personalizado puede resolver este problema. No se pudo cargar la ROM Archivo ROM no existe + El juego requiere firmware + vuelca e instala el firmware, o pulsa "Aceptar" para continuar de todos modos.]]> Salir de la emulación diff --git a/src/android/app/src/main/res/values-fa/strings.xml b/src/android/app/src/main/res/values-fa/strings.xml index 99f48c1c9d..2b41dfb0a4 100644 --- a/src/android/app/src/main/res/values-fa/strings.xml +++ b/src/android/app/src/main/res/values-fa/strings.xml @@ -62,24 +62,25 @@ نمایش نسخه فریمور نسخه نصب شده فریمور را نمایش می‌دهد - + افزونه‌های GPU رندرر RAII روشی برای مدیریت خودکار منابع در ولکان که تضمین می‌کند منابع به درستی آزاد شوند وقتی دیگر مورد نیاز نیستند، اما ممکن است باعث کرش شدن بازی‌های بسته‌بندی شده شود. - متفرقه + پردازنده و حافظه پرده عدن تنظیمات آزمایشی برای بهبود عملکرد و قابلیت. این تنظیمات ممکن است باعث نمایش صفحه سیاه یا سایر مشکلات بازی شود. تنظیمات آزمایشی - تنظیمات موجود در Eden\'s Veil بسیار آزمایشی هستند و ممکن است باعث مشکلات شوند. اگر بازی شما بوت نمی‌شود، همه افزونه‌ها را غیرفعال کنید و Extended Dynamic State را روی 0 تنظیم کنید. + تنظیمات موجود در پرده عدن بسیار آزمایشی هستند و ممکن است باعث مشکلاتی شوند. اگر بازی شما اجرا نمی‌شود، همه پسوندها را غیرفعال کنید. در حال توسعه: رد کردن فریم‌ها با فعال کردن رد کردن فریم‌ها، عملکرد را با کاهش تعداد فریم‌های رندر شده بهبود دهید. این قابلیت در حال توسعه است و در نسخه‌های آینده فعال خواهد شد. زمان‌بندی پیشرفته فریم‌ها ارسال یکنواخت و پایدار فریم‌ها را با همگام‌سازی زمان بین آن‌ها تضمین می‌کند، که منجر به کاهش لرزش و انیمیشن‌های ناهموار می‌شود. برای بازی‌هایی که ناپایداری در زمان‌بندی فریم‌ها یا میکرو لرزش در حین بازی دارند ایده‌آل است فعال‌سازی حافظه نهان LRU - فعال یا غیرفعال کردن کش LRU (کمترین اخیراً استفاده شده)، که با کاهش استفاده از CPU عملکرد را افزایش می‌دهد. برخی بازی‌ها با آن مشکل دارند، به ویژه TotK 1.2.1، بنابراین اگر بازی اجرا نشد یا به صورت تصادفی کرش کرد، آن را غیرفعال کنید. + حافظه پنهان LRU را فعال یا غیرفعال کنید تا با کاهش استفاده از پردازنده، عملکرد بهبود یابد. برخی بازی‌ها مانند TotK 1.2.1 با این ویژگی مشکل دارند، در صورت عدم راه‌اندازی یا قطعی تصادفی بازی، آن را غیرفعال کنید. حالت پویای گسترده - قابلیت‌های ولکان را برای بهبود عملکرد، رندرینگ و صرفه‌جویی در منابع هنگام ایجاد خط لوله فعال می‌کند، در حالی که استفاده از CPU/GPU پایین نگه داشته می‌شود. این افزونه‌ها ممکن است دمای دستگاه را افزایش دهند و کارت‌های گرافیک سری قدیمی A6XX ممکن است به درستی کار نکنند. برای استفاده از فرمت‌های شبیه‌سازی شده قدیمی، مقدار را روی 0 تنظیم کنید. + قابلیت‌های ولکان را برای بهبود عملکرد، رندرینگ و صرفه‌جویی در منابع هنگام ایجاد خط لوله فعال می‌کند، در حالی که استفاده کمتری از CPU/GPU دارد. این پسوندها ممکن است دمای دستگاه را افزایش دهند و GPUهای قدیمی خط A6XX ممکن است به درستی واکنش نشان ندهند. برای شبیه‌سازی فرمت‌های مقیاس‌شده غیرفعال کنید. + غیرفعال همگام‌سازی سرعت هسته همگام‌سازی سرعت هسته با حداکثر درصد سرعت برای بهبود عملکرد بدون تغییر سرعت واقعی بازی. استفاده از استاب خودکار @@ -184,18 +185,18 @@ پیوستن به اتاق ناموفق بود نام خیلی کوتاه است آدرس نامعتبر - پورت نامعتبر! + پورت نامعتبر خروج از اتاق خطای شبکه اتصال قطع شد - تضاد نام + نام کاربری قبلا انتخاب شده تضاد آدرس MAC تضاد شناسه کنسول نسخه نادرست رمز عبور نادرست اتصال برقرار نشد اتاق پر است - میزبان مسدود شده + شما از این اتاق محروم شده‌اید دسترسی رد شد کاربری وجود ندارد قبلاً در اتاق هستید @@ -257,7 +258,8 @@ الزامی توکن وب مورد نیاز است، به تنظیمات پیشرفته -> سیستم -> شبکه بروید فرمت IP نامعتبر - باید بین 4-20 کاراکتر باشد + باید بین 4 تا 20 کاراکتر و فقط شامل حروف الفبا، اعداد، نقطه، خط تیره، زیرخط و فاصله باشد + نام کاربری نامعتبر، از تنظیم صحیح آن در سیستم > شبکه اطمینان حاصل کنید باید 48 کاراکتر باشد و فقط حروف کوچک a-z باید بین 1 تا 65535 باشد انصراف @@ -420,6 +422,15 @@ تمام محتوای نصب شده را از نظر خرابی بررسی می‌کند کلیدهای رمزگذاری وجود ندارند ثابت‌افزار و بازی‌های فروشگاهی قابل رمزگشایی نیستند + فیرمور نامعتبر + برای اجرای برخی بازی‌ها و برنامه‌ها نیاز به فیرمور است. ادن فقط با فیرمور 19.0.1 یا قدیمی‌تر کار می‌کند. + فیرمور موجود است اما قابل خواندن نیست. کلیدهای رمزگشایی را بررسی کنید و در صورت نیاز فیرمور را دوباره دامپ کنید. + فیرمور خیلی جدید است یا قابل خواندن نیست. ادن فقط با فیرمور 19.0.1 یا قدیمی‌تر کار می‌کند. + خطا در نصب کلیدها + کلیدها با موفقیت نصب شدند + یک یا چند کلید با خطا در کپی مواجه شد. + اطمینان حاصل کنید که فایل کلید شما دارای پسوند .keys است و مجددا تلاش کنید. + خطا در مقداردهی اولیه کلیدها. ابزارهای دامپ خود را بررسی و کلیدها را مجددا دامپ کنید. Qlaunch @@ -485,7 +496,7 @@ تولید نام کاربری وب - نام کاربری که در لابی‌های چندنفره نمایش داده می‌شود. باید بین 4 تا 20 کاراکتر باشد. + نام کاربری نمایش داده شده در اتاق‌های چندنفره. باید بین 4-20 کاراکتر و فقط شامل حروف الفبا، اعداد، خط تیره، نقطه، زیرخط و فاصله باشد. توکن وب توکن وب برای ایجاد اتاق‌های عمومی استفاده می‌شود. این یک رشته 48 کاراکتری است که فقط شامل حروف کوچک a-z می‌شود. شبکه @@ -720,12 +731,14 @@ رام شما رمزگذاری شده است - کارتیج‌های بازی یا عناوین نصب شده خود را استخراج کنید.]]> + کارت‌های بازی یا عنوان‌های دیجیتال را دنبال کنید.]]> در راه‌اندازی اولیه هسته ویدیو خطایی رخ داد این مورد معمولاً توسط یک درایور گرافیکی ناسازگار ایجاد می‌شود. نصب درایور گرافیکی سفارشی ممکن است این مشکل را حل کند. رام بارگذاری نشد فایل رام وجود ندارد + بازی نیاز به فیرمور دارد + فیرمور را دامپ و نصب کنید یا برای ادامه کار "تایید" را فشار دهید.]]> خروج از شبیه‌سازی 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 d9783766fe..ac5bf9876c 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -62,24 +62,25 @@ Afficher la version du firmware Affiche la version du firmware installé - + Extensions GPU Rendu RAII Une méthode de gestion automatique des ressources dans Vulkan qui assure la libération correcte des ressources lorsqu\'elles ne sont plus nécessaires, mais peut provoquer des plantages dans les jeux regroupés. - Divers - Voile d`Éden + CPU et mémoire + Voile d\'Eden Paramètres expérimentaux pour améliorer les performances et les capacités. Ces paramètres peuvent causer des écrans noirs ou d\'autres problèmes de jeu. Paramètres expérimentaux - Les paramètres contenus dans Eden\'s Veil sont hautement expérimentaux et peuvent causer des problèmes. Si votre jeu ne démarre pas, désactivez toutes les extensions et réglez Extended Dynamic State sur 0. + Les paramètres du Voile d\'Eden sont très expérimentaux et peuvent causer des problèmes. Si votre jeu ne démarre pas, désactivez toutes les extensions. WIP: Saut de frames Activez ou désactivez le saut d\'images pour améliorer les performances en réduisant le nombre d\'images affichées. Cette fonctionnalité est en cours de développement et sera activée dans les futures versions. Synchronisation avancée des frames Assure une diffusion fluide et régulière des frames en synchronisant leur timing, réduisant ainsi les saccades et les animations irrégulières. Idéal pour les jeux souffrant d`instabilité de timing des frames ou de micro-saccades pendant le jeu. Activer le cache LRU - Activez ou désactivez le cache LRU (Least Recently Used), ce qui améliore les performances en réduisant l\'utilisation du CPU. Certains jeux ont des problèmes avec, notamment TotK 1.2.1, désactivez-le donc si le jeu ne démarre pas ou plante aléatoirement. + Active ou désactive le cache LRU pour améliorer les performances en réduisant l\'utilisation du processeur. Certains jeux comme TotK 1.2.1 ont des problèmes - désactivez-le si le jeu ne démarre pas ou plante aléatoirement. État dynamique étendu - Active les fonctionnalités Vulkan pour améliorer les performances, le rendu et économiser les ressources lors de la création du pipeline tout en maintenant une faible utilisation du CPU/GPU. Ces extensions peuvent augmenter la température de l\'appareil, et les GPU de l\'ancienne série A6XX peuvent ne pas fonctionner correctement. Réglez sur 0 pour utiliser les formats émulés hérités. + Active les fonctionnalités Vulkan pour améliorer les performances, le rendu et économiser les ressources lors de la création des pipelines tout en maintenant une utilisation réduite du CPU/GPU. Ces extensions peuvent augmenter la température de l\'appareil et les GPU anciens de la série A6XX peuvent mal réagir. Désactivez pour émuler les formats mis à l\'échelle. + Désactivé Synchroniser la vitesse du cœur Synchronise la vitesse du cœur avec le pourcentage de vitesse maximal pour améliorer les performances sans modifier la vitesse réelle du jeu. Utiliser le stub automatique @@ -184,18 +185,18 @@ Échec de la connexion au salon Nom trop court Adresse invalide - Port invalide ! + Port invalide Quitter le salon Erreur réseau Connexion perdue - Conflit de noms + Nom d\'utilisateur déjà pris Conflit d\'adresse MAC Conflit d\'ID console Mauvaise version Mauvais mot de passe Connexion impossible Salon plein - Hôte banni + Vous êtes banni de cette salle Permission refusée Utilisateur introuvable Déjà dans le salon @@ -255,7 +256,8 @@ Doit contenir entre 3 et 20 caractères Requis Format IP invalide - Doit contenir entre 4 et 20 caractères + Doit contenir 4-20 caractères alphanumériques, points, tirets, underscores et espaces uniquement + Nom d\'utilisateur invalide, vérifiez le dans Système → Réseau Doit être entre 1 et 65535 Doit contenir 48 caractères, uniquement des lettres minuscules a-z Type de lobby @@ -422,6 +424,15 @@ Vérifie l\'intégrité des contenus installés Les clés de chiffrement sont manquantes. Le firmware et les jeux commerciaux ne peuvent pas être déchiffrés + Firmware invalide + Le firmware est requis pour exécuter certains jeux et applications. Eden ne fonctionne qu\'avec le firmware 19.0.1 ou antérieur. + Firmware présent mais illisible. Vérifiez les clés de décryptage et redumpz le firmware si nécessaire. + Firmware trop récent ou illisible. Eden ne supporte que le firmware 19.0.1 ou antérieur. + Échec de l\'installation des clés + Clés installées avec succès + Échec de la copie d\'une ou plusieurs clés. + Vérifiez que votre fichier de clés a l\'extension .keys et réessayez. + Échec de l\'initialisation des clés. Vérifiez vos outils de dumping et redumpz les clés. Qlaunch @@ -487,7 +498,7 @@ Générer Nom d\'utilisateur web - Nom d\'utilisateur à afficher dans les lobbys multijoueur. Doit contenir entre 4 et 20 caractères. + Nom d\'utilisateur affiché dans les salles multijoueur. Doit contenir 4-20 caractères alphanumériques, tirets, points, underscores et espaces uniquement. Jeton Web Jeton Web utilisé pour créer des salles publiques. Il s\'agit d\'une chaîne de 48 caractères ne contenant que des lettres minuscules a-z. Réseau @@ -769,12 +780,14 @@ Votre ROM est cryptée - cartouches de jeu ou de vos titres installés.]]> + cartes de jeu ou titres numériques.]]> prod.keys est installé pour que les jeux puissent être déchiffrés.]]> Une erreur s\'est produite lors de l\'initialisation du noyau vidéo Cela est généralement dû à un pilote GPU incompatible. L\'installation d\'un pilote GPU personnalisé peut résoudre ce problème. Impossible de charger la ROM Le fichier ROM n\'existe pas + Jeu nécessite un firmware + dumper et installer le firmware ou appuyez sur "OK" pour continuer quand même.]]> Quitter l\'émulation diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml index 47a01d7dbd..2d7e575710 100644 --- a/src/android/app/src/main/res/values-he/strings.xml +++ b/src/android/app/src/main/res/values-he/strings.xml @@ -62,24 +62,25 @@ הצג גרסת קושחה מציג את גרסת הקושחה המותקנת - + הרחבות GPU רנדרר RAII שיטה לניהול אוטומטי של משאבים ב-Vulkan המבטיחה שחרור נכון של משאבים כאשר הם כבר לא נחוצים, אך עלולה לגרום לקריסות במשחקים מאוגדים. - שונות + מעבד וזיכרון עדן וייל הגדרות ניסיוניות לשיפור ביצועים ויכולות. הגדרות אלו עלולות לגרום למסכים שחורים או לבעיות אחרות במשחק. הגדרות ניסיוניות - ההגדרות הכלולות ב-Eden\'s Veil הן ניסיוניות מאוד ועלולות לגרום לבעיות. אם המשחק שלך לא נטען, השבת כל הרחבות ושנה את Extended Dynamic State ל-0. + הגדרות במסך העדן ניסיוניות מאוד ועלולות לגרום לבעיות. אם המשחק לא עולה, השבת את כל ההרחבות. בעבודה: דילוג פריימים החלף דילוג על פריימים כדי לשפר ביצועים על ידי הפחתת מספר הפריימים המוצגים. תכונה זו עדיין בפיתוח ותופעל בגרסאות עתידיות. סנכרון פריימים מתקדם מבטיח אספקה חלקה ועקבית של פריימים על ידי סנכרון התזמון ביניהם, מפחית קפיצות ואנימציה לא אחידה. אידיאלי למשחקים עם בעיות בתזמון פריימים או מיקרו-קפיצות במהלך המשחק. הפעלת מטמון LRU - הפעל או השבת את מטמון LRU (Least Recently Used), המשפר ביצועים על ידי חיסכון בשימוש במעבד. חלק מהמשחקים נתקלים בבעיות עם זה, במיוחד TotK 1.2.1, אז השבת אם המשחק לא נטען או קורס אקראית. + הפעל או השבת מטמון LRU לשיפור ביצועים על ידי חיסכון בשימוש במעבד. לחלק מהמשחקים כמו TotK 1.2.1 יש בעיות - השבת אם המשחק לא עולה או קורס באקראי. מצב דינמי מורחב - מאפשר תכונות Vulkan לשיפור ביצועים, רינדור וחיסכון במשאבים בעת יצירת צינור עיבוד תוך שמירה על שימוש נמוך ב-CPU/GPU. הרחבות אלו עשויות להעלות את טמפרטורת המכשיר, וייתכן שכרטיסי מסך מסדרת A6XX הישנה לא יגיבו כראוי. הגדר ל-0 כדי להשתמש בפורמטים מדומים מיושנים. + מאפשר תכונות Vulkan לשיפור ביצועים, רינדור וחיסכון במשאבים בעת יצירת צינורות עיבוד תוך שמירה על שימוש נמוך במעבד/מעבד גרפי. הרחבות אלו עלולות להעלות את טמפרטורת המכשיר, ומעבדים גרפיים מקו A6XX הישן עשויים לא להגיב כראוי. השבת כדי לדמות פורמטים מוקטנים. + מושבת סנכרון מהירות ליבה סנכרן את מהירות הליבה לאחוז המהירות המרבי כדי לשפר ביצועים מבלי לשנות את מהירות המשחק בפועל. שימוש ב-Auto Stub @@ -185,18 +186,18 @@ הצטרפות לחדר נכשלה השם קצר מדי כתובת לא תקינה - פורט לא תקין! + פורט לא תקין צא מהחדר שגיאת רשת החיבור אבד - שם כבר קיים + שם משתמש כבר תפוס כתובת MAC כבר קיימת מזהה קונסולה כבר קיים גרסה לא נכונה סיסמה לא נכונה לא ניתן להתחבר החדר מלא - מארח חסום + הוחרמת מחדר זה הרשאה נדחתה אין משתמש כזה כבר בחדר @@ -256,7 +257,8 @@ חייב להיות בין 3-20 תווים נדרש פורמט IP לא תקין - חייב להיות בין 4-20 תווים + חייב להכיל 4-20 תווים (אותיות, מספרים, נקודות, מקפים, קו תחתון ורווחים בלבד) + שם משתמש לא תקין, ודא שהוא מוגדר כראוי במערכת → רשת חייב להיות בין 1-65535 חייב להיות באורך 48 תווים, ורק אותיות קטנות a-z סוג לובי @@ -421,6 +423,15 @@ בודק תוכן מותקן לשגיאות מפתחות הצפנה חסרים לא ניתן לפענח firmware ומשחקים + קושחה לא תקינה + נדרש קושחה להפעלת משחקים ויישומים מסוימים. עדן עובד רק עם קושחה 19.0.1 או ישנה יותר. + קושחה קיימת אך לא ניתנת לקריאה. בדוק מפתחות פענוח ושמור קושחה מחדש אם needed. + קושחה חדשה מדי או לא ניתנת לקריאה. עדן תומך רק בקושחה 19.0.1 או ישנה יותר. + התקנת מפתחות נכשלה + מפתחות הותקנו בהצלחה + אחד או יותר מהמפתחות לא הצליחו להעתקה. + ודא שלקובץ המפתחות שלך יש סיומת .keys ונסה שוב. + אתחול המפתחות נכשל. ודא שכלי ה-dump מעודכנים ושמור מפתחות מחדש. Qlaunch @@ -486,7 +497,7 @@ יצירה שם משתמש באינטרנט - שם המשתמש שיוצג בלובי מרובה משתתפים. חייב להיות באורך 4-20 תווים. + שם משתמש שיוצג בחדרים מרובי משתתפים. חייב להכיל 4-20 תווים (אותיות, מספרים, מקפים, נקודות, קו תחתון ורווחים בלבד). אסימון אינטרנט אסימון אינטרנט המשמש ליצירת חדרים ציבוריים. זהו מחרוזת של 48 תווים המכילה רק אותיות קטנות a-z. רשת @@ -655,12 +666,14 @@ אימות התקינות הצליח! המשחק שלך מוצפן - כרטיסי המשחקאו הכותרות המותקנות שלך.]]> + כרטיסי משחק או כותרות דיגיטליות מחדש.]]> prod.keys מותקן כך שניתן יהיה לפענח משחקים.]]> התרחשה בעיה באתחול של ליבת הווידאו זה בדרך כלל נגרם על ידי דרייבר לא מתאים עבור המעבד הגרפי. התקנת דרייבר אשר מתאים למעבד הגרפי יכול לפתור את הבעיה הזו. אין אפשרות לטעון את המשחק קובץ המשחק לא קיים + המשחק דורש קושחה + שמור והתקן קושחה או לחץ "אישור" כדי להמשיך בכל מקרה.]]> צא מהאמולציה diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml index e14af511c6..a9448a7e41 100644 --- a/src/android/app/src/main/res/values-hu/strings.xml +++ b/src/android/app/src/main/res/values-hu/strings.xml @@ -62,24 +62,25 @@ Firmver verzió megjelenítése A telepített firmver verziójának megjelenítése - + GPU kiterjesztések Megjelenítő RAII A Vulkan erőforrás-kezelési módszere, amely biztosítja az erőforrások megfelelő felszabadítását, ha már nincs rájuk szükség, de csomagolt játékok összeomlását okozhatja. - Egyéb + CPU és memória Eden Fátyla Kísérleti beállítások a teljesítmény és képesség javításához. Ezek a beállítások fekete képernyőket vagy más játékproblémákat okozhatnak. Kísérleti beállítások - Az Eden\'s Veil-ben található beállítások nagyon kísérletiek és problémákat okozhatnak. Ha a játékod nem indul, tiltsd le az összes kiterjesztést és állítsd az Extended Dynamic State értékét 0-ra. + Az Eden Fátyla beállításai nagyon kísérleti jellegűek, és problémákat okozhatnak. Ha a játék nem indul el, kérjük, tiltsa le az összes kiterjesztést. Folyamatban: Képkihagyás Kapcsolja be a képkihagyást a teljesítmény javításához a renderelt képkockák számának csökkentésével. Ez a funkció még fejlesztés alatt áll, és a jövőbeli kiadásokban lesz elérhető. Továbbfejlesztett Képkocka-időzítés Biztosítja a képkockák sima és egyenletes kézbesítését azok időzítésének szinkronizálásával, csökkentve a megakadásokat és egyenetlen animációkat. Ideális azokhoz a játékokhoz, amelyek képkocka-időzítési instabilitást vagy mikro-reccsenést tapasztalnak játék közben. LRU gyorsítótár engedélyezése - Engedélyezze vagy tiltsa le az LRU gyorsítótárat (Least Recently Used), amely a CPU használatának csökkentésével növeli a teljesítményt. Néhány játékkal problémák adódhatnak, különösen a TotK 1.2.1 esetében, ezért tiltsa le, ha a játék nem indul el vagy véletlenszerűen összeomlik. + Engedélyezi vagy letiltja az LRU gyorsítótárat, növelve a teljesítményt a CPU használat csökkentésével. Néhány játéknak (különösen a TotK 1.2.1-nek) problémája lehet vele - tiltsa le, ha a játék nem indul el vagy véletlenszerűen összeomlik. Kiterjesztett Dinamikus Állapot - Engedélyezi a Vulkan funkciókat a teljesítmény javításához, a rendereléshez és az erőforrások megtakarításához a pipeline létrehozása során, miközben alacsony CPU/GPU kihasználtságot tart fenn. Ezek a kiterjesztések növelhetik az eszköz hőmérsékletét, és a régebbi A6XX sorozatú GPU-k nem feltétlenül működnek megfelelően. Állítsd 0-ra a régi emulált formátumok használatához. + Engedélyezi a Vulkan funkciókat a teljesítmény, renderelés javítására és erőforrások spórolására a pipeline létrehozásánál, miközben alacsony CPU/GPU használatot tart fenn. Ezek a kiterjesztések növelhetik az eszköz hőmérsékletét, és a régebbi A6XX sorozatú GPU-k nem feltétlenül működnek megfelelően. Tiltsa le a skálázott formátumok emulálásához. + Letiltva Magsebesség szinkronizálása A mag sebességének szinkronizálása a maximális sebesség százalékával a teljesítmény javítása érdekében a játék tényleges sebességének megváltoztatása nélkül. Automatikus Stub használata @@ -184,18 +185,18 @@ Csatlakozás sikertelen Túl rövid név Érvénytelen cím - Érvénytelen port! + Érvénytelen port Kilépés a szobából Hálózati hiba Kapcsolat megszakadt - Névütközés + A felhasználónév már foglalt MAC-cím ütközés Konzol azonosító ütközés Hibás verzió Hibás jelszó Nem sikerült csatlakozni A szoba tele van - Gazda kitiltva + Ki vagy tiltva ebből a szobából Engedély megtagadva Nincs ilyen felhasználó Már a szobában van @@ -255,7 +256,8 @@ 3-20 karakter kell Kötelező Érvénytelen IP formátum - 4–20 karakter hosszú lehet + 4-20 karakter hosszú legyen, és csak betűket, számokat, pontokat, kötőjeleket, aláhúzásjeleket és szóközöket tartalmazzon + Érvénytelen felhasználónév, ellenőrizze a Rendszer → Hálózat menüpontban 1-65535 között legyen 48 karakter hosszú lehet, és csak kisbetűket (a-z) tartalmazhat Lobby típusa @@ -418,6 +420,15 @@ A telepített tartalom épségét ellenőrzi Hiányzó titkosítókulcsok A Firmware és a kiskereskedelmi (retail) játékok nem dekódolhatók + Érvénytelen firmware + A firmware szükséges egyes játékok és alkalmazások futtatásához. Az Eden csak 19.0.1 vagy régebbi firmware-ekkel működik. + A firmware jelen van, de nem olvasható. Ellenőrizze a visszafejtési kulcsokat és szükség esetén dumpolja újra a firmware-t. + A firmware túl új vagy nem olvasható. Az Eden csak a 19.0.1 vagy régebbi firmware-ekkel működik. + A kulcsok telepítése sikertelen + A kulcsok sikeresen telepítve + Egy vagy több kulcs másolása sikertelen. + Győződjön meg róla, hogy a kulcsfájl .keys kiterjesztésű, és próbálja újra. + A kulcsok inicializálása sikertelen. Ellenőrizze, hogy a dumping eszközök naprakészek-e, és dumpolja újra a kulcsokat. Qlaunch @@ -482,7 +493,7 @@ Generálás Webes felhasználónév - A többjátékos lobbikban megjelenítendő felhasználónév. 4–20 karakter hosszúságú lehet. + A többjátékos szobákban megjelenítendő felhasználónév. 4-20 karakter hosszú legyen, és csak betűket, számokat, kötőjeleket, pontokat, aláhúzásjeleket és szóközöket tartalmazzon. Web token Web token nyilvános termek létrehozásához. Ez egy 48 karakter hosszú sztring, amely csak kisbetűket (a-z) tartalmaz. Hálózat @@ -759,11 +770,14 @@ ROM titkosítva + játékkártyák vagy digitális címek újradumpolásához.]]> prod.keys fájl telepítve van, hogy a játékok visszafejthetők legyenek.]]> Hiba lépett fel a videómag inicializása során Ezt általában egy nem kompatibilis GPU illesztő okozza. Egyéni GPU illesztőprogram telepítése megoldhatja a problémát. Nem sikerült betölteni a ROM-ot ROM fájl nem létezik + A játék firmware-t igényel + dumpolja és telepítse a firmware-t, vagy nyomja meg az "OK" gombot a folytatáshoz.]]> Emuláció bezárása diff --git a/src/android/app/src/main/res/values-id/strings.xml b/src/android/app/src/main/res/values-id/strings.xml index 2d931d2c5d..a9aade2ed7 100644 --- a/src/android/app/src/main/res/values-id/strings.xml +++ b/src/android/app/src/main/res/values-id/strings.xml @@ -62,24 +62,25 @@ Tampilkan Versi Firmware Menampilkan versi firmware yang terinstal - + Ekstensi GPU Renderer RAII Metode manajemen sumber daya otomatis di Vulkan yang memastikan pelepasan sumber daya yang tepat ketika tidak lagi diperlukan, tetapi dapat menyebabkan crash pada game yang dibundel. - Lain-lain - Eden`s Veil + CPU dan Memori + Eden\'s Veil Pengaturan eksperimental untuk meningkatkan kinerja dan kemampuan. Pengaturan ini dapat menyebabkan layar hitam atau masalah game lainnya. Pengaturan Eksperimental - Pengaturan dalam Eden\'s Veil sangat eksperimental dan dapat menyebabkan masalah. Jika game Anda tidak boot, nonaktifkan semua ekstensi dan ubah Extended Dynamic State ke 0. + Pengaturan di Eden\'s Veil sangat eksperimental dan dapat menyebabkan masalah. Jika game tidak mau berjalan, nonaktifkan semua ekstensi. WIP: Loncatan Frame Aktifkan atau nonaktifkan frame skipping untuk meningkatkan performa dengan mengurangi jumlah frame yang dirender. Fitur ini masih dalam pengembangan dan akan diaktifkan di rilis mendatang. Penyelarasan Frame Tingkat Lanjut Memastikan pengiriman frame yang halus dan konsisten dengan menyinkronkan waktu antar frame, mengurangi stuttering dan animasi tidak rata. Ideal untuk game yang mengalami ketidakstabilan waktu frame atau micro-stutter selama gameplay. Aktifkan LRU Cache - Aktifkan atau nonaktifkan cache LRU (Least Recently Used), meningkatkan performa dengan menghemat penggunaan CPU. Beberapa game memiliki masalah dengannya, terutama TotK 1.2.1, jadi nonaktifkan jika game tidak mau boot atau sering crash. + Aktifkan atau nonaktifkan cache LRU untuk meningkatkan performa dengan mengurangi penggunaan proses CPU. Beberapa game seperti TotK 1.2.1 memiliki masalah - nonaktifkan jika game tidak mau berjalan atau crash acak. Status Dinamis Ekstensi - Mengaktifkan fitur Vulkan untuk meningkatkan kinerja, rendering, dan menghemat sumber daya saat pembuatan pipeline dengan tetap mempertahankan penggunaan CPU/GPU yang rendah. Ekstensi ini dapat meningkatkan suhu perangkat, dan GPU dari seri A6XX lama mungkin tidak berfungsi dengan baik. Setel ke 0 untuk menggunakan format emulasi lama. + Aktifkan fitur Vulkan untuk meningkatkan performa, rendering dan menghemat sumber daya saat pembuatan pipeline sambil mempertahankan penggunaan CPU/GPU yang rendah. Ekstensi ini dapat meningkatkan suhu perangkat, dan GPU seri A6XX lama mungkin tidak merespons dengan benar. Nonaktifkan untuk emulasi format skala. + Dinonaktifkan Sinkronisasi Kecepatan Inti Sinkronkan kecepatan inti dengan persentase kecepatan maksimum untuk meningkatkan performa tanpa mengubah kecepatan sebenarnya dari permainan. Gunakan Auto Stub @@ -184,18 +185,18 @@ Gagal bergabung ke ruang Nama terlalu pendek Alamat tidak valid - Port tidak valid! + Port tidak valid Keluar Ruang Kesalahan jaringan Koneksi terputus - Nama bentrok + Nama pengguna sudah digunakan Alamat MAC bentrok ID Konsol bentrok Versi salah Password salah Tidak dapat terhubung Ruang penuh - Host diblokir + Anda dilarang masuk ke ruangan ini Izin ditolak Pengguna tidak ditemukan Sudah berada di ruang @@ -257,7 +258,8 @@ Diperlukan Token Web diperlukan, buka Pengaturan Lanjutan -> Sistem -> Jaringan Format IP tidak valid - Harus antara 4-20 karakter + Harus 4-20 karakter (hanya alfanumerik, titik, tanda hubung, garis bawah, dan spasi) + Nama pengguna tidak valid, pastikan sudah diatur dengan benar di Sistem → Jaringan Harus 48 karakter, dan hanya huruf kecil a-z Harus antara 1-65535 Batal @@ -418,6 +420,15 @@ Memeriksa semua konten yang terinstal dari kerusakan Kunci enkripsi hilang Firmware dan game retail tidak dapat didekripsi + Firmware tidak valid + Firmware diperlukan untuk menjalankan game dan aplikasi tertentu. Eden hanya bekerja dengan firmware 19.0.1 atau lebih lama. + Firmware dilaporkan ada tetapi tidak dapat dibaca. Periksa kunci dekripsi dan dump ulang firmware jika diperlukan. + Firmware terlalu baru atau tidak dapat dibaca. Eden hanya bekerja dengan firmware 19.0.1 atau lebih lama. + Gagal menginstal kunci + Kunci berhasil diinstal + Satu atau lebih kunci gagal disalin. + Pastikan file kunci Anda memiliki ekstensi .keys dan coba lagi. + Kunci gagal diinisialisasi. Periksa apakah alat dumping Anda mutakhir dan dump ulang kunci. Qlaunch @@ -483,7 +494,7 @@ Hasilkan Nama Pengguna Web - Nama pengguna yang akan ditampilkan di lobi multipemain. Harus terdiri dari 4–20 karakter. + Nama pengguna yang ditampilkan di ruang multiplayer. Harus 4-20 karakter (hanya alfanumerik, tanda hubung, titik, garis bawah, dan spasi). Token Web Token web digunakan untuk membuat ruang publik. Ini adalah string 48 karakter yang hanya berisi huruf kecil a-z. Jaringan @@ -713,12 +724,14 @@ ROM-mu ter-enkripsi - kartu permainan atau judul yang terinstal.]]> + kartrid game atau judul digital Anda.]]> prod.keys diinstal sehingga game dapat didekripsi.]]> Terjadi kesalahan ketika menginisialisasi inti video. Hal ini biasanya disebabkan oleh driver GPU yang tidak kompatibel. Menginstal driver GPU khusus dapat mengatasi masalah ini Tidak Dapat Memuat ROM Berkas Tidak Ditemukan + Game memerlukan firmware + dump dan instal firmware, atau tekan "OK" untuk melanjutkan.]]> Keluar 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 33a937ca93..4bbc1a78eb 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml @@ -62,24 +62,25 @@ Mostra versione firmware Visualizza la versione del firmware installato - + Estensioni GPU Renderer RAII Un metodo di gestione automatica delle risorse in Vulkan che garantisce il corretto rilascio delle risorse quando non sono più necessarie, ma può causare crash nei giochi in bundle. - Varie + CPU e Memoria Velo di Eden Impostazioni sperimentali per migliorare prestazioni e capacità. Queste impostazioni possono causare schermate nere o altri problemi di gioco. Impostazioni sperimentali - Le impostazioni contenute in Eden\'s Veil sono altamente sperimentali e possono causare problemi. Se il tuo gioco non si avvia, disabilita tutte le estensioni e imposta Extended Dynamic State su 0. + Le impostazioni nel Velo di Eden sono altamente sperimentali e possono causare problemi. Se il gioco non si avvia, disabilita tutte le estensioni. WIP: Salto fotogrammi Attiva o disattiva il salto dei fotogrammi per migliorare le prestazioni riducendo il numero di fotogrammi renderizzati. Questa funzionalità è ancora in sviluppo e verrà abilitata nelle versioni future. Sincronizzazione avanzata fotogrammi Garantisce una consegna fluida e costante dei fotogrammi sincronizzandone i tempi, riducendo scatti e animazioni irregolari. Ideale per giochi che presentano instabilità nei tempi dei fotogrammi o micro-scatti durante il gameplay. Abilita cache LRU - Abilita o disabilita la cache LRU (Least Recently Used), aumentando le prestazioni risparmiando l\'utilizzo della CPU. Alcuni giochi hanno problemi con essa, in particolare TotK 1.2.1, quindi disabilitala se il gioco non si avvia o si blocca casualmente. + Abilita o disabilita la cache LRU per migliorare le prestazioni riducendo l\'uso della CPU. Alcuni giochi come TotK 1.2.1 hanno problemi - disabilitalo se il gioco non si avvia o crasha casualmente. Stato dinamico esteso - Abilita le funzionalità Vulkan per migliorare le prestazioni, il rendering e risparmiare risorse nella creazione della pipeline mantenendo un basso utilizzo di CPU/GPU. Queste estensioni possono aumentare la temperatura del dispositivo e le GPU della vecchia serie A6XX potrebbero non funzionare correttamente. Imposta su 0 per utilizzare i formati emulati legacy. + Abilita le funzionalità Vulkan per migliorare prestazioni, rendering e risparmiare risorse durante la creazione della pipeline mantenendo un basso utilizzo di CPU/GPU. Queste estensioni possono aumentare la temperatura del dispositivo e le GPU della vecchia serie A6XX potrebbero non rispondere correttamente. Disabilita per emulare formati scalati. + Disabilitato Sincronizza velocità core Sincronizza la velocità del core con la percentuale massima di velocità per migliorare le prestazioni senza alterare la velocità effettiva del gioco. Usa Auto Stub @@ -184,18 +185,18 @@ Unione alla stanza fallita Nome troppo corto Indirizzo non valido - Porta non valida! + Porta non valida Esci dalla stanza Errore di rete Connessione persa - Conflitto di nomi + Nome utente già in uso Conflitto MAC Conflitto ID console Versione errata Password errata Impossibile connettersi Stanza piena - Host bannato + Sei bannato da questa stanza Permesso negato Utente inesistente Già nella stanza @@ -255,7 +256,8 @@ 3-20 caratteri richiesti Richiesto Formato IP non valido - Deve essere lungo 4–20 caratteri + Deve essere lungo 4-20 caratteri (solo alfanumerici, punti, trattini, underscore e spazi) + Nome utente non valido, verificare in Sistema → Rete Deve essere tra 1-65535 Deve essere lungo 48 caratteri e contenere solo lettere minuscole a-z Tipo di lobby @@ -421,6 +423,15 @@ Verifica l\'integrità di tutti i contenuti installati. Chiavi di crittografia mancanti Impossibile decifrare firmware e giochi retail + Firmware non valido + Il firmware è richiesto per eseguire alcuni giochi e applicazioni. Eden funziona solo con firmware 19.0.1 o precedente. + Il firmware è presente ma non può essere letto. Controlla le chiavi di decrittazione e ridumpa il firmware se necessario. + Il firmware è troppo recente o illeggibile. Eden supporta solo firmware 19.0.1 o precedenti. + Installazione chiavi fallita + Chiavi installate con successo + Una o più chiavi non sono state copiate. + Assicurati che il tuo file chiavi abbia estensione .keys e riprova. + Inizializzazione chiavi fallita. Controlla che i tuoi strumenti di dumping siano aggiornati e ridumpa le chiavi. Qlaunch @@ -486,7 +497,7 @@ Genera Nome utente web - Nome utente da visualizzare nelle lobby multigiocatore. Deve essere lungo 4–20 caratteri. + Nome utente visualizzato nelle stanze multiplayer. Deve essere lungo 4-20 caratteri (solo alfanumerici, trattini, punti, underscore e spazi). Token web Token web utilizzato per creare stanze pubbliche. È una stringa di 48 caratteri contenente solo lettere minuscole a-z. Rete @@ -686,12 +697,14 @@ L\'integrità dei contenuti non è stata validata La tua ROM è criptata - dump delle tue cartucce di giocooppure dei titoli già installati.]]> + schede di gioco o titoli digitali.]]> prod.keys sia installato in modo che i giochi possano essere decrittati.]]> È stato riscontrato un errore nell\'inizializzazione del core video Questo è causato solitamente dal driver incompatibile di una GPU. L\'installazione di driver GPU personalizzati potrebbe risolvere questo problema. Impossibile caricare la ROM Il file della ROM non esiste + Il gioco richiede firmware + dumpa e installa il firmware, o premi "OK" per continuare comunque.]]> Arresta emulazione 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 c8d23cb6b9..3a9f55e883 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml @@ -62,24 +62,25 @@ ファームウェアバージョンを表示 インストールされているファームウェアのバージョンを表示します - + GPU拡張機能 レンダラー RAII Vulkanにおける自動リソース管理の方法で、不要になったリソースを適切に解放しますが、バンドルされたゲームでクラッシュを引き起こす可能性があります。 - その他 + CPUとメモリ エデンのベール パフォーマンスと機能を向上させる実験的な設定。これらの設定は黒画面やその他のゲームの問題を引き起こす可能性があります。 実験的設定 - Eden\'s Veil内の設定は非常に実験的で問題を引き起こす可能性があります。ゲームが起動しない場合は、すべての拡張機能を無効にし、Extended Dynamic Stateを0に変更してください。 + エデンのベールに含まれる設定は非常に実験的であり、問題を引き起こす可能性があります。ゲームが起動しない場合は、拡張機能を無効にしてください。 WIP: フレームスキップ フレームスキップを切り替えて、レンダリングされるフレーム数を減らしパフォーマンスを向上させます。この機能は開発中であり、今後のリリースで有効になります。 高度なフレーム同期 フレーム間のタイミングを同期させることで、スムーズで一貫したフレーム配信を確保し、カクつきや不均一なアニメーションを軽減します。フレームタイミングの不安定さやマイクロスタッターが発生するゲームに最適です。 LRUキャッシュを有効化 - LRUキャッシュ(最近最も使われていない)を有効または無効にし、CPU使用率を節約してパフォーマンスを向上させます。一部のゲーム(特にTotK 1.2.1)で問題が発生する可能性があるため、ゲームが起動しないかランダムにクラッシュする場合は無効にしてください。 + LRUキャッシュを有効/無効にし、CPUプロセスの使用を節約してパフォーマンスを向上させます。TotK 1.2.1など一部のゲームで問題が発生する可能性があるため、ゲームが起動しない場合やランダムにクラッシュする場合は無効にしてください。 拡張ダイナミックステート - Vulkan機能を有効にし、パフォーマンスとレンダリングを向上させ、パイプライン作成時のリソースを節約しながらCPU/GPU使用率を低く保ちます。これらの拡張機能はデバイスの温度を上昇させる可能性があり、旧世代のA6XXシリーズのGPUは正しく動作しない場合があります。レガシーエミュレートフォーマットを使用するには0に設定してください。 + Vulkan機能を有効にし、パイプライン作成時のCPU/GPU使用率を低く保ちながら、パフォーマンス、レンダリング、リソース節約を改善します。これらの拡張機能はデバイスの温度を上昇させる可能性があり、旧A6XXシリーズのGPUは正しく反応しない場合があります。スケールフォーマットをエミュレートするには無効にしてください。 + 無効 コア速度の同期 コアの速度を最大速度パーセンテージに同期させ、ゲームの実際の速度を変えずにパフォーマンスを向上させます。 自動スタブを使用 @@ -184,18 +185,18 @@ ルーム参加に失敗 名前が短すぎます 無効なアドレス - 無効なポート! + 無効なポート ルームを退出 ネットワークエラー 接続が切断されました - 名前が重複しています + ユーザー名が既に使用されています MACアドレスが重複 コンソールIDが重複 バージョンが異なります パスワードが間違っています 接続できませんでした ルームが満員です - ホストが禁止されています + このルームから追放されました 権限がありません ユーザーが存在しません 既にルームに参加しています @@ -255,7 +256,8 @@ 3〜20文字で入力してください 必須 無効なIP形式 - Deve essere lungo 4–20 caratteri + 4~20文字の英数字、ピリオド、ハイフン、アンダースコア、スペースのみ使用可能 + 無効なユーザー名です。システム→ネットワークで正しく設定されていることを確認してください 1〜65535の間で入力してください 48文字で、小文字のa-zのみ使用可能 ロビータイプ @@ -411,6 +413,15 @@ すべてのインストール済みコンテンツの整合性を確認 暗号化キーが不足 ファームウェアと製品版ゲームを復号化できません + ファームウェアが無効 + 一部のゲームとアプリケーションを実行するにはファームウェアが必要です。Edenは19.0.1以前のファームウェアでのみ動作します。 + ファームウェアは存在しますが読み取れません。復号キーを確認し、必要に応じてファームウェアを再ダンプしてください。 + ファームウェアが新しすぎるか読み取れません。Edenは19.0.1以前のファームウェアのみに対応しています。 + キーのインストールに失敗 + キーが正常にインストールされました + 1つ以上のキーのコピーに失敗しました。 + キーファイルの拡張子が.keysであることを確認し、再度お試しください。 + キーの初期化に失敗しました。ダンプツールが最新であることを確認し、キーを再ダンプしてください。 Qlaunch @@ -472,7 +483,7 @@ 生成 ウェブユーザー名 - マルチプレイヤーロビーに表示されるユーザー名。4〜20文字でなければなりません。 + マルチプレイヤールームに表示されるユーザー名。4~20文字の英数字、ハイフン、ピリオド、アンダースコア、スペースのみ使用可能。 ウェブトークン 公開ルームを作成するために使用されるウェブトークン。a-zの小文字のみを含む48文字の文字列です。 ネットワーク @@ -645,11 +656,14 @@ 整合性の確認に失敗しました! ROMが暗号化されています + ゲームカードまたはデジタルタイトルを再ダンプするにはガイドに従ってください。]]> prod.keys ファイルがインストールされていることを確認してください。]]> ビデオコアの初期化中にエラーが発生しました これは通常、互換性のないGPUドライバーが原因で発生します。 カスタムGPUドライバーをインストールすると、問題が解決する可能性があります。 ROMの読み込みに失敗しました ROMファイルが存在しません + ゲームにはファームウェアが必要です + ファームウェアをダンプしてインストールするか、"OK"を押して続行してください。]]> 終了 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 deb901fc3f..c8a6a04bdd 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -62,24 +62,25 @@ 펌웨어 버전 표시 설치된 펌웨어 버전을 표시합니다 - + GPU 확장 기능 렌더러 RAII Vulkan에서 자동 리소스 관리를 위한 방법으로, 더 이상 필요하지 않은 리소스를 적절히 해제하지만 번들된 게임에서 충돌을 일으킬 수 있습니다. - 기타 - 에덴의 장막 + CPU 및 메모리 + 에덴의 베일 성능 및 기능을 향상시키기 위한 실험적 설정. 이 설정은 검은 화면 또는 기타 게임 문제를 일으킬 수 있습니다. 실험적 설정 - Eden\'s Veil 내의 설정은 매우 실험적이며 문제를 일으킬 수 있습니다. 게임이 부팅되지 않으면 모든 확장 기능을 비활성화하고 Extended Dynamic State를 0으로 변경하십시오. + 에덴의 베일에 포함된 설정은 매우 실험적이며 문제가 발생할 수 있습니다. 게임이 시작되지 않으면 확장 기능을 비활성화하세요. 작업 중: 프레임 스킵 렌더링되는 프레임 수를 줄여 성능을 향상시키기 위해 프레임 스킵을 전환합니다. 이 기능은 현재 개발 중이며 향후 출시 버전에서 활성화될 예정입니다. 향상된 프레임 페이싱 프레임 간 타이밍을 동기화하여 부드럽고 일관된 프레임 전달을 보장하며, 끊김과 불균일한 애니메이션을 줄입니다. 프레임 타이밍 불안정이나 게임 플레이 중 미세 끊김이 발생하는 게임에 이상적입니다. LRU 캐시 사용 - LRU 캐시(최근 사용되지 않은 항목)를 활성화 또는 비활성화하여 CPU 사용량을 절약하고 성능을 향상시킵니다. 일부 게임(특히 TotK 1.2.1)에서 문제가 발생할 수 있으므로 게임이 부팅되지 않거나 무작위로 충돌하는 경우 비활성화하세요. + LRU 캐시를 활성화 또는 비활성화하여 CPU 프로세스 사용을 절약하고 성능을 향상시킵니다. TotK 1.2.1을 포함한 일부 게임에서 문제가 발생할 수 있으므로 게임이 부팅되지 않거나 무작위로 충돌하는 경우 비활성화하세요. 확장 동적 상태 - Vulkan 기능을 활성화하여 성능을 향상시키고, 렌더링을 개선하며, 파이프라인 생성 시 리소스를 절약하면서 CPU/GPU 사용량을 낮게 유지합니다. 이 확장 기능은 기기 온도를 높일 수 있으며, 이전 A6XX 시리즈 GPU는 제대로 작동하지 않을 수 있습니다. 레거시 에뮬레이션 형식을 사용하려면 0으로 설정하세요. + 파이프라인 생성 시 CPU/GPU 사용량을 낮게 유지하면서 성능, 렌더링 및 리소스 절약을 개선하기 위해 Vulkan 기능을 활성화합니다. 이 확장 기능은 장치 온도를 높일 수 있으며 이전 A6XX 라인에 속하는 GPU가 제대로 반응하지 않을 수 있습니다. 스케일된 형식을 에뮬레이트하려면 비활성화하세요. + 비활성화됨 코어 속도 동기화 코어 틱 속도를 최대 속도 백분율과 동기화하여 게임의 실제 속도를 변경하지 않고 성능을 향상시킵니다. 자동 스텁 사용 @@ -183,18 +184,18 @@ 방 참가 실패 이름이 너무 짧습니다 잘못된 주소 - 잘못된 포트! + 잘못된 포트 방 나가기 네트워크 오류 연결 끊김 - 이름 충돌 + 사용자 이름이 이미 사용 중입니다 MAC 주소 충돌 콘솔 ID 충돌 잘못된 버전 잘못된 비밀번호 연결할 수 없음 방이 가득 찼습니다 - 호스트 차단됨 + 이 방에서 차단되었습니다 권한 거부됨 해당 사용자 없음 이미 방에 참여 중 @@ -254,7 +255,8 @@ 3~20자여야 합니다 필수 잘못된 IP 형식 - 4~20자여야 합니다 + 4~20자의 영숫자, 마침표, 하이픈, 밑줄 및 공백만 포함해야 합니다 + 사용자 이름이 유효하지 않습니다. 시스템 → 네트워크에서 올바르게 설정되었는지 확인하세요 1~65535 사이여야 합니다 48자여야 하며, 소문자 a-z만 사용 가능 로비 유형 @@ -417,6 +419,15 @@ 전체 설치된 콘텐츠의 손상을 확인합니다. 암호화 키를 찾을 수 없음 펌웨어 및 패키지 게임을 해독할 수 없음 + 펌웨어가 유효하지 않음 + 일부 게임 및 애플리케이션 실행을 위해 펌웨어가 필요합니다. Eden은 19.0.1 이하 버전의 펌웨어에서만 작동합니다. + 펌웨어가 존재하지만 읽을 수 없습니다. 복호화 키를 확인하고 필요한 경우 펌웨어를 다시 덤프하세요. + 펌웨어가 너무 최신이거나 읽을 수 없습니다. Eden은 19.0.1 이전 버전의 펌웨어에서만 작동합니다. + 키 설치 실패 + 키가 성공적으로 설치됨 + 하나 이상의 키를 복사하지 못했습니다. + 키 파일에 .keys 확장자가 있는지 확인하고 다시 시도하세요. + 키 초기화에 실패했습니다. 덤프 도구가 최신 상태인지 확인하고 키를 다시 덤프하세요. Qlaunch @@ -482,7 +493,7 @@ 생성 웹 사용자 이름 - 멀티플레이어 로비에 표시될 사용자 이름. 4~20자여야 합니다. + 멀티플레이어 방에 표시되는 사용자 이름. 4~20자의 영숫자, 하이픈, 마침표, 밑줄 및 공백만 포함해야 합니다. 웹 토큰 공개 방을 만들기 위해 사용되는 웹 토큰. a-z 소문자만 포함된 48자 문자열입니다. 네트워크 @@ -713,12 +724,14 @@ 롬 파일이 암호화되어있음 - 게임 카트리지 또는 설치된 타이틀을 다시 덤프하세요.]]> + 게임 카드 또는 디지털 타이틀을 다시 덤프하려면 가이드를 따르세요.]]> prod.keys 파일이 설치되어 있는지 확인하세요.]]> 비디오 코어를 초기화하는 동안 오류 발생 일반적으로 이 문제는 호환되지 않는 GPU 드라이버로 인해 발생합니다. 사용자 지정 GPU 드라이버를 설치하면 이 문제가 해결될 수 있습니다. 롬 파일을 불러올 수 없음 롬 파일이 존재하지 않음 + 게임에 펌웨어가 필요합니다 + 펌웨어를 덤프하여 설치하거나 "확인"을 눌러 계속 진행하세요.]]> 에뮬레이션 종료 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 80173a357d..287b2bd071 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml @@ -62,24 +62,25 @@ Vis firmwareversjon Viser den installerte firmwareversjonen - + GPU-utvidelser Renderer RAII En metode for automatisk ressurshåndtering i Vulkan som sikrer riktig frigjøring av ressurser når de ikke lenger trengs, men kan føre til krasj i bundlede spill. - Diverse + CPU og minne Edens slør Eksperimentelle innstillinger for å forbedre ytelse og funksjonalitet. Disse innstillingene kan forårsake svarte skjermer eller andre spillproblemer. Eksperimentelle innstillinger - Innstillingene i Eden\'s Veil er svært eksperimentelle og kan forårsake problemer. Hvis spillet ditt ikke starter, deaktiver alle utvidelser og endre Extended Dynamic State til 0. + Innstillingene i Edens slør er svært eksperimentelle og kan forårsake problemer. Hvis spillet ikke starter, deaktiver alle utvidelser. WIP: Hoppe over bilder Slå av/på frame skipping for å forbedre ytelsen ved å redusere antall renderte bilder. Denne funksjonen er fortsatt under utvikling og vil bli aktivert i fremtidige versjoner. Avansert bildevindu-synkronisering Sikrer jevn og konsekvent bildelevering ved å synkronisere tiden mellom bilder, noe som reduserer hakking og ujevn animasjon. Ideelt for spill som opplever ustabil bildetid eller mikro-hakk under spilling. Aktiver LRU-mellomlager - Aktiver eller deaktiver LRU-mellomlager (Least Recently Used), som forbedrer ytelsen ved å spare CPU-bruk. Noen spill har problemer med dette, spesielt TotK 1.2.1, så deaktiver hvis spillet ikke starter eller krasjer tilfeldig. + Aktiver eller deaktiver LRU-mellomlager for å forbedre ytelsen ved å spare CPU-prosessorbruk. Noen spill som TotK 1.2.1 har problemer med dette - deaktiver hvis spillet ikke starter eller krasjer tilfeldig. Utvidet dynamisk tilstand - Aktiverer Vulkan-funksjoner for å forbedre ytelse, rendering og spare ressurser ved oppretting av pipeline samtidig som CPU/GPU-bruken holdes lav. Disse utvidelsene kan øke enhetstemperaturen, og eldre GPU-er fra A6XX-serien fungerer kanskje ikke riktig. Sett til 0 for å bruke eldre emulerte formater. + Aktiverer Vulkan-funksjoner for å forbedre ytelse, rendering og spare ressurser ved oppretting av pipeline samtidig som CPU/GPU-bruken holdes lav. Disse utvidelsene kan øke enhetstemperaturen, og eldre GPU-er fra A6XX-serien reagerer kanskje ikke ordentlig. Deaktiver for å emulere skalerte formater. + Deaktivert Synkroniser kjernespeed Synkroniser kjernens hastighet med maksimal hastighetsprosent for å forbedre ytelsen uten å endre spillets faktiske hastighet. Bruk Auto Stub @@ -184,18 +185,18 @@ Kunne ikke bli med i rommet Navn for kort Ugyldig adresse - Ugyldig port! + Ugyldig port Forlat rom Nettverksfeil Mistet tilkobling - Navnekollisjon + Brukernavnet er allerede tatt MAC-kollisjon Konsoll-ID-kollisjon Feil versjon Feil passord Kunne ikke koble til Rommet er fullt - Vert utestengt + Du er utestengt fra dette rommet Tilgang nektet Ingen slik bruker Allerede i rommet @@ -255,7 +256,8 @@ Må være 3-20 tegn Påkrevd Ugyldig IP-format - Må være mellom 4–20 tegn + Må være mellom 4-20 tegn (kun alfanumeriske tegn, punktum, bindestreker, understreker og mellomrom) + Ugyldig brukernavn, kontroller at det er riktig satt under System → Nettverk Må være mellom 1-65535 Må være 48 tegn, og kun små bokstaver a-z Lobbytype @@ -410,6 +412,15 @@ Sjekk for korrupsjon Nøkler mangler Kan ikke dekryptere firmware/spill + Ugyldig fastvare + Fastvare kreves for å kjøre enkelte spill og applikasjoner. Eden fungerer bare med fastvare 19.0.1 eller eldre. + Fastvare er til stede, men kan ikke leses. Sjekk dekrypteringsnøklene og dump fastvaren på nytt om nødvendig. + Fastvaren er for ny eller kan ikke leses. Eden fungerer bare med fastvare 19.0.1 eller eldre. + Kunne ikke installere nøkler + Nøklene ble installert + En eller flere nøkler kunne ikke kopieres. + Kontroller at nøkkelfilen din har filendelsen .keys og prøv på nytt. + Kunne ikke initialisere nøkler. Sjekk at dumpverktøyene dine er oppdaterte og dump nøklene på nytt. Qlaunch @@ -463,7 +474,7 @@ Generer Webbrukernavn - Brukernavn som vises i flerspillerlobbyer. Må være 4–20 tegn langt. + Brukernavn som vises i flerspillerrom. Må være mellom 4-20 tegn (kun alfanumeriske tegn, bindestreker, punktum, understreker og mellomrom). Nett-token Nett-token som brukes til å opprette offentlige rom. Det er en 48-tegns streng som kun inneholder små bokstaver a-z. Nettverk @@ -636,11 +647,14 @@ Integritetsverifisering mislyktes! ROM-en din er kryptert + spillkort eller digitale titler på nytt.]]> prod.keys filen er installert slik at spillene kan dekrypteres.]]> Det oppstod en feil ved initialisering av videokjernen Dette skyldes vanligvis en inkompatibel GPU-driver. Installering av en tilpasset GPU-driver kan løse problemet. Kunne ikke laste inn ROM ROM-filen finnes ikke + Spillet krever fastvare + dump og installer fastvare, eller trykk "OK" for å fortsette likevel.]]> Avslutt emulering 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 4742f795c8..ab4a7b2704 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -62,24 +62,25 @@ Pokaż wersję firmware Wyświetla zainstalowaną wersję firmware - + Rozszerzenia GPU Renderer RAII Metoda automatycznego zarządzania zasobami w Vulkanie, która zapewnia prawidłowe zwalnianie zasobów, gdy nie są już potrzebne, ale może powodować awarie w pakietowych grach. - Różne + Procesor i pamięć Zasłona Edenu Eksperymentalne ustawienia poprawiające wydajność i możliwości. Te ustawienia mogą powodować czarne ekrany lub inne problemy z grą. Ustawienia eksperymentalne - Ustawienia zawarte w Eden\'s Veil są wysoce eksperymentalne i mogą powodować problemy. Jeśli gra się nie uruchamia, wyłącz wszystkie rozszerzenia i zmień Extended Dynamic State na 0. + Ustawienia w Zasłona Edenu są wysoce eksperymentalne i mogą powodować problemy. Jeśli gra się nie uruchamia, wyłącz wszystkie rozszerzenia. WIP: Pomijanie klatek Włącz lub wyłącz pomijanie klatek, aby poprawić wydajność poprzez zmniejszenie liczby renderowanych klatek. Ta funkcja jest wciąż w fazie rozwoju i zostanie włączona w przyszłych wersjach. Zaawansowana synchronizacja klatek Zapewnia płynne i spójne wyświetlanie klatek poprzez synchronizację ich czasu, redukując zacinanie i nierówną animację. Idealne dla gier z niestabilnym czasem klatek lub mikro-zacinaniem podczas rozgrywki. Włącz pamięć podręczną LRU - Włącz lub wyłącz pamięć podręczną LRU (najrzadziej używane), zwiększając wydajność poprzez oszczędzanie użycia procesora. Niektóre gry mają z nią problemy, szczególnie TotK 1.2.1, więc wyłącz, jeśli gra się nie uruchamia lub losowo się zawiesza. + Włącz lub wyłącz pamięć podręczną LRU, aby poprawić wydajność poprzez zmniejszenie użycia procesora. Niektóre gry, takie jak TotK 1.2.1, mogą mieć problemy - wyłącz, jeśli gra się nie uruchamia lub losowo zawiesza. Rozszerzony stan dynamiczny - Włącza funkcje Vulkan w celu poprawy wydajności, renderowania i oszczędzania zasobów podczas tworzenia potoku przy zachowaniu niskiego wykorzystania CPU/GPU. Te rozszerzenia mogą zwiększać temperaturę urządzenia, a starsze GPU z serii A6XX mogą nie działać poprawnie. Ustaw na 0, aby używać emulowanych formatów starszej wersji. + Włącza funkcje Vulkan w celu poprawy wydajności, renderowania i oszczędzania zasobów podczas tworzenia potoku, przy jednoczesnym utrzymaniu niskiego wykorzystania CPU/GPU. Te rozszerzenia mogą zwiększać temperaturę urządzenia, a starsze GPU z linii A6XX mogą nie działać poprawnie. Wyłącz, aby emulować formaty skalowane. + Wyłączone Synchronizuj prędkość rdzenia Synchronizuje prędkość rdzenia z maksymalnym procentem prędkości, aby poprawić wydajność bez zmiany rzeczywistej prędkości gry. Użyj Auto Stub @@ -184,18 +185,18 @@ Nie udało się dołączyć do pokoju Nazwa jest za krótka Nieprawidłowy adres - Nieprawidłowy port! + Nieprawidłowy port Opuść pokój Błąd sieci Utracono połączenie - Konflikt nazw + Nazwa użytkownika jest już zajęta Konflikt adresu MAC Konflikt ID konsoli Nieprawidłowa wersja Nieprawidłowe hasło Nie można się połączyć Pokój jest pełny - Gospodarz zbanowany + Masz zakaz wstępu do tego pokoju Odmowa dostępu Nie ma takiego użytkownika Już jesteś w pokoju @@ -255,7 +256,8 @@ 3-20 znaków wymagane Wymagane Nieprawidłowy format IP - Musi mieć 4–20 znaków + Musi zawierać 4-20 znaków (tylko znaki alfanumeryczne, kropki, myślniki, podkreślenia i spacje) + Nieprawidłowa nazwa użytkownika, sprawdź w System → Sieć Musi być między 1-65535 Musi mieć 48 znaków i składać się tylko z małych liter a-z Typ lobby @@ -410,6 +412,15 @@ Sprawdza integralność zainstalowanych plików. Brak kluczy Firmware i gry nie mogą być odszyfrowane. + Nieprawidłowe oprogramowanie sprzętowe + Firmware jest wymagany do uruchamiania niektórych gier i aplikacji. Eden działa tylko z firmwarem 19.0.1 lub starszym. + Oprogramowanie sprzętowe jest obecne, ale nie można go odczytać. Sprawdź klucze deszyfrujące i w razie potrzeby zrzuć oprogramowanie ponownie. + Oprogramowanie sprzętowe jest zbyt nowe lub nie można go odczytać. Eden działa tylko z oprogramowaniem sprzętowym w wersji 19.0.1 lub starszej. + Nie udało się zainstalować kluczy + Pomyślnie zainstalowano klucze + Nie udało się skopiować jednego lub więcej kluczy. + Upewnij się, że plik kluczy ma rozszerzenie .keys i spróbuj ponownie. + Nie udało się zainicjować kluczy. Upewnij się, że twoje narzędzia do zrzucania są aktualne i zrzuć klucze ponownie. Qlaunch @@ -463,7 +474,7 @@ Generuj Nazwa użytkownika w sieci - Nazwa użytkownika wyświetlana w lobby multiplayer. Musi mieć 4–20 znaków. + Nazwa użytkownika wyświetlana w pokojach multiplayer. Musi zawierać 4-20 znaków (tylko znaki alfanumeryczne, myślniki, kropki, podkreślenia i spacje). Token internetowy Token internetowy używany do tworzenia publicznych pokoi. Jest to 48-znakowy ciąg zawierający tylko małe litery a-z. Sieć @@ -634,11 +645,14 @@ Dodatki Twój ROM jest zakodowany + karty gry lub tytuły cyfrowe.]]> prod.keys jest zainstalowany aby gry mogły zostać odczytane.]]> Błąd inicjacji podsystemu graficznego Zazwyczaj spowodowane niekompatybilnym sterownikiem GPU, instalacja niestandardowego sterownika może rozwiązać ten problem. Nie można wczytać pliku ROM Plik ROM nie istnieje + Gra wymaga oprogramowania sprzętowego + zrzuć i zainstaluj oprogramowanie sprzętowe, lub naciśnij "OK", aby kontynuować mimo to.]]> Zakończ emulację 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 a886450e47..ee83c34c79 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 @@ -62,24 +62,25 @@ Mostrar versão do firmware Exibe a versão do firmware instalado - + Extensões da GPU Renderizador RAII Um método de gerenciamento automático de recursos no Vulkan que garante a liberação adequada de recursos quando não são mais necessários, mas pode causar falhas em jogos empacotados. - Diversos + CPU e Memória Véu do Éden Configurações experimentais para melhorar desempenho e capacidade. Essas configurações podem causar telas pretas ou outros problemas no jogo. Configurações experimentais - As configurações contidas no Eden\'s Veil são altamente experimentais e podem causar problemas. Se o jogo não iniciar, desative todas as extensões e altere o Extended Dynamic State para 0. + As configurações no Véu do Éden são altamente experimentais e podem causar problemas. Se seu jogo não iniciar, desative todas as extensões. WIP: Pular quadros Ative ou desative o pulo de quadros para melhorar o desempenho reduzindo o número de quadros renderizados. Este recurso ainda está em desenvolvimento e será habilitado em versões futuras. Sincronização avançada de quadros Garante entrega suave e consistente de quadros sincronizando seu tempo, reduzindo engasgos e animações irregulares. Ideal para jogos com instabilidade no tempo de quadros ou micro-engasgos durante a jogatina. Ativar cache LRU - Ative ou desative o cache LRU (Least Recently Used), aumentando o desempenho ao economizar uso da CPU. Alguns jogos têm problemas com ele, especialmente TotK 1.2.1, então desative se o jogo não iniciar ou travar aleatoriamente. + Ative ou desative o cache LRU para melhorar o desempenho economizando uso do processador. Alguns jogos como TotK 1.2.1 têm problemas - desative se o jogo não iniciar ou travar aleatoriamente. Estado Dinâmico Estendido - Ativa recursos do Vulkan para melhorar desempenho, renderização e economizar recursos na criação de pipeline, mantendo o uso baixo de CPU/GPU. Essas extensões podem aumentar a temperatura do dispositivo, e GPUs da linha A6XX mais antiga podem não funcionar corretamente. Defina como 0 para usar formatos emulados legados. + Ativa recursos Vulkan para melhorar desempenho, renderização e economizar recursos na criação de pipelines mantendo baixo uso de CPU/GPU. Essas extensões podem aumentar a temperatura do dispositivo e GPUs da linha A6XX mais antigos podem não responder adequadamente. Desative para emular formatos dimensionados. + Desativado Sincronizar velocidade do núcleo Sincroniza a velocidade do núcleo com a porcentagem máxima de velocidade para melhorar o desempenho sem alterar a velocidade real do jogo. Usar Auto Stub @@ -184,18 +185,18 @@ Falha ao entrar na sala Nome muito curto Endereço inválido - Porta inválida! + Porta inválida Sair da sala Erro de rede Conexão perdida - Conflito de nome + Nome de usuário já em uso Conflito de MAC Conflito de ID do console Versão incorreta Senha incorreta Não foi possível conectar Sala cheia - Host banido + Você está banido desta sala Permissão negada Usuário não existe Já está na sala @@ -255,7 +256,8 @@ 3-20 caracteres necessários Obrigatório Formato de IP inválido - Deve ter entre 4–20 caracteres + Deve ter 4-20 caracteres (apenas alfanuméricos, pontos, hífens, sublinhados e espaços) + Nome de usuário inválido, verifique em Sistema → Rede Deve ser entre 1-65535 Deve ter 48 caracteres e apenas letras minúsculas a-z Tipo de lobby @@ -422,6 +424,15 @@ Verifica todo o conteúdo instalado em busca de dados corrompidos Faltando chaves de encriptação O firmware e jogos comerciais não poderão ser decriptados + Firmware inválido + Firmware é necessário para executar certos jogos e aplicativos. Eden só funciona com firmware 19.0.1 ou anterior. + Firmware está presente mas não pode ser lido. Verifique chaves de descriptografia e refaça o dump do firmware se necessário. + Firmware é muito novo ou ilegível. Eden só funciona com firmware 19.0.1 ou anterior. + Falha ao instalar chaves + Chaves instaladas com sucesso + Uma ou mais chaves falharam ao copiar. + Verifique se seu arquivo de chaves tem extensão .keys e tente novamente. + Falha ao inicializar chaves. Verifique se suas ferramentas de dump estão atualizadas e refaça o dump das chaves. Qlaunch @@ -487,7 +498,7 @@ Gerar Nome de usuário web - Nome de usuário a ser exibido nos lobbies multiplayer. Deve ter 4–20 caracteres. + Nome de usuário exibido em salas multiplayer. Deve ter 4-20 caracteres (apenas alfanuméricos, hífens, pontos, sublinhados e espaços). Token web Token web usado para criar salas públicas. É uma string de 48 caracteres contendo apenas letras minúsculas a-z. Rede @@ -770,12 +781,14 @@ uma tentativa de mapeamento automático Sua ROM está encriptada - cartuchos de jogos ou títulos instalados.]]> + cartões de jogo ou títulos digitais.]]> prod.keys está instalado para que os jogos possam ser decriptados.]]> Ocorreu um erro ao iniciar o núcleo de vídeo. Isto é normalmente causado por um driver de GPU incompatível. Instalar um driver de GPU personalizado pode resolver este problema. Impossível carregar a ROM O arquivo ROM não existe + O jogo requer firmware + faça dump e instale o firmware, ou pressione "OK" para continuar mesmo assim.]]> Sair da emulação 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 caf7090993..896f72cacb 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 @@ -62,24 +62,25 @@ Mostrar versão do firmware Mostra a versão do firmware instalado - + Extensões da GPU Renderizador RAII Um método de gestão automática de recursos no Vulkan que garante a libertação adequada de recursos quando já não são necessários, mas pode causar falhas em jogos empacotados. - Diversos + CPU e Memória Véu do Éden Definições experimentais para melhorar o desempenho e capacidade. Estas definições podem causar ecrãs pretos ou outros problemas no jogo. Definições experimentais - As definições contidas no Eden\'s Veil são altamente experimentais e podem causar problemas. Se o jogo não iniciar, desative todas as extensões e altere o Extended Dynamic State para 0. + As configurações no Véu do Éden são altamente experimentais e podem causar problemas. Se o jogo não iniciar, desative todas as extensões. WIP: Saltar frames Ative ou desative o salto de frames para melhorar o desempenho reduzindo o número de frames renderizados. Esta funcionalidade ainda está em desenvolvimento e será ativada em versões futuras. Sincronização avançada de frames Garante uma entrega suave e consistente de frames sincronizando o seu tempo, reduzindo engasgadelas e animações irregulares. Ideal para jogos que experienciam instabilidade no tempo de frames ou micro-engasgadelas durante o jogo. Ativar cache LRU - Ative ou desative a cache LRU (Least Recently Used), aumentando o desempenho ao poupar utilização da CPU. Alguns jogos têm problemas com ela, especialmente TotK 1.2.1, por isso desative se o jogo não arrancar ou falhar aleatoriamente. + Ative ou desative a cache LRU para melhorar desempenho poupando uso do processador. Alguns jogos como TotK 1.2.1 têm problemas - desative se o jogo não iniciar ou falhar aleatoriamente. Estado Dinâmico Estendido - Ativa funcionalidades Vulkan para melhorar o desempenho, renderização e poupar recursos na criação de pipeline, mantendo uma utilização reduzida da CPU/GPU. Estas extensões podem aumentar a temperatura do dispositivo e as GPUs da antiga linha A6XX podem não funcionar corretamente. Defina como 0 para utilizar formatos emulados legados. + Ativa funcionalidades Vulkan para melhorar desempenho, renderização e poupar recursos na criação de pipelines mantendo baixa utilização de CPU/GPU. Estas extensões podem aumentar a temperatura do dispositivo e GPUs da linha A6XX mais antigos podem não responder corretamente. Desative para emular formatos dimensionados. + Desativado Sincronizar velocidade do núcleo Sincroniza a velocidade do núcleo com a percentagem máxima de velocidade para melhorar o desempenho sem alterar a velocidade real do jogo. Usar Auto Stub @@ -184,18 +185,18 @@ Falha ao entrar na sala Nome muito curto Endereço inválido - Porta inválida! + Porto inválido Sair da sala Erro de rede Conexão perdida - Conflito de nomes + Nome de utilizador já em uso Conflito de endereço MAC Conflito de ID da consola Versão incorreta Palavra-passe incorreta Não foi possível conectar Sala cheia - Anfitrião banido + Está banido desta sala Permissão negada Utilizador não existe Já está na sala @@ -255,7 +256,8 @@ 3-20 caracteres necessários Obrigatório Formato de IP inválido - Deve ter entre 4–20 caracteres + Deve ter 4-20 caracteres (apenas alfanuméricos, pontos, hífens, underscores e espaços) + Nome de utilizador inválido, verifique em Sistema → Rede Deve ser entre 1-65535 Deve ter 48 caracteres e apenas letras minúsculas a-z Tipo de lobby @@ -422,6 +424,15 @@ Verifica todo o conteúdo instalado em busca de dados corrompidos Faltando chaves de encriptação O firmware e jogos comerciais não poderão ser decriptados + Firmware inválido + É necessário firmware para executar alguns jogos e aplicações. O Eden só funciona com firmware 19.0.1 ou anterior. + Firmware está presente mas não pode ser lido. Verifique chaves de descriptografia e volte a fazer dump do firmware se necessário. + Firmware é demasiado recente ou ilegível. O Eden só funciona com firmware 19.0.1 ou anterior. + Falha ao instalar chaves + Chaves instaladas com sucesso + Uma ou mais chaves falharam ao copiar. + Verifique se o seu ficheiro de chaves tem extensão .keys e tente novamente. + Falha ao inicializar chaves. Verifique se as suas ferramentas de dump estão atualizadas e volte a fazer dump das chaves. Qlaunch @@ -487,7 +498,7 @@ Gerar Nome de utilizador web - Nome de utilizador a ser exibido nos lobbies multiplayer. Deve ter entre 4–20 caracteres. + Nome de utilizador exibido em salas multiplayer. Deve ter 4-20 caracteres (apenas alfanuméricos, hífens, pontos, underscores e espaços). Token web Token web usado para criar salas públicas. É uma string de 48 caracteres contendo apenas letras minúsculas a-z. Rede @@ -770,12 +781,14 @@ uma tentativa de mapeamento automático A tua ROM está encriptada - cartucho de jogo or títulos instalados.]]> + cartões de jogo ou títulos digitais.]]> prod.keys está instalado para que os jogos possam ser desencriptados.]]> Ocorreu um erro ao iniciar o núcleo de vídeo. Isto é normalmente causado por um driver de GPU incompatível. Instalar um driver GPU pode resolver este problema. Impossível carregar a tua ROM O ficheiro da ROM não existe + O jogo requer firmware + faça dump e instale o firmware, ou pressione "OK" para continuar mesmo assim.]]> Parar emulação 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 3d8371324f..19d18e13d7 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -55,21 +55,21 @@ Показать модель устройства Отобразить модель хостового устройства - Показать модель GPU - Отобразить модель хостовой GPU + Показать модель ГПУ + Отобразить модель хостовой ГПУ Показать модель SoC Отобразить модель хостового SoC Показать версию прошивки Отображает установленную версию прошивки - - Расширения GPU - Рендерер - Разное + + Расширения ГПУ + Рендеринг + Процессор и память Покров Эдема Экспериментальные настройки для улучшения производительности и возможностей. Эти настройки могут вызывать черные экраны или другие проблемы в игре. Экспериментальные настройки - Настройки в Eden\'s Veil являются экспериментальными и могут вызывать проблемы. Если ваша игра не запускается, отключите все расширения и установите расширенное динамическое состояние на 0. + Настройки в Покров Эдема являются экспериментальными и могут вызывать проблемы. Если ваша игра не запускается, отключите все расширения. В разработке: Пропуск кадров Включите или отключите пропуск кадров для повышения производительности за счет уменьшения количества отображаемых кадров. Эта функция находится в разработке и будет включена в будущих версиях. RAII @@ -77,9 +77,10 @@ Улучшенная синхронизация кадров Обеспечивает плавную и стабильную подачу кадров за счет синхронизации их времени, уменьшая подтормаживания и неравномерную анимацию. Идеально для игр с нестабильным временем кадров или микро-подтормаживаниями во время игры. Включить LRU-кеш - Включите или отключите кэш LRU (наименее недавно использованный), что повышает производительность за счет экономии использования ЦП. Некоторые игры имеют проблемы с ним, особенно TotK 1.2.1, поэтому отключите, если игра не запускается или случайно вылетает. + Включите или отключите кэш LRU (наименее недавно использованный), повышая производительность за счёт снижения нагрузки на ЦП. Некоторые игры могут работать с ним некорректно (например, TotK 1.2.1), поэтому отключите, если игра не запускается или случайно вылетает. Расширенное динамическое состояние - Активирует расширения Vulkan для оптимизации производительности, улучшения рендеринга и снижения нагрузки при создании конвейера с сохранением низкого энергопотребления CPU/GPU. Может привести к повышению температуры устройства. На GPU серии A6XX возможны некорректные работы. Установите 0 для использования эмулируемых форматов (Legacy). + Включает функции Vulkan для улучшения производительности, рендеринга и экономии ресурсов при создании конвейера, сохраняя низкое использование ЦП/ГПУ. Эти расширения могут увеличить температуру устройства, а ГПУ из старой линейки A6XX могут работать некорректно. Отключите для эмуляции масштабируемых форматов. + Отключено Синхронизация скорости ядра Синхронизирует скорость ядра с максимальным процентом скорости для улучшения производительности без изменения фактической скорости игры. Использовать Auto Stub @@ -89,12 +90,12 @@ Удаление прошивки... Прошивка успешно удалена Определяющая вершина - Улучшает освещение и обработку вершин в некоторых играх. Поддерживается только GPU с Vulkan 1.0+. + Улучшает освещение и обработку вершин в некоторых играх. Поддерживается только ГПУ с Vulkan 1.0+. Индексирование дескрипторов - Улучшает обработку текстур и буферов, а также слой перевода Maxwell. Поддерживается некоторыми GPU Vulkan 1.1 и всеми GPU Vulkan 1.2+. - Использовать быстрое время GPU + Улучшает обработку текстур и буферов, а также слой перевода Maxwell. Поддерживается некоторыми ГПУ Vulkan 1.1 и всеми ГПУ Vulkan 1.2+. + Использовать быстрое время ГПУ Заставляет большинство игр работать в максимальном нативном разрешении. Эта опция может вызывать проблемы. - Фактор разгона GPU + Фактор разгона ГПУ Используйте 128 для максимальной производительности и 512 для максимальной графической точности. Низкий (128) Средний (256) @@ -104,19 +105,19 @@ Цельсий Фаренгейт - Быстрое время CPU + Быстрое время ЦП Заставляет эмулируемый процессор работать на более высокой частоте, уменьшая некоторые ограничители FPS. Эта опция нестабильна и может вызывать проблемы, а более слабые процессоры могут показывать снижение производительности. - Тактовая частота CPU + Тактовая частота ЦП Используйте Разгон (1700MHz) для работы на максимальной нативной частоте Switch или Быстрая (2000MHz) для работы на удвоенной частоте. Распределение памяти (ЭКСПЕРИМЕНТАЛЬНО) Изменяет эмулируемое распределение памяти. Эта настройка не увеличивает производительность, но может помочь в играх, использующих высокие разрешения через моды. Не используйте на телефонах с 8 ГБ ОЗУ или меньше. Сэмпловый шейдинг Позволяет шейдеру фрагментов выполняться на каждый сэмпл в мультисэмпловом фрагменте вместо одного раза на фрагмент. Улучшает качество графики ценой производительности. Только устройства с Vulkan 1.1+ поддерживают это расширение. - Пользовательские такты CPU - Установите пользовательское значение тактов CPU. Более высокие значения могут увеличить производительность, но также могут вызвать зависание игры. Рекомендуется диапазон 77–21000. + Пользовательские такты ЦП + Установите пользовательское значение тактов ЦП. Более высокие значения могут увеличить производительность, но также могут вызвать зависание игры. Рекомендуется диапазон 77–21000. Такты - Пропустить внутреннюю инвалидацию CPU - Пропускает некоторые инвалидации кэша на стороне CPU при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх. + Пропустить внутреннюю инвалидацию ЦП + Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх. 4 ГБ (Рекомендуется) 6 ГБ (Небезопасно) @@ -128,10 +129,10 @@ Метод декодирования ASTC - Выберите способ декодирования сжатых текстур ASTC для рендеринга: CPU (медленно, безопасно), GPU (быстро, рекомендуется) или CPU Async (без заиканий, могут возникнуть проблемы) - CPU - GPU - CPU (асинхронно) + Выберите способ декодирования сжатых текстур ASTC для рендеринга: ЦП (медленно, безопасно), ГПУ (быстро, рекомендуется) или ЦП асинхронно (без заиканий, могут возникнуть проблемы) + ЦП + ГПУ + ЦП (асинхронно) Метод пережатия ASTC @@ -156,13 +157,13 @@ Эмуляция NVDEC Обработка видео (ролики, интро) - CPU - GPU + ЦП + ГПУ Отключено Оптимизация Spir-V - Оптимизирует скомпилированный шейдер для повышения эффективности GPU. + Оптимизирует скомпилированный шейдер для повышения эффективности ГПУ. Никогда При загрузке Всегда @@ -184,18 +185,18 @@ Ошибка подключения Слишком короткое имя Неверный адрес - Неверный порт! + Неверный порт Выйти Ошибка сети Соединение потеряно - Имя занято + Имя пользователя уже занято Конфликт MAC-адреса Конфликт ID консоли Неверная версия Неверный пароль Не удалось подключиться Комната заполнена - Хост заблокирован + Вы забанены в этой комнате Доступ запрещен Пользователь не найден Уже в комнате @@ -255,7 +256,8 @@ 3-20 символов Обязательно Неверный IP - Должно быть от 4 до 20 символов + Должно быть от 4 до 20 символов и содержать только буквы, цифры, точки, дефисы, подчёркивания и пробелы + Неверное имя пользователя. Убедитесь, что оно правильно указано в Система -> Сеть 1-65535 Должно быть 48 символов и содержать только строчные буквы a-z Тип лобби @@ -424,6 +426,15 @@ Проверяет весь установленный контент на наличие повреждений Отсутствуют ключи шифрования Прошивка и розничные игры не могут быть расшифрованы + Прошивка недействительна + Для запуска некоторых игр и системных апплетов требуется прошивка. Eden работает только с прошивкой версии 19.0.1 и ниже. + Прошивка обнаружена, но не может быть прочитана. Проверьте наличие ключей дешифрования и при необходимости пересохраните прошивку. + Прошивка слишком новая или не может быть прочитана. Eden работает только с прошивкой версии 19.0.1 и ниже. + Ошибка установки ключей + Ключи успешно установлены + Не удалось скопировать один или несколько ключей. + Убедитесь, что файл ключей имеет расширение .keys, и попробуйте снова. + Не удалось инициализировать ключи. Проверьте, актуальны ли ваши инструменты для дампа, и пересохраните ключи. Qlaunch @@ -489,7 +500,7 @@ Создать Веб-имя пользователя - Имя пользователя, которое будет отображаться в мультиплеерных лобби. Должно содержать 4–20 символов. + Имя пользователя, отображаемое в мультиплеерных лобби. Должно быть от 4 до 20 символов и содержать только буквы, цифры, дефисы, точки, подчёркивания и пробелы. Веб-токен Веб-токен, используемый для создания публичных комнат. Это строка из 48 символов, содержащая только строчные буквы a-z. Сеть @@ -524,7 +535,7 @@ ЦП Отладка ЦП Переводит ЦП в режим медленной отладки. - Графический процессор + ГПУ API Отладка графики Переводит графический API в режим медленной отладки. @@ -679,7 +690,7 @@ Установка драйвера... - Получение драйверов GPU + Получение драйверов ГПУ Получить Ошибка при получении Проверьте соединение и попробуйте снова. @@ -701,9 +712,9 @@ Установка… Последний Рекомендуемый драйвер: - Модель GPU: - GPU не поддерживается - Ваш GPU не поддерживает замену драйверов. Установка сторонних драйверов не рекомендуется. + Модель ГПУ: + ГПУ не поддерживается + Ваш ГПУ не поддерживает замену драйверов. Установка сторонних драйверов не рекомендуется. Настройки @@ -771,12 +782,14 @@ Ваш ROM зашифрованный - или установленные игры.]]> + игровых картриджей или установленных игр.]]> prod.keys установлен, чтобы игры можно было расшифровать.]]> Произошла ошибка при инициализации видеоядра. Обычно это вызвано несовместимым драйвером ГП. Установка пользовательского драйвера ГП может решить эту проблему. Не удалось запустить ROM Файл ROM не существует + Игре требуется прошивка + сохраните и установите прошивку или нажмите "OK" для запуска в любом случае.]]> Выход из эмуляции diff --git a/src/android/app/src/main/res/values-sr/strings.xml b/src/android/app/src/main/res/values-sr/strings.xml index d133a470b2..b3d6e2a1ef 100644 --- a/src/android/app/src/main/res/values-sr/strings.xml +++ b/src/android/app/src/main/res/values-sr/strings.xml @@ -65,15 +65,16 @@ Прикажи верзију фирмвера Приказује инсталирану верзију фирмвера - + Еденова аукција Експериментална подешавања за побољшање перформанси и способности. Ова подешавања могу изазвати црне екране или друге проблеме у игри. Експериментална подешавања - Подешавања која се налазе у оквиру ЕДЕН-ова вела су високо експериментални и могу проузроковати проблеме. Ако се ваша игра не покрене, онемогућите било какве проширења и промените продужено динамичко стање на 0. + Подешавања која се налазе у оквиру ЕДЕН-ова вела су високо експериментални и могу проузроковати проблеме. Ако се ваша игра не покрене, онемогућите било какве проширења. ГПУ екстензије Проширена динамичка држава - Омогућава функције Вулкана да побољшају перформансе, приказивање и уштеде ресурсе за креирање на цевоводама, задржавајући нижи ЦПУ / ГПУ коришћење. Ови продужеци могу повећати температуру уређаја и ГПУ који припадају старијој А6кк линији не могу правилно реаговати. Подесите на 0 да бисте користили заоставштине емулираних формата. + Омогућава функције Вулкана да побољшају перформансе, приказивање и уштеде ресурсе за креирање на цевоводама, задржавајући нижи ЦПУ / ГПУ коришћење. Ови продужеци могу повећати температуру уређаја и ГПУ који припадају старијој А6кк линији не могу правилно реаговати. Искључите за емулацију скалираних формата. + Искључено Провоцирајући врх Побољшава осветљење и вертификат руковања у одређеним играма. Подржан само на Вулкану 1.0+ ГПУ-у. Индексирање дескриптора @@ -91,11 +92,11 @@ Деинсталирање фирмвера Фирмваре је успешно деинсталирано - Остало + CPU и меморија Синхронизујте брзину језгре Синхронизујте језгро Тицк Брзина брзине максималне брзине да бисте побољшали перформансе без измене стварне брзине игре. Омогући ЛРУ кеш - Омогућите или онемогућите најмање недавно коришћене (ЛРУ) кеш меморије, све веће перформансе штедећи употребу процеса ЦПУ-а. Неке игре са њом имају проблем, посебно тотк 1.2.1, тако да онемогућите ако се игра не покрене или се руши насумично. + Омогућите или онемогућите најмање недавно коришћене (ЛРУ) кеш меморије, све веће перформансе штедећи употребу процеса ЦПУ-а. Неке игре са њом имају проблеме, посебно тотк 1.2.1, тако да онемогућите ако се игра не покрене или се руши насумично. Брзо време ЦПУ-а Присиљава опомена ЦПУ-а како би се покренуло на вишим сатима, смањујући одређене ФПС ограничења. Ова опција је хакира и може проузроковати проблеме и слабији ЦПУ-ови могу видети смањене перформансе. ЦПУ сат @@ -145,18 +146,18 @@ Успешно се придружите собу! Није успело креирање собе! Није успело да се придружи соби! - Неважећи порт! + Неважећи порт Излазна соба Грешка у мрежи Изгубљена веза - Судар имена + Корисничко име је већ заузето Мац судар Судар конзоле Погрешна верзија Погрешна лозинка Није се могао повезати Соба је пуна - Домаћин забрањен + Забрањен вам је приступ овој соби Дозвола одбијена Нема таквог корисника Већ у соби @@ -217,7 +218,8 @@ Неприказано Име је прекратко Неисправна адреса - Мора бити између 4–20 знакова + Мора садржати 4-20 знакова (само слова, бројеве, тачке, цртице, доње црте и размаке) + Неисправно корисничко име, проверите у Систем → Мрежа Отказати У реду Освежити @@ -373,6 +375,15 @@ Проверава све инсталиране садржаје за корупцију Шифра о шифрирањима недостају Фирмваре и малопродајне игре не могу се дешифровати + Неисправан firmware + Firmware је потребан за покретање одређених игара и апликација. Eden ради само са firmware-ом 19.0.1 или старијим. + Firmware је присутан али се не може прочитати. Проверите кључеве за дешифровање и поново направите firmware дамп ако је потребно. + Firmware је превише нов или се не може прочитати. Eden ради само са firmware-ом 19.0.1 или старијим. + Неуспела инсталација кључева + Кључеви успешно инсталирани + Један или више кључева није успело да се копира. + Проверите да ли ваш фајл са кључевима има .keys екстензију и покушајте поново. + Неуспела иницијализација кључева. Проверите да ли су вам алати за прављење дампа ажурни и поново направите дамп кључева. @@ -438,7 +449,7 @@ Генериши Веб корисничко име - Корисничко име које ће бити приказано у мултиплејер лобијима. Мора бити дугачко 4–20 знакова. + Корисничко име које се приказује у мултиплејер собама. Мора садржати 4-20 знакова (само слова, бројеве, цртице, тачке, доње црте и размаке). Веб токен Веб токен који се користи за стварање јавних лобија. То је низ од 48 знакова који садржи само мала слова А-З. Мрежа @@ -733,6 +744,8 @@ То обично узрокује некомпатибилни ГПУ драјвер. Инсталирање прилагођеног ГПУ управљачког програма може решити овај проблем. Није могуће учитати РОМ РОМ датотека не постоји + Игра захтева firmware + направите дамп и инсталирајте firmware, или притисните "OK" да бисте наставили у сваком случају.]]> Излазни емулација 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 91b600eded..473dff349c 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -62,14 +62,14 @@ Показати версію прошивки Відображає встановлену версію прошивки - + Розширення GPU Рендеринг - Різне + Процесор і пам\'ять Завіса Eden Експериментальні налаштування для покращення продуктивності та сумісності. Ці налаштування можуть викликати збої, зокрема чорний екран. Експериментальні налаштування - Налаштування Завіси Eden (Eden\'s Veil) є експериментальними та можуть спричинити проблеми. Якщо ваша гра не запускається — вимкніть усі розширення та змініть \"Розширений динамічний стан\" на 0. + Налаштування Завіси Eden є експериментальними та можуть спричинити проблеми. Якщо ваша гра не запускається — вимкніть усі розширення. RAII Метод автоматичного керування ресурсами у Vulkan, який забезпечує правильне звільнення ресурсів після завершення їх використання, проте він може спричинити збої в ігрових збірниках. В розробці: Пропуск кадрів @@ -77,9 +77,10 @@ Покращена синхронізація кадрів Забезпечує плавну та стабільну подачу кадрів шляхом синхронізації їх часу, зменшуючи підвисання та нерівномірну анімацію. Ідеально для ігор з нестабільним часом кадрів або мікро-підвисаннями під час гри. Увімкнути LRU-кеш - Увімкніть або вимкніть кеш LRU (Least Recently Used), підвищуючи продуктивність за рахунок зменшення навантаження на CPU. Однак деякі ігри, як-от TotK версії 1.2.1, можуть працювати некоректно — у такому разі вимкніть цю опцію, якщо гра не запускається або раптово вилітає. + Увімкніть або вимкніть кеш LRU для покращення продуктивності шляхом зменшення використання процесора. Деякі ігри (зокрема TotK 1.2.1) мають проблеми - вимкніть, якщо гра не запускається або випадково зникає. Розширений динамічний стан - Активує функції Vulkan для покращення продуктивності, поліпшеня рендерингу та економії ресурсів під час створення конвеєрів (pepeline), зберігаючи низьке використання CPU/GPU. Ці розширення можуть підвищити температуру пристрою, а старі GPU серії A6XX можуть реагувати некоректно. Встановіть 0, щоб увімкнути використання емуляції попередніх (Legacy) форматів. + Активує функції Vulkan для покращення продуктивності, поліпшеня рендерингу та економії ресурсів під час створення конвеєрів (pepeline), зберігаючи низьке використання CPU/GPU. Ці розширення можуть підвищити температуру пристрою, а старі GPU серії A6XX можуть реагувати некоректно. Вимкніть для емуляції масштабованих форматів. + Вимкнено Синхронізувати швидкість ядра Синхронізує швидкість ядра з максимальним відсотком швидкості для покращення продуктивності без зміни реальної швидкості гри. Використовувати Auto Stub @@ -187,14 +188,14 @@ Вийти Помилка мережі Втрачено з\'єднання - Конфлікт імен + Ім\'я користувача вже використовується Конфлікт MAC Конфлікт ID консолі Невірна версія Невірний пароль Не вдалося підключитися Кімната заповнена - Хост заблокований + Вам заборонено входити до цієї кімнати Доступ заборонено Користувача не знайдено Вже в кімнаті @@ -253,7 +254,8 @@ Обов\'язково Невірний IP Ім\'я занадто коротке - Має бути від 4 до 20 символів + Повинно містити 4-20 символів (лише літери, цифри, крапки, дефіси, підкреслення та пробіли) + Недійсне ім\'я користувача, перевірте в Система → Мережа 1-65535 Має бути 48 символів і містити лише малі літери a-z Тип лобі @@ -409,6 +411,15 @@ Перевіряє встановлений вміст на наявність помилок. Відсутні ключі Прошивку та роздрібні ігри не вдасться розшифрувати. + Недійсна прошивка + Для запуску деяких ігор та системних аплетів потрібна прошивка. Eden працює лише з прошивкою 19.0.1 або старішою. + Прошивка є, але не може бути прочитана. Перевірте ключі дешифрування та повторно зробіть дамп прошивки за необхідності. + Прошивка занадто нова або нечитабельна. Eden працює лише з прошивкою 19.0.1 або старішою. + Не вдалося встановити ключі + Ключі успішно встановлено + Не вдалося скопіювати один або кілька ключів. + Переконайтеся, що ваш файл ключів має розширення .keys і спробуйте ще раз. + Не вдалося ініціалізувати ключі. Переконайтеся, що ваші інструменти дампу оновлені, і повторно зробіть дамп ключів. Qlaunch @@ -462,7 +473,7 @@ Створити Веб-ім\'я користувача - Ім\'я користувача, яке буде відображатися в мультиплеєрних лобі. Має містити 4–20 символів. + Ім\'я користувача, що відображається в мультиплеєрних кімнатах. Повинно містити 4-20 символів (лише літери, цифри, дефіси, крапки, підкреслення та пробіли). Веб-токен Веб-токен, що використовується для створення публічних кімнат. Це рядок із 48 символів, що містить лише малі літери a-z. Мережа @@ -634,11 +645,14 @@ Ваш ROM зашифрований + ігрових картриджів або цифрових назв.]]> prod.keys встановлено, щоб ігри можна було розшифрувати.]]> Сталася помилка під час ініціалізації відеоядра. Зазвичай це спричинено несумісним драйвером GPU. Встановлення користувацького драйвера GPU може вирішити цю проблему. Не вдалося запустити ROM ROM файлу не існує + Гра вимагає прошивки + зробіть дамп і встановіть прошивку, або натисніть "OK", щоб продовжити в будь-якому разі.]]> Готово Масштаб diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml index 87de8e80dc..5d2e59a6d5 100644 --- a/src/android/app/src/main/res/values-vi/strings.xml +++ b/src/android/app/src/main/res/values-vi/strings.xml @@ -62,24 +62,25 @@ Hiển thị phiên bản firmware Hiển thị phiên bản firmware đã cài đặt - + Tiện ích mở rộng GPU Trình kết xuất RAII Phương pháp quản lý tài nguyên tự động trong Vulkan đảm bảo giải phóng tài nguyên đúng cách khi không còn cần thiết, nhưng có thể gây ra sự cố trong các trò chơi được đóng gói. - Khác - Màn Eden + CPU và Bộ nhớ + Mành che của Eden Cài đặt thử nghiệm để cải thiện hiệu suất và khả năng. Những cài đặt này có thể gây ra màn hình đen hoặc các vấn đề khác trong trò chơi. Cài đặt thử nghiệm - Các cài đặt trong Eden\'s Veil rất thử nghiệm và có thể gây ra sự cố. Nếu trò chơi của bạn không khởi động, hãy tắt tất cả các tiện ích mở rộng và thay đổi Extended Dynamic State thành 0. + Các cài đặt trong Mành che của Eden rất thử nghiệm và có thể gây ra sự cố. Nếu trò chơi của bạn không khởi động, hãy tắt tất cả các tiện ích mở rộng. WIP: Bỏ qua khung hình Bật hoặc tắt bỏ qua khung hình để cải thiện hiệu suất bằng cách giảm số lượng khung hình được kết xuất. Tính năng này đang được phát triển và sẽ được kích hoạt trong các bản phát hành tương lai. Đồng bộ khung hình nâng cao Đảm bảo cung cấp khung hình mượt mà và ổn định bằng cách đồng bộ hóa thời gian giữa các khung hình, giảm giật lag và hoạt ảnh không đồng đều. Lý tưởng cho các trò chơi gặp vấn đề về thời gian khung hình không ổn định hoặc giật lag nhẹ trong khi chơi. Bật bộ nhớ đệm LRU - Bật hoặc tắt bộ nhớ đệm LRU (ít được sử dụng gần đây nhất), tăng hiệu suất bằng cách tiết kiệm sử dụng CPU. Một số trò chơi có vấn đề với nó, đặc biệt là TotK 1.2.1, vì vậy hãy tắt nếu trò chơi không khởi động hoặc bị treo ngẫu nhiên. + Bật hoặc tắt bộ nhớ đệm LRU để cải thiện hiệu suất bằng cách tiết kiệm quy trình sử dụng CPU. Một số trò chơi như TotK 1.2.1 có vấn đề - hãy tắt nếu trò chơi không khởi động hoặc bị treo ngẫu nhiên. Trạng thái động mở rộng - Kích hoạt các tính năng Vulkan để cải thiện hiệu suất, kết xuất và tiết kiệm tài nguyên khi tạo pipeline trong khi vẫn duy trì mức sử dụng CPU/GPU thấp. Các tiện ích mở rộng này có thể làm tăng nhiệt độ thiết bị và GPU thuộc dòng A6XX cũ có thể hoạt động không chính xác. Đặt thành 0 để sử dụng các định dạng mô phỏng cũ. + Kích hoạt tính năng Vulkan để cải thiện hiệu suất, kết xuất và tiết kiệm tài nguyên khi tạo pipeline trong khi vẫn duy trì mức sử dụng CPU/GPU thấp. Các tiện ích mở rộng này có thể làm tăng nhiệt độ thiết bị và GPU thuộc dòng A6XX cũ có thể không phản ứng đúng. Tắt để mô phỏng các định dạng tỷ lệ. + Đã tắt Đồng bộ tốc độ lõi Đồng bộ tốc độ lõi với tỷ lệ phần trăm tốc độ tối đa để cải thiện hiệu suất mà không làm thay đổi tốc độ thực tế của trò chơi. Sử dụng Auto Stub @@ -184,18 +185,18 @@ Lỗi tham gia Tên quá ngắn Địa chỉ không hợp lệ - Cổng không hợp lệ! + Cổng không hợp lệ Rời phòng Lỗi mạng Mất kết nối - Xung đột tên + Tên người dùng đã được sử dụng Xung đột MAC Xung đột ID máy chơi game Sai phiên bản Sai mật khẩu Không thể kết nối Phòng đã đầy - Host bị cấm + Bạn đã bị cấm khỏi phòng này Từ chối truy cập Không có người chơi Đã trong phòng @@ -255,7 +256,8 @@ 3-20 ký tự Bắt buộc Sai định dạng IP - Phải có từ 4–20 ký tự + Phải có 4-20 ký tự (chỉ chữ số, dấu chấm, dấu gạch ngang, dấu gạch dưới và khoảng trắng) + Tên người dùng không hợp lệ, hãy đảm bảo đã đặt đúng trong Hệ thống → Mạng 1-65535 Phải có 48 ký tự và chỉ chữ thường a-z Loại phòng @@ -410,6 +412,15 @@ Kiểm tra lỗi nội dung đã cài Thiếu keys mã hóa Không thể giải mã firmware và game + Firmware không hợp lệ + Firmware là cần thiết để chạy một số trò chơi và ứng dụng. Eden chỉ hoạt động với firmware 19.0.1 trở về trước. + Firmware được báo cáo là có nhưng không thể đọc được. Kiểm tra khóa giải mã và dump lại firmware nếu cần. + Firmware quá mới hoặc không thể đọc. Eden chỉ hoạt động với firmware 19.0.1 trở về trước. + Không thể cài đặt khóa + Đã cài đặt khóa thành công + Không thể sao chép một hoặc nhiều khóa. + Đảm bảo tệp khóa của bạn có phần mở rộng .keys và thử lại. + Không thể khởi tạo khóa. Kiểm tra xem công cụ dump của bạn đã cập nhật chưa và dump lại khóa. Qlaunch @@ -463,7 +474,7 @@ Tạo Tên người dùng web - Tên người dùng hiển thị trong phòng chơi nhiều người. Phải có 4–20 ký tự. + Tên người dùng hiển thị trong phòng chơi nhiều người. Phải có 4-20 ký tự (chỉ chữ số, dấu gạch ngang, dấu chấm, dấu gạch dưới và khoảng trắng). Token web Token web dùng để tạo phòng công khai. Đây là chuỗi 48 ký tự chỉ chứa chữ thường a-z. Mạng @@ -639,11 +650,14 @@ Kiểm tra tính toàn vẹn thất bại! ROM của bạn đã bị mã hoá + băng trò chơi hoặc tựa game kỹ thuật số của bạn.]]> prod.keys đã được cài đặt để game có thể được giải mã.]]> Đã xảy ra lỗi khi khởi tạo lõi video 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. Không thể tải ROM Tệp ROM không tồn tại + Trò chơi yêu cầu firmware + dump và cài đặt firmware, hoặc nhấn "OK" để tiếp tục dù sao đi nữa.]]> Thoát giả lập 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 20f9bdea90..95c7352d08 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 @@ -62,24 +62,24 @@ 显示固件版本 显示已安装的固件版本 - + GPU扩展 渲染器 RAII Vulkan中的一种自动资源管理方法,确保在不再需要时正确释放资源,但可能导致捆绑游戏崩溃。 - 杂项 + CPU和内存 伊甸之幕 实验性设置以提高性能和能力。这些设置可能会导致黑屏或其他游戏问题。 - 实验性设置 - Eden\'s Veil 中的设置是高度实验性的,可能会导致问题。如果您的游戏无法启动,请禁用所有扩展并将 Extended Dynamic State 更改为 0。 + 伊甸之幕中的设置具有高度实验性,可能导致问题。如果游戏无法启动,请禁用所有扩展。 开发中:跳帧 启用或禁用跳帧以减少渲染帧数,提高性能。此功能仍在开发中,将在未来版本中启用。 增强帧同步 通过同步帧间时间确保流畅一致的帧交付,减少卡顿和不均匀动画。适合存在帧时间不稳定或游戏过程中出现微卡顿的游戏。 启用LRU缓存 - 启用或禁用LRU(最近最少使用)缓存,通过节省CPU使用来提高性能。某些游戏(尤其是TotK 1.2.1)可能存在问题,如果游戏无法启动或随机崩溃,请禁用它。 + 启用或禁用LRU缓存,通过节省CPU进程使用来提高性能。某些游戏可能存在问题,特别是TotK 1.2.1,如果游戏无法启动或随机崩溃,请禁用此选项。 扩展动态状态 - 启用Vulkan功能以提升性能、优化渲染并在流水线创建时节省资源,同时保持较低的CPU/GPU占用率。这些扩展可能会提高设备温度,旧款A6XX系列GPU可能无法正常工作。设置为0以使用旧版模拟格式。 + 启用Vulkan功能以提高性能、渲染效果,并在创建管道时节省资源,同时保持较低的CPU/GPU使用率。这些扩展可能会提高设备温度,旧款A6XX系列GPU可能无法正常工作。禁用可模拟缩放格式。 + 已禁用 同步核心速度 将核心速度与最大速度百分比同步,在不改变游戏实际速度的情况下提高性能。 使用自动存根 @@ -184,18 +184,18 @@ 加入房间失败 名称过短 地址无效 - 端口无效! + 端口无效 退出房间 网络错误 连接丢失 - 名称冲突 + 用户名已被占用 MAC地址冲突 主机ID冲突 版本不匹配 密码错误 无法连接 房间已满 - 主机被封禁 + 您已被禁止加入此房间 权限不足 用户不存在 已在房间中 @@ -255,7 +255,8 @@ 长度需为3-20个字符 必填 IP格式无效 - 必须为4-20个字符 + 必须为4-20个字符(仅字母数字、点号、连字符、下划线和空格) + 用户名无效,请在系统→网络中检查设置 端口需为1-65535 必须为48个字符,且仅包含小写字母a-z 大厅类型 @@ -417,6 +418,15 @@ 检查所有安装的内容是否有损坏 密钥缺失 无法解密固件和商业游戏 + 固件无效 + 运行某些游戏和系统应用需要固件。Eden仅支持19.0.1及更早版本的固件。 + 检测到固件存在但无法读取。请检查解密密钥并在必要时重新转储固件。 + 固件过新或无法读取。Eden仅支持19.0.1及更早版本的固件。 + 密钥安装失败 + 密钥安装成功 + 一个或多个密钥复制失败。 + 请确保密钥文件具有.keys扩展名后重试。 + 密钥初始化失败。请检查您的转储工具是否为最新版本并重新转储密钥。 Qlaunch @@ -482,7 +492,7 @@ 生成 网络用户名 - 在多人游戏大厅中显示的用户名。必须为4-20个字符。 + 多人游戏房间中显示的用户名。必须为4-20个字符(仅字母数字、连字符、点号、下划线和空格)。 网络令牌 用于创建公共房间的网络令牌。它是一个48个字符的字符串,仅包含小写字母a-z。 网络 @@ -764,12 +774,14 @@ 您的 ROM 已加密 - 游戏卡带或已安装的游戏。]]> + 游戏卡带或数字版游戏。]]> prod.keys 文件已安装,使得游戏可以被解密。]]> 初始化视频核心时发生错误 这通常由不兼容的 GPU 驱动程序造成,安装自定义 GPU 驱动程序可能解决此问题。 无法载入 ROM ROM 文件不存在 + 游戏需要固件 + 转储并安装固件,或点击"确定"继续。]]> 退出模拟 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 dcda2d8260..b9511bb959 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 @@ -62,24 +62,25 @@ 顯示韌體版本 顯示已安裝的韌體版本 - + GPU擴充功能 渲染器 RAII Vulkan中的一種自動資源管理方法,確保在不再需要時正確釋放資源,但可能導致捆綁遊戲崩潰。 - 其他 + CPU與記憶體 伊甸之幕 實驗性設定以提高效能和能力。這些設定可能會導致黑屏或其他遊戲問題。 實驗性設定 - Eden\'s Veil 中的設定是高度實驗性的,可能會導致問題。如果您的遊戲無法啟動,請停用所有擴充功能並將 Extended Dynamic State 更改為 0。 + 伊甸之幕中的設定具有高度實驗性,可能導致問題。如果遊戲無法啟動,請停用所有擴充功能。 開發中:跳幀 啟用或停用跳幀以減少渲染幀數,提高效能。此功能仍在開發中,將在未來版本中啟用。 增強幀同步 通過同步幀間時間確保流暢一致的幀交付,減少卡頓和不均勻動畫。適合存在幀時間不穩定或遊戲過程中出現微卡頓的遊戲。 啟用LRU快取 - 啟用或停用LRU(最近最少使用)快取,通過節省CPU使用來提高效能。某些遊戲(尤其是TotK 1.2.1)可能存在問題,如果遊戲無法啟動或隨機崩潰,請停用它。 + 啟用或停用LRU快取,透過節省CPU進程使用來提高效能。某些遊戲可能存在問題,特別是TotK 1.2.1,如果遊戲無法啟動或隨機崩潰,請停用此選項。 擴展動態狀態 - 啟用Vulkan功能以提升效能、改善渲染並在創建管線時節省資源,同時保持較低的CPU/GPU使用率。這些擴充功能可能會提高設備溫度,且舊款A6XX系列的GPU可能無法正常運作。設為0以使用舊版模擬格式。 + 啟用Vulkan功能以提高效能、渲染效果,並在創建管線時節省資源,同時保持較低的CPU/GPU使用率。這些擴充功能可能會提高裝置溫度,舊款A6XX系列GPU可能無法正常運作。停用可模擬縮放格式。 + 已停用 同步核心速度 將核心速度與最大速度百分比同步,在不改變遊戲實際速度的情況下提高效能。 使用自動存根 @@ -187,18 +188,18 @@ 加入房間失敗 名稱過短 位址無效 - 埠號無效! + 連接埠無效 退出房間 網路錯誤 連線中斷 - 名稱衝突 + 使用者名稱已被佔用 MAC位址衝突 主機ID衝突 版本不符 密碼錯誤 無法連接 房間已滿 - 主機已被封鎖 + 您已被禁止加入此房間 權限不足 使用者不存在 已在房間中 @@ -258,7 +259,8 @@ 長度需為3-20個字元 必填 IP格式無效 - 必須為4-20個字元 + 必須為4-20個字元(僅字母數字、點號、連字號、底線和空格) + 使用者名稱無效,請在系統→網路中檢查設定 埠號需為1-65535 必須為48個字元,且僅包含小寫字母a-z 大廳類型 @@ -421,6 +423,15 @@ 检查所有安装的内容是否有损坏 密钥缺失 无法解密固件和商业游戏 + 韌體無效 + 執行某些遊戲和系統應用需要韌體。Eden僅支援19.0.1及更早版本的韌體。 + 檢測到韌體存在但無法讀取。請檢查解密金鑰並在必要時重新轉儲韌體。 + 韌體過新或無法讀取。Eden僅支援19.0.1及更早版本的韌體。 + 金鑰安裝失敗 + 金鑰安裝成功 + 一個或多個金鑰複製失敗。 + 請確保金鑰檔案具有.keys副檔名後重試。 + 金鑰初始化失敗。請檢查您的轉儲工具是否為最新版本並重新轉儲金鑰。 Qlaunch @@ -486,7 +497,7 @@ 生成 網路使用者名稱 - 在多人遊戲大廳中顯示的使用者名稱。必須為4-20個字元。 + 多人遊戲房間中顯示的使用者名稱。必須為4-20個字元(僅字母數字、連字號、點號、底線和空格)。 網路令牌 用於建立公開大廳的網路令牌。它是由48個小寫字母a-z組成的字串。 網路 @@ -768,12 +779,14 @@ 您的 ROM 已加密 - 遊戲卡匣或已安裝的遊戲。]]> + 遊戲卡帶或數位版遊戲。]]> prod.keys 檔案已安裝,讓遊戲可以解密。]]> 初始化視訊核心時發生錯誤 這經常由不相容的 GPU 驅動程式造成,安裝自訂 GPU 驅動程式可能會解決此問題。 無法載入 ROM ROM 檔案不存在 + 遊戲需要韌體 + 轉儲並安裝韌體,或點擊"確定"繼續。]]> 結束模擬 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 3814105ea0..cac730b66b 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -66,11 +66,11 @@ Show Firmware Version Display the installed firmware version - - Eden’s Veil + + Eden\'s Veil Experimental settings to improve performance and capability. These settings may cause black screens or other game issues. Experimental Settings - The settings contained within Eden\'s Veil are highly experimental and may cause issues. If your game does not boot, disable any extensions and change Extended Dynamic State to 0. + The settings contained within Eden\'s Veil are highly experimental and may cause issues. If your game does not boot, disable any extensions. GPU Extensions Extended Dynamic State @@ -386,7 +386,7 @@ https://yuzu-mirror.github.io/help/quickstart/#dumping-decryption-keys Firmware Invalid - Firmware is required to run certain games and use the Home Menu. Eden only works with firmware 19.0.1 and earlier. + Firmware is required to run certain games and use system applications. Eden only works with firmware 19.0.1 and earlier. Firmware reported as present, but was unable to be read. Check for decryption keys and redump firmware if necessary. Firmware is too new or could not be read. Eden only works with firmware 19.0.1 and earlier. https://yuzu-mirror.github.io/help/quickstart/#dumping-system-firmware From e8e0d7fa20f8c20a389c9a4702844bf5b52633ed Mon Sep 17 00:00:00 2001 From: crueter Date: Sun, 20 Jul 2025 22:11:02 -0400 Subject: [PATCH 28/41] [vk] tmp: workaround for RAII crash on exit Prevent double-free Signed-off-by: crueter --- src/video_core/vulkan_common/vulkan_wrapper.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index c703abe40e..40136c3fbf 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -11,6 +11,7 @@ #include #include "common/common_types.h" +#include "common/settings.h" #include "common/logging/log.h" #include "video_core/vulkan_common/vk_enum_string_helper.h" #include "video_core/vulkan_common/vma.h" @@ -309,7 +310,10 @@ const char* Exception::what() const noexcept { } void Destroy(VkInstance instance, const InstanceDispatch& dld) noexcept { - dld.vkDestroyInstance(instance, nullptr); + // FIXME: A double free occurs here if RAII is enabled. + if (!Settings::values.enable_raii.GetValue()) { + dld.vkDestroyInstance(instance, nullptr); + } } void Destroy(VkDevice device, const InstanceDispatch& dld) noexcept { @@ -412,7 +416,10 @@ void Destroy(VkInstance instance, VkDebugReportCallbackEXT handle, } void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept { - dld.vkDestroySurfaceKHR(instance, handle, nullptr); + // FIXME: A double free occurs here if RAII is enabled. + if (!Settings::values.enable_raii.GetValue()) { + dld.vkDestroySurfaceKHR(instance, handle, nullptr); + } } VkResult Free(VkDevice device, VkDescriptorPool handle, Span sets, From 12a690e15fd28d77e6d4cc970d306f2fe0e99217 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 21 Jul 2025 04:35:43 +0200 Subject: [PATCH 29/41] [nvdrv] ZBC table implement stubs (#83) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/83 This commit introduces an implementation for `ZBCSetTable` in `nvhost_ctrl_gpu`, which is invoked by games and system services to configure default clear values for depth, stencil, and color attachments on the GPU, allowing better instructions on how the ZCull Block Compression expects to work within the emulated GPU. This is an important step for compatibility with titles or system modules that expect ZBC configuration support. Co-authored-by: lizzie Co-committed-by: lizzie --- .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 83 ++++++++++++++++++- .../service/nvdrv/devices/nvhost_ctrl_gpu.h | 22 +++++ 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 1b8f575586..45a4a402da 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -35,6 +35,7 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span return WrapFixed(this, &nvhost_ctrl_gpu::ZBCSetTable, input, output); case 0x4: return WrapFixed(this, &nvhost_ctrl_gpu::ZBCQueryTable, input, output); + //deviation case 0x5: return WrapFixed(this, &nvhost_ctrl_gpu::GetCharacteristics1, input, output); case 0x6: @@ -50,7 +51,29 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span } break; } - UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); + // still unimplemented + std::string_view friendly_name = [raw = command.raw]() { + switch (raw) { + case 0x0d: return "INVAL_ICACHE"; + case 0x0e: return "SET_MMU_DEBUG_MODE "; + case 0x0f: return "SET_SM_DEBUG_MODE"; + case 0x10: return "WAIT_FOR_PAUSE"; + case 0x11: return "GET_TPC_EXCEPTION_EN_STATUS"; + case 0x12: return "NUM_VSMS"; + case 0x13: return "VSMS_MAPPING"; + case 0x14: return "ZBC_GET_ACTIVE_SLOT_MASK"; + case 0x15: return "PMU_GET_GPU_LOAD"; + case 0x16: return "SET_CG_CONTROLS"; + case 0x17: return "GET_CG_CONTROLS"; + case 0x18: return "SET_PG_CONTROLS"; + case 0x19: return "GET_PG_CONTROLS"; + case 0x1A: return "PMU_GET_ELPG_RESIDENCY_GATING"; + case 0x1B: return "GET_ERROR_CHANNEL_USER_DATA"; + case 0x1D: return "GET_CPU_TIME_CORRELATION_INFO"; + default: return "UNKNOWN"; + } + }(); + UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X} {}", command.raw, friendly_name); return NvResult::NotImplemented; } @@ -230,18 +253,70 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params) { } NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called"); + ZbcEntry entry = {}; + std::memset(&entry, 0, sizeof(entry)); // TODO(ogniK): What does this even actually do? + // TODO(myself): This thing I guess + if (params.type == 1) { + for (auto i = 0; i < 4; ++i) { + entry.color_ds[i] = params.color_ds[i]; + entry.color_l2[i] = params.color_l2[i]; + } + ASSERT(this->max_color_entries < 16); + this->color_entries[this->max_color_entries] = entry; + ++this->max_color_entries; + } else if (params.type == 2) { + entry.depth = params.depth; + ASSERT(this->max_depth_entries < 16); + this->depth_entries[this->max_depth_entries] = entry; + ++this->max_depth_entries; + } return NvResult::Success; } NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called"); + struct ZbcQueryParams { + u32_le color_ds[4]; + u32_le color_l2[4]; + u32_le depth; + u32_le ref_cnt; + u32_le format; + u32_le type; + u32_le index_size; + } entry = {}; + std::memset(&entry, 0, sizeof(entry)); + auto const index = params.index_size; + if (params.type == 0) { //no + entry.index_size = 15; + } else if (params.type == 1) { //color + ASSERT(index < 16); + for (auto i = 0; i < 4; ++i) { + params.color_ds[i] = this->color_entries[index].color_ds[i]; + params.color_l2[i] = this->color_entries[index].color_l2[i]; + } + // TODO: Only if no error thrown (otherwise dont modify) + params.format = this->color_entries[index].format; + //params.ref_cnt = this->color_entries[index].ref_cnt; + } else if (params.type == 2) { //depth + ASSERT(index < 16); + params.depth = this->depth_entries[index].depth; + // TODO: Only if no error thrown (otherwise dont modify) + params.format = this->depth_entries[index].format; + //params.ref_cnt = this->depth_entries[index].ref_cnt; + } else { + UNREACHABLE(); + } return NvResult::Success; } NvResult nvhost_ctrl_gpu::FlushL2(IoctlFlushL2& params) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called 0x{:X}", params.flush); + // if ((params.flush & 0x01) != 0) //l2 flush + // /* we dont emulate l2 */; + // if ((params.flush & 0x04) != 0) //fb flush + // /* we dont emulate fb */; return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index d2ab05b214..c36fcbaa69 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -151,6 +151,16 @@ private: }; static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); + struct IoctlGetCpuTimeCorrelationInfo { + struct { + u64_le cpu_timestamp; + u64_le gpu_timestamp; + } samples[16]; + u32_le count; + u32_le source_id; + }; + static_assert(sizeof(IoctlGetCpuTimeCorrelationInfo) == 264); + NvResult GetCharacteristics1(IoctlCharacteristics& params); NvResult GetCharacteristics3(IoctlCharacteristics& params, std::span gpu_characteristics); @@ -171,6 +181,18 @@ private: // Events Kernel::KEvent* error_notifier_event; Kernel::KEvent* unknown_event; + + struct ZbcEntry { + u32_le color_ds[4]; + u32_le color_l2[4]; + u32_le depth; + u32_le type; + u32_le format; + }; + std::array color_entries; + std::array depth_entries; + u8 max_color_entries; + u8 max_depth_entries; }; } // namespace Service::Nvidia::Devices From be97bf3c1b6b09fcfc1828f201f04a87d034b92e Mon Sep 17 00:00:00 2001 From: Maufeat Date: Mon, 21 Jul 2025 07:16:26 +0200 Subject: [PATCH 30/41] [nvnflinger] add GetBufferHistory from sudachi (#82) Co-authored-by: Maufeat Co-authored-by: CamilleLaVey Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/82 This commit adds a working implementation of the `GetBufferHistory` transaction in `BufferQueueProducer`, removing the previous stub. Adapted by Jarrod Norwell for Sudachi, this implementation references the behavior in Ryujinx; commit rescued by Maufeat and another Eden teammate from Sudachi's reference, fixed and adapted for Eden usage. It helps improve compatibility with Unreal Engine 4 titles and others that depend on proper surface history tracking for rendering pipelines, especially with regard to lighting, bloom, and alpha transitions. Functionality has been tested for stability and does not introduce regressions, though further validation is recommended. Co-authored-by: Maufeat Co-committed-by: Maufeat --- .../nvnflinger/buffer_queue_consumer.cpp | 8 +++ .../service/nvnflinger/buffer_queue_core.cpp | 4 +- .../service/nvnflinger/buffer_queue_core.h | 21 ++++++++ .../nvnflinger/buffer_queue_producer.cpp | 49 ++++++++++++++++--- .../nvnflinger/buffer_queue_producer.h | 2 + src/core/hle/service/nvnflinger/buffer_slot.h | 3 +- 6 files changed, 79 insertions(+), 8 deletions(-) diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp index 3bc23aa976..91ba35aef5 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp @@ -101,6 +101,14 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, // slot to the producer, it will wait for the fence to pass. We should fix this // by properly waiting for the fence in the BufferItemConsumer. // slots[slot].fence = Fence::NoFence(); + + const auto target_frame_number = slots[slot].frame_number; + for (size_t i = 0; i < core->history.size(); i++) { + if (core->history[i].frame_number == target_frame_number) { + core->history[i].state = BufferState::Acquired; + break; + } + } } // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp index 5d8c861fa7..30095b0f73 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp @@ -10,7 +10,9 @@ namespace Service::android { -BufferQueueCore::BufferQueueCore() = default; +BufferQueueCore::BufferQueueCore() { + history.resize(8); +}; BufferQueueCore::~BufferQueueCore() = default; diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h index e513d183bf..341634352b 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h @@ -21,6 +21,25 @@ namespace Service::android { +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif +struct BufferInfo { + u64 frame_number; + s64 queue_time; + s64 presentation_time{}; + BufferState state{BufferState::Free}; +} +#if defined(__GNUC__) || defined(__clang__) +__attribute__((packed)) +#endif +; +#ifdef _MSC_VER +#pragma pack(pop) +#endif +static_assert(sizeof(BufferInfo) == 0x1C, + "BufferInfo is an invalid size"); + class IConsumerListener; class IProducerListener; @@ -72,6 +91,8 @@ private: u32 transform_hint{}; bool is_allocating{}; mutable std::condition_variable_any is_allocating_condition; + + std::vector history{8}; }; } // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index 9e5091eebd..4317aee1c4 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -512,6 +512,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, slots[slot].buffer_state = BufferState::Queued; ++core->frame_counter; slots[slot].frame_number = core->frame_counter; + slots[slot].queue_time = timestamp; + slots[slot].presentation_time = 0; item.acquire_called = slots[slot].acquire_called; item.graphic_buffer = slots[slot].graphic_buffer; @@ -528,6 +530,11 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, item.is_droppable = core->dequeue_buffer_cannot_block || async; item.swap_interval = swap_interval; + position = (position + 1) % 8; + core->history[position] = {.frame_number = core->frame_counter, + .queue_time = slots[slot].queue_time, + .state = BufferState::Queued}; + sticky_transform = sticky_transform_; if (core->queue.empty()) { @@ -803,6 +810,10 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, return Status::NoError; } +Kernel::KReadableEvent* BufferQueueProducer::GetNativeHandle(u32 type_id) { + return &buffer_wait_event->GetReadableEvent(); +} + void BufferQueueProducer::Transact(u32 code, std::span parcel_data, std::span parcel_reply, u32 flags) { // Values used by BnGraphicBufferProducer onTransact @@ -922,23 +933,49 @@ void BufferQueueProducer::Transact(u32 code, std::span parcel_data, status = SetBufferCount(buffer_count); break; } - case TransactionId::GetBufferHistory: - LOG_WARNING(Service_Nvnflinger, "(STUBBED) called, transaction=GetBufferHistory"); + case TransactionId::GetBufferHistory: { + LOG_WARNING(Service_Nvnflinger, "called, transaction=GetBufferHistory"); + + std::scoped_lock lock{core->mutex}; + + auto buffer_history_count = std::min(parcel_in.Read(), (s32)core->history.size()); + + if (buffer_history_count <= 0) { + parcel_out.Write(Status::BadValue); + parcel_out.Write(0); + status = Status::None; + break; + } + + auto info = new BufferInfo[buffer_history_count]; + auto pos = position; + for (int i = 0; i < buffer_history_count; i++) { + info[i] = core->history[(pos - i) % core->history.size()]; + LOG_WARNING(Service_Nvnflinger, "frame_number={}, state={}", + core->history[(pos - i) % core->history.size()].frame_number, + (u32)core->history[(pos - i) % core->history.size()].state); + pos--; + } + + parcel_out.Write(Status::NoError); + parcel_out.Write(buffer_history_count); + parcel_out.WriteFlattenedObject(info); + status = Status::None; break; + } default: ASSERT_MSG(false, "Unimplemented TransactionId {}", code); break; } - parcel_out.Write(status); + if (status != Status::None) { + parcel_out.Write(status); + } const auto serialized = parcel_out.Serialize(); std::memcpy(parcel_reply.data(), serialized.data(), std::min(parcel_reply.size(), serialized.size())); } -Kernel::KReadableEvent* BufferQueueProducer::GetNativeHandle(u32 type_id) { - return &buffer_wait_event->GetReadableEvent(); -} } // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h index 048523514c..28195cd3c5 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h @@ -86,6 +86,8 @@ private: s32 current_callback_ticket{}; std::condition_variable_any callback_condition; + u64 position; + Service::Nvidia::NvCore::NvMap& nvmap; }; diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h index 37daca78b1..5b5cbb6fbd 100644 --- a/src/core/hle/service/nvnflinger/buffer_slot.h +++ b/src/core/hle/service/nvnflinger/buffer_slot.h @@ -15,7 +15,7 @@ namespace Service::android { class GraphicBuffer; -enum class BufferState : u32 { +enum class BufferState : s32 { Free = 0, Dequeued = 1, Queued = 2, @@ -34,6 +34,7 @@ struct BufferSlot final { bool needs_cleanup_on_release{}; bool attached_by_consumer{}; bool is_preallocated{}; + s64 queue_time{}, presentation_time{}; }; } // namespace Service::android From be9a415157bc553b4c29172426e60faec16cc320 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 21 Jul 2025 13:44:26 +0200 Subject: [PATCH 31/41] Revert [Android] 0 FPS Error fix for certain titles. revert [android] Fix 0fps errors on DKCR, Subnautica, and Ori 2 (#79) Co-authored-by: Pavel Barabanov Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/79 Option reverted due problems caused with another games and make some advances worst when testing, if necessary it's going to be refined to be converted into a toggle. --- src/video_core/fence_manager.h | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index a2411af63c..2135f1f2da 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -76,27 +73,15 @@ public: void SignalFence(std::function&& func) { bool delay_fence = Settings::IsGPULevelHigh(); - #ifdef __ANDROID__ - if (!delay_fence) { - TryReleasePendingFences(); - } - #else if constexpr (!can_async_check) { TryReleasePendingFences(); } - #endif const bool should_flush = ShouldFlush(); CommitAsyncFlushes(); TFence new_fence = CreateFence(!should_flush); - #ifdef __ANDROID__ - if (delay_fence) { - guard.lock(); - } - #else if constexpr (can_async_check) { guard.lock(); } - #endif if (delay_fence) { uncommitted_operations.emplace_back(std::move(func)); } @@ -109,17 +94,10 @@ public: if (should_flush) { rasterizer.FlushCommands(); } - #ifdef __ANDROID__ - if (delay_fence) { - guard.unlock(); - cv.notify_all(); - } - #else if constexpr (can_async_check) { guard.unlock(); cv.notify_all(); } - #endif rasterizer.InvalidateGPUCache(); } From 7db5eb8f0861988776e19c4e76b15e3752fee3a1 Mon Sep 17 00:00:00 2001 From: MaranBr Date: Tue, 22 Jul 2025 07:31:36 +0200 Subject: [PATCH 32/41] [host1x] Fix hardware detection and improve compatibility (#85) This fixes green screen on unsupported devices trying to use GPU decoding and improve compatibility with default builds of FFmpeg on all platforms. Co-authored-by: MaranBr Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/85 Co-authored-by: MaranBr Co-committed-by: MaranBr --- src/video_core/host1x/ffmpeg/ffmpeg.cpp | 288 ++++++++++++------------ src/video_core/host1x/ffmpeg/ffmpeg.h | 7 +- 2 files changed, 145 insertions(+), 150 deletions(-) diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp index 3170f53884..6609752bdb 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp @@ -23,80 +23,81 @@ namespace FFmpeg { namespace { +constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12; +constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P; constexpr std::array PreferredGpuDecoders = { #if defined (_WIN32) - AV_HWDEVICE_TYPE_CUDA, - AV_HWDEVICE_TYPE_D3D11VA, - AV_HWDEVICE_TYPE_DXVA2, + AV_HWDEVICE_TYPE_CUDA, + AV_HWDEVICE_TYPE_D3D11VA, + AV_HWDEVICE_TYPE_DXVA2, #elif defined(__FreeBSD__) - AV_HWDEVICE_TYPE_VDPAU, + AV_HWDEVICE_TYPE_VDPAU, #elif defined(__unix__) AV_HWDEVICE_TYPE_CUDA, - AV_HWDEVICE_TYPE_VAAPI, + AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_VDPAU, #endif - AV_HWDEVICE_TYPE_VULKAN, + AV_HWDEVICE_TYPE_VULKAN, }; AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) { - // Check if there is a pixel format supported by the GPU decoder. const auto desc = av_pix_fmt_desc_get(codec_context->pix_fmt); - if (desc && desc->flags & AV_PIX_FMT_FLAG_HWACCEL) { - for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { - if (*p == codec_context->pix_fmt) { - return codec_context->pix_fmt; + if (desc && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { + for (int i = 0;; i++) { + const AVCodecHWConfig* config = avcodec_get_hw_config(codec_context->codec, i); + if (!config) { + break; + } + + for (const auto type : PreferredGpuDecoders) { + if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) { + codec_context->pix_fmt = config->pix_fmt; + } } } } - // Another check to confirm if there is a pixel format supported by specific GPU decoders. - for (int i = 0;; i++) { - const AVCodecHWConfig* config = avcodec_get_hw_config(codec_context->codec, i); - if (!config) { - break; - } - - if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) && (config->device_type == AV_HWDEVICE_TYPE_CUDA || config->device_type == AV_HWDEVICE_TYPE_VAAPI)) { - return config->pix_fmt; + for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { + if (*p == codec_context->pix_fmt) { + return codec_context->pix_fmt; } } - // Fallback to CPU decoder. - LOG_INFO(HW_GPU, "Could not find compatible GPU pixel format, falling back to CPU"); - av_buffer_unref(&codec_context->hw_device_ctx); - - return codec_context->pix_fmt; + LOG_INFO(HW_GPU, "Could not find supported GPU pixel format, falling back to CPU decoder"); + av_buffer_unref(&codec_context->hw_device_ctx); + codec_context->pix_fmt = PreferredCpuFormat; + return codec_context->pix_fmt; } std::string AVError(int errnum) { - char errbuf[AV_ERROR_MAX_STRING_SIZE] = {}; - av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum); - return errbuf; + char errbuf[AV_ERROR_MAX_STRING_SIZE] = {}; + av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum); + return errbuf; } } Packet::Packet(std::span data) { - m_packet = av_packet_alloc(); - m_packet->data = const_cast(data.data()); - m_packet->size = static_cast(data.size()); + m_packet = av_packet_alloc(); + m_packet->data = const_cast(data.data()); + m_packet->size = static_cast(data.size()); } Packet::~Packet() { - av_packet_free(&m_packet); + av_packet_free(&m_packet); } Frame::Frame() { - m_frame = av_frame_alloc(); + m_frame = av_frame_alloc(); } Frame::~Frame() { - av_frame_free(&m_frame); + av_frame_free(&m_frame); } Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) { - const AVCodecID av_codec = [&] { - switch (codec) { + const AVCodecID av_codec = [&] { + switch (codec) { case Tegra::Host1x::NvdecCommon::VideoCodec::H264: return AV_CODEC_ID_H264; case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: @@ -106,138 +107,138 @@ Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) { default: UNIMPLEMENTED_MSG("Unknown codec {}", codec); return AV_CODEC_ID_NONE; - } - }(); + } + }(); - m_codec = avcodec_find_decoder(av_codec); + m_codec = avcodec_find_decoder(av_codec); } bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const { - for (int i = 0;; i++) { - const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i); - if (!config) { - LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name, av_hwdevice_get_type_name(type)); - break; - } + for (int i = 0;; i++) { + const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i); + if (!config) { + LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name, av_hwdevice_get_type_name(type)); + break; + } - if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) { - LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); - *out_pix_fmt = config->pix_fmt; - return true; - } - } + if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) { + LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); + *out_pix_fmt = config->pix_fmt; + return true; + } + } - return false; + return false; } std::vector HardwareContext::GetSupportedDeviceTypes() { - std::vector types; - AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; + std::vector types; + AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; - while (true) { - current_device_type = av_hwdevice_iterate_types(current_device_type); - if (current_device_type == AV_HWDEVICE_TYPE_NONE) { - return types; - } + while (true) { + current_device_type = av_hwdevice_iterate_types(current_device_type); + if (current_device_type == AV_HWDEVICE_TYPE_NONE) { + return types; + } - types.push_back(current_device_type); - } + types.push_back(current_device_type); + } } HardwareContext::~HardwareContext() { - av_buffer_unref(&m_gpu_decoder); + av_buffer_unref(&m_gpu_decoder); } bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder) { - const auto supported_types = GetSupportedDeviceTypes(); - for (const auto type : PreferredGpuDecoders) { - AVPixelFormat hw_pix_fmt; + const auto supported_types = GetSupportedDeviceTypes(); + for (const auto type : PreferredGpuDecoders) { + AVPixelFormat hw_pix_fmt; - if (std::ranges::find(supported_types, type) == supported_types.end()) { - LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); - continue; - } + if (std::ranges::find(supported_types, type) == supported_types.end()) { + LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); + continue; + } - if (!this->InitializeWithType(type)) { - continue; - } + if (!this->InitializeWithType(type)) { + continue; + } - if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) { - decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt); - return true; - } - } + if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) { + decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt); + return true; + } + } - return false; + return false; } bool HardwareContext::InitializeWithType(AVHWDeviceType type) { - av_buffer_unref(&m_gpu_decoder); + av_buffer_unref(&m_gpu_decoder); - if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0); ret < 0) { - LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), AVError(ret)); - return false; - } + if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0); ret < 0) { + LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), AVError(ret)); + return false; + } #ifdef LIBVA_FOUND - if (type == AV_HWDEVICE_TYPE_VAAPI) { - // We need to determine if this is an impersonated VAAPI driver. - auto* hwctx = reinterpret_cast(m_gpu_decoder->data); - auto* vactx = static_cast(hwctx->hwctx); - const char* vendor_name = vaQueryVendorString(vactx->display); - if (strstr(vendor_name, "VDPAU backend")) { - // VDPAU impersonated VAAPI impls are super buggy, we need to skip them. - LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver"); - return false; - } else { - // According to some user testing, certain VAAPI drivers (Intel?) could be buggy. - // Log the driver name just in case. - LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name); - } - } + if (type == AV_HWDEVICE_TYPE_VAAPI) { + // We need to determine if this is an impersonated VAAPI driver. + auto* hwctx = reinterpret_cast(m_gpu_decoder->data); + auto* vactx = static_cast(hwctx->hwctx); + const char* vendor_name = vaQueryVendorString(vactx->display); + if (strstr(vendor_name, "VDPAU backend")) { + // VDPAU impersonated VAAPI impls are super buggy, we need to skip them. + LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver"); + return false; + } else { + // According to some user testing, certain VAAPI drivers (Intel?) could be buggy. + // Log the driver name just in case. + LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name); + } + } #endif - return true; + return true; } DecoderContext::DecoderContext(const Decoder& decoder) : m_decoder{decoder} { - m_codec_context = avcodec_alloc_context3(m_decoder.GetCodec()); - av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0); - m_codec_context->thread_count = 0; - m_codec_context->thread_type &= ~FF_THREAD_FRAME; + m_codec_context = avcodec_alloc_context3(m_decoder.GetCodec()); + av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0); + m_codec_context->thread_count = 0; + m_codec_context->thread_type &= ~FF_THREAD_FRAME; } DecoderContext::~DecoderContext() { - av_buffer_unref(&m_codec_context->hw_device_ctx); - avcodec_free_context(&m_codec_context); + av_buffer_unref(&m_codec_context->hw_device_ctx); + avcodec_free_context(&m_codec_context); } void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt) { - m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef()); - m_codec_context->get_format = GetGpuFormat; - m_codec_context->pix_fmt = hw_pix_fmt; + m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef()); + m_codec_context->get_format = GetGpuFormat; + m_codec_context->pix_fmt = hw_pix_fmt; } bool DecoderContext::OpenContext(const Decoder& decoder) { - if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) { - LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret)); - return false; - } + if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) { + LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret)); + return false; + } - if (!m_codec_context->hw_device_ctx) { - LOG_INFO(HW_GPU, "Using FFmpeg software decoding"); - } + if (!m_codec_context->hw_device_ctx) { + LOG_INFO(HW_GPU, "Using FFmpeg CPU decoder"); + } - return true; + return true; } bool DecoderContext::SendPacket(const Packet& packet) { - if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0 && ret != AVERROR_EOF) { - LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret)); - return false; - } + if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0 && ret != AVERROR_EOF) { + LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret)); + return false; + } - return true; + return true; } std::shared_ptr DecoderContext::ReceiveFrame() { @@ -256,13 +257,12 @@ std::shared_ptr DecoderContext::ReceiveFrame() { m_temp_frame = std::make_shared(); if (m_codec_context->hw_device_ctx) { - m_temp_frame->SetFormat(AV_PIX_FMT_NV12); + m_temp_frame->SetFormat(PreferredGpuFormat); if (int ret = av_hwframe_transfer_data(m_temp_frame->GetFrame(), intermediate_frame->GetFrame(), 0); ret < 0) { LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret)); return {}; } } else { - m_temp_frame->SetFormat(AV_PIX_FMT_YUV420P); m_temp_frame = std::move(intermediate_frame); } @@ -270,39 +270,39 @@ std::shared_ptr DecoderContext::ReceiveFrame() { } void DecodeApi::Reset() { - m_hardware_context.reset(); - m_decoder_context.reset(); - m_decoder.reset(); + m_hardware_context.reset(); + m_decoder_context.reset(); + m_decoder.reset(); } bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) { - this->Reset(); - m_decoder.emplace(codec); - m_decoder_context.emplace(*m_decoder); + this->Reset(); + m_decoder.emplace(codec); + m_decoder_context.emplace(*m_decoder); - // Enable GPU decoding if requested. - if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) { - m_hardware_context.emplace(); - m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder); - } + // Enable GPU decoding if requested. + if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) { + m_hardware_context.emplace(); + m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder); + } - // Open the decoder context. - if (!m_decoder_context->OpenContext(*m_decoder)) { - this->Reset(); - return false; - } + // Open the decoder context. + if (!m_decoder_context->OpenContext(*m_decoder)) { + this->Reset(); + return false; + } - return true; + return true; } bool DecodeApi::SendPacket(std::span packet_data) { - FFmpeg::Packet packet(packet_data); - return m_decoder_context->SendPacket(packet); + FFmpeg::Packet packet(packet_data); + return m_decoder_context->SendPacket(packet); } std::shared_ptr DecodeApi::ReceiveFrame() { - // Receive raw frame from decoder. - return m_decoder_context->ReceiveFrame(); + // Receive raw frame from decoder. + return m_decoder_context->ReceiveFrame(); } } diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.h b/src/video_core/host1x/ffmpeg/ffmpeg.h index 0864fd0cbe..0dd7c7cb04 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.h +++ b/src/video_core/host1x/ffmpeg/ffmpeg.h @@ -23,15 +23,10 @@ extern "C" { #endif #include +#include #include #include -#if defined(__FreeBSD__) -#include -#else -#include -#endif - #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif From 8d86b615d54525ffc8602264085a9f7faf532309 Mon Sep 17 00:00:00 2001 From: crueter Date: Tue, 22 Jul 2025 14:42:33 -0400 Subject: [PATCH 33/41] [discord] Update RPC ID Signed-off-by: crueter --- src/yuzu/discord_impl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yuzu/discord_impl.cpp b/src/yuzu/discord_impl.cpp index 6b1bff9ef0..04f4ddb3a1 100644 --- a/src/yuzu/discord_impl.cpp +++ b/src/yuzu/discord_impl.cpp @@ -23,7 +23,7 @@ DiscordImpl::DiscordImpl(Core::System& system_) : system{system_} { DiscordEventHandlers handlers{}; // The number is the client ID for yuzu, it's used for images and the // application name - Discord_Initialize("1371246384434380841", &handlers, 1, nullptr); + Discord_Initialize("1397286652128264252", &handlers, 1, nullptr); } DiscordImpl::~DiscordImpl() { @@ -62,7 +62,7 @@ std::string DiscordImpl::GetGameString(const std::string& title) { void DiscordImpl::UpdateGameStatus(bool use_default) { const std::string default_text = "eden is an emulator for the Nintendo Switch"; - const std::string default_image = "https://github.com/pflyly/eden-mirror/raw/branch/master/" + const std::string default_image = "https://git.eden-emu.dev/eden-emu/eden/raw/branch/master/" "dist/qt_themes/default/icons/256x256/eden_named.png"; const std::string url = use_default ? default_image : game_url; s64 start_time = std::chrono::duration_cast( From b66adfe04c2a0bae57d829553aa329a445d8aee0 Mon Sep 17 00:00:00 2001 From: lizzie Date: Tue, 22 Jul 2025 20:49:00 +0200 Subject: [PATCH 34/41] [vulkan] add native cubic filtering (#88) Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/88 This implements the use of VK_FILTER_CUBIC_EXT as a replacement for the software-based bicubic window adapting filter, used primarily for texture sampling in upscaled or downscaled surfaces such as UI, transparency effects, and screen-space elements in Unreal Engine 4 titles. The Vulkan cubic filter is now conditionally enabled if the following are satisfied: The device supports VK_EXT_filter_cubic The format used supports VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT This change improves visual quality while reducing GPU workload by offloading cubic filtering to the driver instead of running custom sampling code in shaders. On supported hardware (e.g. desktop GPUs or high-end Adreno/AMD devices), it results in smoother transitions, improved transparency sampling, and better fidelity with lower shader complexity. Fallback to the original software bicubic logic remains in place for devices lacking the extension or format capability. Tested on several UE4 titles and confirmed to preserve or enhance visual output, especially in alpha-blended and UI-heavy scenes. Co-authored-by: lizzie Co-committed-by: lizzie --- .../convert_abgr8_srgb_to_d24s8.frag | 5 ++-- .../renderer_vulkan/present/filters.cpp | 6 +++- .../renderer_vulkan/present/util.cpp | 28 +++++++++++++++++++ src/video_core/renderer_vulkan/present/util.h | 4 +++ src/video_core/vulkan_common/vulkan_device.h | 11 +++++++- 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/video_core/host_shaders/convert_abgr8_srgb_to_d24s8.frag b/src/video_core/host_shaders/convert_abgr8_srgb_to_d24s8.frag index a9bd21192d..2e464fffa6 100644 --- a/src/video_core/host_shaders/convert_abgr8_srgb_to_d24s8.frag +++ b/src/video_core/host_shaders/convert_abgr8_srgb_to_d24s8.frag @@ -5,9 +5,10 @@ layout(binding = 0) uniform sampler2D color_texture; -// More accurate sRGB to linear conversion +// Even more accurate sRGB to linear conversion +// https://entropymine.com/imageworsener/srgbformula/ float srgbToLinear(float srgb) { - if (srgb <= 0.04045) { + if (srgb <= 0.0404482362771082f) { //assumes it's >= 0 return srgb / 12.92; } else { return pow((srgb + 0.055) / 1.055, 2.4); diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index e5365acde9..7843f38d2c 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -46,8 +46,12 @@ std::unique_ptr MakeBilinear(const Device& device, VkFormat fra } std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format) { + // No need for handrolled shader -- if the VK impl can do it for us ;) + if (device.IsExtFilterCubicSupported()) + return std::make_unique(device, frame_format, CreateCubicSampler(device), + BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); return std::make_unique(device, frame_format, CreateBilinearSampler(device), - BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); + BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); } std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format) { diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 7f27c7c1b5..6874bbae99 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -621,6 +624,31 @@ vk::Sampler CreateNearestNeighborSampler(const Device& device) { return device.GetLogical().CreateSampler(ci_nn); } +vk::Sampler CreateCubicSampler(const Device& device) { + const VkSamplerCreateInfo ci_nn{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .magFilter = VK_FILTER_CUBIC_EXT, + .minFilter = VK_FILTER_CUBIC_EXT, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .mipLodBias = 0.0f, + .anisotropyEnable = VK_FALSE, + .maxAnisotropy = 0.0f, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_NEVER, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE, + }; + + return device.GetLogical().CreateSampler(ci_nn); +} + void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { static constexpr std::array subresources{{{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h index 5b22f0fa82..11810352df 100644 --- a/src/video_core/renderer_vulkan/present/util.h +++ b/src/video_core/renderer_vulkan/present/util.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -54,6 +57,7 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector VkDescriptorSet set, u32 binding); vk::Sampler CreateBilinearSampler(const Device& device); vk::Sampler CreateNearestNeighborSampler(const Device& device); +vk::Sampler CreateCubicSampler(const Device& device); void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, VkExtent2D extent); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index ddc9451a7e..9b78f2e599 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -85,7 +88,8 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \ EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \ - EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) + EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \ + EXTENSION(EXT, FILTER_CUBIC, filter_cubic) // Define extensions which must be supported. #define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ @@ -549,6 +553,11 @@ public: return dynamic_state3_enables; } + /// Returns true if the device supports VK_EXT_filter_cubic + bool IsExtFilterCubicSupported() const { + return extensions.filter_cubic; + } + /// Returns true if the device supports VK_EXT_line_rasterization. bool IsExtLineRasterizationSupported() const { return extensions.line_rasterization; From a974c5a29c267a08e051b8da8b12f9da76683d9b Mon Sep 17 00:00:00 2001 From: Aleksandr Popovich Date: Tue, 22 Jul 2025 21:34:38 +0200 Subject: [PATCH 35/41] [vk] Enable line stipple and depth bound reg transfer (#59) It should improve line stipple accuracy and the depth stencilling as part of the ExtendedDynamicState improvements. Co-authored-by: crueter Signed-off-by: Aleksandr Popovich Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/59 Co-authored-by: Aleksandr Popovich Co-committed-by: Aleksandr Popovich --- .../renderer_vulkan/fixed_pipeline_state.cpp | 8 ++++++++ .../renderer_vulkan/fixed_pipeline_state.h | 8 ++++++++ .../renderer_vulkan/vk_graphics_pipeline.cpp | 19 ++++++++----------- .../renderer_vulkan/vk_pipeline_cache.cpp | 2 +- .../renderer_vulkan/vk_rasterizer.cpp | 15 ++++++++++++++- .../renderer_vulkan/vk_rasterizer.h | 1 + src/video_core/renderer_vulkan/vk_scheduler.h | 1 + .../renderer_vulkan/vk_state_tracker.cpp | 17 ++++++++++++----- .../renderer_vulkan/vk_state_tracker.h | 5 ++++- src/video_core/vulkan_common/vulkan_wrapper.h | 6 ++++++ 10 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 133ab01701..39f4fc6993 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -86,6 +86,12 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); app_stage.Assign(maxwell3d.engine_state); + depth_bounds_min = static_cast(regs.depth_bounds[0]); + depth_bounds_max = static_cast(regs.depth_bounds[1]); + + line_stipple_factor = regs.line_stipple_params.factor; + line_stipple_pattern = regs.line_stipple_params.pattern; + for (size_t i = 0; i < regs.rt.size(); ++i) { color_formats[i] = static_cast(regs.rt[i].format); } @@ -258,6 +264,8 @@ void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs) { Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || regs.viewport_clip_control.geometry_clip == Maxwell::ViewportClipControl::GeometryClip::FrustumZ); + + line_stipple_enable.Assign(regs.line_stipple_enable); } size_t FixedPipelineState::Hash() const noexcept { diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index dfe6d80323..f0b021ca08 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -150,6 +150,7 @@ struct FixedPipelineState { BitField<6, 4, u32> logic_op; BitField<10, 1, u32> logic_op_enable; BitField<11, 1, u32> depth_clamp_disabled; + BitField<12, 1, u32> line_stipple_enable; }; union { u32 raw2; @@ -218,6 +219,7 @@ struct FixedPipelineState { u32 alpha_test_ref; u32 point_size; + std::array viewport_swizzles; union { u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state @@ -233,6 +235,12 @@ struct FixedPipelineState { VideoCommon::TransformFeedbackState xfb_state; + u32 depth_bounds_min; + u32 depth_bounds_max; + + u32 line_stipple_factor; + u32 line_stipple_pattern; + void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features); size_t Hash() const noexcept; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index eb757d68f5..0d07e89b6b 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -701,6 +701,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .depthBiasClamp = 0.0f, .depthBiasSlopeFactor = 0.0f, .lineWidth = 1.0f, + // TODO(alekpop): Transfer from regs }; VkPipelineRasterizationLineStateCreateInfoEXT line_state{ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT, @@ -708,9 +709,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .lineRasterizationMode = key.state.smooth_lines != 0 ? VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT : VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT, - .stippledLineEnable = VK_FALSE, // TODO - .lineStippleFactor = 0, - .lineStipplePattern = 0, + .stippledLineEnable = dynamic.line_stipple_enable ? VK_TRUE : VK_FALSE, + .lineStippleFactor = key.state.line_stipple_factor, + .lineStipplePattern = static_cast(key.state.line_stipple_pattern), }; VkPipelineRasterizationConservativeStateCreateInfoEXT conservative_raster{ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT, @@ -763,8 +764,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .stencilTestEnable = dynamic.stencil_enable, .front = GetStencilFaceState(dynamic.front), .back = GetStencilFaceState(dynamic.back), - .minDepthBounds = 0.0f, - .maxDepthBounds = 0.0f, + .minDepthBounds = static_cast(key.state.depth_bounds_min), + .maxDepthBounds = static_cast(key.state.depth_bounds_max), }; if (dynamic.depth_bounds_enable && !device.IsDepthBoundsSupported()) { LOG_WARNING(Render_Vulkan, "Depth bounds is enabled but not supported"); @@ -805,12 +806,12 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pAttachments = cb_attachments.data(), .blendConstants = {} }; - static_vector dynamic_states{ + static_vector dynamic_states{ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, - VK_DYNAMIC_STATE_LINE_WIDTH, VK_DYNAMIC_STATE_LINE_STIPPLE, + VK_DYNAMIC_STATE_LINE_WIDTH, }; if (key.state.extended_dynamic_state) { static constexpr std::array extended{ @@ -855,9 +856,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT, // additional state3 extensions - - // FIXME(crueter): conservative rasterization is totally broken - // VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT, VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT, VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT, @@ -865,7 +863,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT, VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT, VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT, - VK_DYNAMIC_STATE_TESSELLATION_DOMAIN_ORIGIN_EXT, VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT, VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT, }; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 66835fc1c7..0311c37bd9 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -57,7 +57,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 11; +constexpr u32 CACHE_VERSION = 12; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; template diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 0243693049..33b90a1ec9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -935,6 +935,8 @@ void RasterizerVulkan::UpdateDynamicStates() { UpdateDepthBounds(regs); UpdateStencilFaces(regs); UpdateLineWidth(regs); + // TODO: updating line stipple causes the cmdbuf to die + // UpdateLineStipple(regs); const u8 dynamic_state = Settings::values.dyna_state.GetValue(); @@ -1352,7 +1354,7 @@ void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwe scheduler.Record([enable = regs.conservative_raster_enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetConservativeRasterizationModeEXT( - enable ? VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT + enable ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT); }); } @@ -1368,6 +1370,17 @@ void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& }); } +void RasterizerVulkan::UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs) +{ + if (!state_tracker.TouchLineStipple()) { + return; + } + + scheduler.Record([params = regs.line_stipple_params](vk::CommandBuffer cmdbuf) { + cmdbuf.SetLineStippleEXT(params.factor, static_cast(params.pattern)); + }); +} + void RasterizerVulkan::UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs) { // if (!state_tracker.TouchLi()) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index f562ad1941..ea032635c2 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -180,6 +180,7 @@ private: void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 51e7ab1e1d..54ab8ba52b 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -153,6 +153,7 @@ private: TypedCommand& operator=(TypedCommand&&) = delete; void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const override { + command(cmdbuf, upload_cmdbuf); } diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index ff274ede6e..242d03cf24 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -52,9 +52,6 @@ Flags MakeInvalidationFlags() { VertexInput, StateEnable, PrimitiveRestartEnable, - RasterizerDiscardEnable, - ConservativeRasterizationMode, - LineStippleEnable, DepthBiasEnable, LogicOpEnable, DepthClampEnable, @@ -63,6 +60,9 @@ Flags MakeInvalidationFlags() { ColorMask, BlendEquations, BlendEnable, + ConservativeRasterizationMode, + LineStippleEnable, + LineStippleParams, }; Flags flags{}; for (const int flag : INVALIDATION_FLAGS) { @@ -142,13 +142,12 @@ void SetupDirtyStateEnable(Tables& tables) { setup(OFF(stencil_enable), StencilTestEnable); setup(OFF(primitive_restart.enabled), PrimitiveRestartEnable); setup(OFF(rasterize_enable), RasterizerDiscardEnable); - setup(OFF(conservative_raster_enable), ConservativeRasterizationMode); - setup(OFF(line_stipple_enable), LineStippleEnable); setup(OFF(polygon_offset_point_enable), DepthBiasEnable); setup(OFF(polygon_offset_line_enable), DepthBiasEnable); setup(OFF(polygon_offset_fill_enable), DepthBiasEnable); setup(OFF(logic_op.enable), LogicOpEnable); setup(OFF(viewport_clip_control.geometry_clip), DepthClampEnable); + setup(OFF(line_stipple_enable), LineStippleEnable); } void SetupDirtyDepthCompareOp(Tables& tables) { @@ -221,6 +220,13 @@ void SetupDirtyVertexBindings(Tables& tables) { tables[1][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = flag; } } + +void SetupRasterModes(Tables &tables) { + auto& table = tables[0]; + + table[OFF(line_stipple_params)] = LineStippleParams; + table[OFF(conservative_raster_enable)] = ConservativeRasterizationMode; +} } // Anonymous namespace void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { @@ -243,6 +249,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { SetupDirtyVertexAttributes(tables); SetupDirtyVertexBindings(tables); SetupDirtySpecialOps(tables); + SetupRasterModes(tables); } void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index c7e460ce11..a78d2113fb 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -55,6 +55,7 @@ enum : u8 { RasterizerDiscardEnable, ConservativeRasterizationMode, LineStippleEnable, + LineStippleParams, DepthBiasEnable, StateEnable, LogicOp, @@ -210,7 +211,9 @@ public: return Exchange(Dirty::ConservativeRasterizationMode, false); } - bool TouchLineStippleEnable() { return Exchange(Dirty::ConservativeRasterizationMode, false); } + bool TouchLineStippleEnable() { return Exchange(Dirty::LineStippleEnable, false); } + + bool TouchLineStipple() { return Exchange(Dirty::LineStippleParams, false); } bool TouchDepthBiasEnable() { return Exchange(Dirty::DepthBiasEnable, false); } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index d12a1546e5..5e99444e61 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -240,6 +240,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetConservativeRasterizationModeEXT vkCmdSetConservativeRasterizationModeEXT{}; PFN_vkCmdSetLineRasterizationModeEXT vkCmdSetLineRasterizationModeEXT{}; PFN_vkCmdSetLineStippleEnableEXT vkCmdSetLineStippleEnableEXT{}; + PFN_vkCmdSetLineStippleEXT vkCmdSetLineStippleEXT{}; PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{}; PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT{}; PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT{}; @@ -1452,6 +1453,11 @@ public: dld->vkCmdSetLineStippleEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); } + void SetLineStippleEXT(u32 factor, u16 pattern) const noexcept + { + dld->vkCmdSetLineStippleEXT(handle, factor, pattern); + } + void SetDepthBiasEnableEXT(bool enable) const noexcept { dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); } From 7962c81738e91ed1ff839ef124363563d508234b Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 23 Jul 2025 01:02:10 +0200 Subject: [PATCH 36/41] [spirv] new castings for int8/int16/etc (#86) This commit introduces extended support for low-precision integer casting (int8, int16) in the SPIR-V shader generation pipeline, improving compatibility and performance across both Android and PC platforms. Co-authored-by: CamilleLaVey Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/86 Co-authored-by: lizzie Co-committed-by: lizzie --- .../backend/glasm/emit_glasm_convert.cpp | 24 +++++++++ .../backend/glasm/emit_glasm_instructions.h | 6 +++ .../backend/glsl/emit_glsl_convert.cpp | 30 ++++++++++++ .../backend/glsl/emit_glsl_instructions.h | 6 +++ .../backend/spirv/emit_spirv_convert.cpp | 49 +++++++++++++++++++ .../backend/spirv/emit_spirv_instructions.h | 6 +++ .../frontend/ir/ir_emitter.cpp | 49 ++++++++++++++++++- .../frontend/ir/ir_emitter.h | 3 +- src/shader_recompiler/frontend/ir/opcodes.inc | 6 +++ src/shader_recompiler/frontend/ir/value.h | 1 + 10 files changed, 178 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp index 473dc83642..6aa132e629 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp @@ -225,4 +225,28 @@ void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, Register value) { Convert(ctx, inst, value, "F64", "U64", true); } +void EmitConvertU16U32(EmitContext& ctx, IR::Inst& inst, Register value) { + Convert(ctx, inst, value, "U32", "U16", false); +} + +void EmitConvertU32U16(EmitContext& ctx, IR::Inst& inst, Register value) { + Convert(ctx, inst, value, "U16", "U32", false); +} + +void EmitConvertU8U32(EmitContext& ctx, IR::Inst& inst, Register value) { + Convert(ctx, inst, value, "U32", "U8", false); +} + +void EmitConvertU32U8(EmitContext& ctx, IR::Inst& inst, Register value) { + Convert(ctx, inst, value, "U8", "U32", false); +} + +void EmitConvertS32S8(EmitContext& ctx, IR::Inst& inst, Register value) { + Convert(ctx, inst, value, "S8", "S32", false); +} + +void EmitConvertS32S16(EmitContext& ctx, IR::Inst& inst, Register value) { + Convert(ctx, inst, value, "S16", "S32", false); +} + } // namespace Shader::Backend::GLASM diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h index c5d4835fa9..e4cc5e694b 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h @@ -541,6 +541,12 @@ void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, Register value); void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, Register value); void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value); void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, Register value); +void EmitConvertU16U32(EmitContext& ctx, IR::Inst& inst, Register value); +void EmitConvertU32U16(EmitContext& ctx, IR::Inst& inst, Register value); +void EmitConvertU8U32(EmitContext& ctx, IR::Inst& inst, Register value); +void EmitConvertU32U8(EmitContext& ctx, IR::Inst& inst, Register value); +void EmitConvertS32S8(EmitContext& ctx, IR::Inst& inst, Register value); +void EmitConvertS32S16(EmitContext& ctx, IR::Inst& inst, Register value); void EmitBindlessImageSampleImplicitLod(EmitContext&); void EmitBindlessImageSampleExplicitLod(EmitContext&); void EmitBindlessImageSampleDrefImplicitLod(EmitContext&); diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp index aeacb5a64c..cf404f7b51 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp @@ -68,6 +68,36 @@ void EmitConvertU32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::I NotImplemented(); } +void EmitConvertU16U32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, + [[maybe_unused]] std::string_view value) { + NotImplemented(); +} + +void EmitConvertU32U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, + [[maybe_unused]] std::string_view value) { + NotImplemented(); +} + +void EmitConvertU8U32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, + [[maybe_unused]] std::string_view value) { + NotImplemented(); +} + +void EmitConvertU32U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, + [[maybe_unused]] std::string_view value) { + NotImplemented(); +} + +void EmitConvertS32S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, + [[maybe_unused]] std::string_view value) { + NotImplemented(); +} + +void EmitConvertS32S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, + [[maybe_unused]] std::string_view value) { + NotImplemented(); +} + void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { ctx.AddU32("{}=uint({});", inst, value); } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h index 9e405b37fb..fb669d399e 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h +++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h @@ -611,6 +611,12 @@ void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, std::string_view value); void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, std::string_view value); void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); +void EmitConvertU16U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); +void EmitConvertU32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value); +void EmitConvertU8U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); +void EmitConvertU32U8(EmitContext& ctx, IR::Inst& inst, std::string_view value); +void EmitConvertS32S8(EmitContext& ctx, IR::Inst& inst, std::string_view value); +void EmitConvertS32S16(EmitContext& ctx, IR::Inst& inst, std::string_view value); void EmitBindlessImageSampleImplicitLod(EmitContext&); void EmitBindlessImageSampleExplicitLod(EmitContext&); void EmitBindlessImageSampleDrefImplicitLod(EmitContext&); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp index 7b6e34f315..bfe71358b1 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp @@ -268,4 +268,53 @@ Id EmitConvertF64U64(EmitContext& ctx, Id value) { return ctx.OpConvertUToF(ctx.F64[1], value); } +Id EmitConvertU16U32(EmitContext& ctx, Id value) { + if (ctx.profile.support_int16) { + return ctx.OpUConvert(ctx.U16, value); + } else { + return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u)); + } +} + +Id EmitConvertU32U16(EmitContext& ctx, Id value) { + if (ctx.profile.support_int16) { + return ctx.OpUConvert(ctx.U32[1], value); + } else { + return ExtractU16(ctx, value); + } +} + +Id EmitConvertU8U32(EmitContext& ctx, Id value) { + if (ctx.profile.support_int8) { + return ctx.OpUConvert(ctx.U8, value); + } else { + return ExtractU8(ctx, value); + } +} + +Id EmitConvertU32U8(EmitContext& ctx, Id value) { + if (ctx.profile.support_int8) { + return ctx.OpUConvert(ctx.U32[1], value); + } else { + return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u)); + } +} + +// in signed +Id EmitConvertS32S8(EmitContext& ctx, Id value) { + if (ctx.profile.support_int8) { + return ctx.OpSConvert(ctx.U32[1], value); + } else { + return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u)); + } +} + +Id EmitConvertS32S16(EmitContext& ctx, Id value) { + if (ctx.profile.support_int16) { + return ctx.OpSConvert(ctx.U32[1], value); + } else { + return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u)); + } +} + } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index b8e98e6c58..52be1369c2 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -503,6 +503,12 @@ Id EmitConvertF64U8(EmitContext& ctx, Id value); Id EmitConvertF64U16(EmitContext& ctx, Id value); Id EmitConvertF64U32(EmitContext& ctx, Id value); Id EmitConvertF64U64(EmitContext& ctx, Id value); +Id EmitConvertU16U32(EmitContext& ctx, Id value); +Id EmitConvertU32U16(EmitContext& ctx, Id value); +Id EmitConvertU8U32(EmitContext& ctx, Id value); +Id EmitConvertU32U8(EmitContext& ctx, Id value); +Id EmitConvertS32S8(EmitContext& ctx, Id value); +Id EmitConvertS32S16(EmitContext& ctx, Id value); Id EmitBindlessImageSampleImplicitLod(EmitContext&); Id EmitBindlessImageSampleExplicitLod(EmitContext&); Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&); diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index e1e46cb81c..a2fa9b1588 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp @@ -1730,10 +1730,36 @@ F16F32F64 IREmitter::ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool i : ConvertUToF(dest_bitsize, src_bitsize, value, control); } -U32U64 IREmitter::UConvert(size_t result_bitsize, const U32U64& value) { +U8U16U32U64 IREmitter::UConvert(size_t result_bitsize, const U8U16U32U64& value) { switch (result_bitsize) { + case 8: + switch (value.Type()) { + case Type::U8: + // Nothing to do + return value; + case Type::U32: + return Inst(Opcode::ConvertU8U32, value); + default: + break; + } + break; + case 16: + switch (value.Type()) { + case Type::U16: + // Nothing to do + return value; + case Type::U32: + return Inst(Opcode::ConvertU16U32, value); + default: + break; + } + break; case 32: switch (value.Type()) { + case Type::U8: + return Inst(Opcode::ConvertU32U8, value); + case Type::U16: + return Inst(Opcode::ConvertU32U16, value); case Type::U32: // Nothing to do return value; @@ -1753,10 +1779,31 @@ U32U64 IREmitter::UConvert(size_t result_bitsize, const U32U64& value) { default: break; } + break; + default: + break; } throw NotImplementedException("Conversion from {} to {} bits", value.Type(), result_bitsize); } +U8U16U32U64 IR::IREmitter::SConvert(size_t result_bitsize, const U8U16U32U64& value) { + switch (result_bitsize) { + case 32: + switch (value.Type()) { + case Type::U8: + return Inst(Opcode::ConvertS32S8, value); + case Type::U16: + return Inst(Opcode::ConvertS32S16, value); + default: + break; + } + break; + default: + break; + } + throw NotImplementedException("Signed Conversion from {} to {} bits", value.Type(), result_bitsize); +} + F16F32F64 IREmitter::FPConvert(size_t result_bitsize, const F16F32F64& value, FpControl control) { switch (result_bitsize) { case 16: diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index 073091ea58..48828f3ecc 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h @@ -305,7 +305,8 @@ public: [[nodiscard]] F16F32F64 ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool is_signed, const Value& value, FpControl control = {}); - [[nodiscard]] U32U64 UConvert(size_t result_bitsize, const U32U64& value); + [[nodiscard]] U8U16U32U64 UConvert(size_t result_bitsize, const U8U16U32U64& value); + [[nodiscard]] U8U16U32U64 SConvert(size_t result_bitsize, const U8U16U32U64& value); [[nodiscard]] F16F32F64 FPConvert(size_t result_bitsize, const F16F32F64& value, FpControl control = {}); diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 2f880a8596..4a6da06f66 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc @@ -475,6 +475,12 @@ OPCODE(ConvertF64U8, F64, U32, OPCODE(ConvertF64U16, F64, U32, ) OPCODE(ConvertF64U32, F64, U32, ) OPCODE(ConvertF64U64, F64, U64, ) +OPCODE(ConvertU16U32, U16, U32, ) +OPCODE(ConvertU32U16, U32, U16, ) +OPCODE(ConvertU8U32, U8, U32, ) +OPCODE(ConvertU32U8, U32, U8, ) +OPCODE(ConvertS32S8, U32, U8, ) +OPCODE(ConvertS32S16, U32, U16, ) // Image operations OPCODE(BindlessImageSampleImplicitLod, F32x4, U32, Opaque, Opaque, Opaque, ) diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index c27546b0e1..514344dfe3 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h @@ -269,6 +269,7 @@ using F32 = TypedValue; using F64 = TypedValue; using U32U64 = TypedValue; using F32F64 = TypedValue; +using U8U16U32U64 = TypedValue; using U16U32U64 = TypedValue; using F16F32F64 = TypedValue; using UAny = TypedValue; From 275d64e653b29b494d91d6e36da6bb0afb42b906 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 23 Jul 2025 09:53:45 +0200 Subject: [PATCH 37/41] [docs] add into docs/ and make links local (#93) Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/93 Co-authored-by: lizzie Co-committed-by: lizzie --- .ci/linux/build.sh | 14 ++- .ci/linux/package.sh | 9 +- .ci/windows/cygwin.bat | 19 --- .ci/windows/install-vulkan-sdk.ps1 | 2 +- .ci/windows/qt-envvars.sh | 7 -- .gitignore | 2 +- README.md | 14 ++- docs/build/Android.md | 42 +++++++ docs/build/FreeBSD.md | 81 ++++++++++++ docs/build/Linux.md | 135 ++++++++++++++++++++ docs/build/Windows.md | 195 +++++++++++++++++++++++++++++ docs/build/macOS.md | 105 ++++++++++++++++ 12 files changed, 584 insertions(+), 41 deletions(-) delete mode 100755 .ci/windows/cygwin.bat delete mode 100755 .ci/windows/qt-envvars.sh create mode 100644 docs/build/Android.md create mode 100644 docs/build/FreeBSD.md create mode 100644 docs/build/Linux.md create mode 100644 docs/build/Windows.md create mode 100644 docs/build/macOS.md diff --git a/.ci/linux/build.sh b/.ci/linux/build.sh index 093b30a7ea..a92cb3fd91 100755 --- a/.ci/linux/build.sh +++ b/.ci/linux/build.sh @@ -3,7 +3,6 @@ # SPDX-FileCopyrightText: 2025 eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later -export ARCH="$(uname -m)" case "$1" in amd64|"") @@ -11,15 +10,15 @@ case "$1" in ARCH="amd64_v3" ARCH_FLAGS="-march=x86-64-v3" ;; - steamdeck) + steamdeck|zen2) echo "Making Steam Deck (Zen 2) optimized build of Eden" ARCH="steamdeck" ARCH_FLAGS="-march=znver2 -mtune=znver2" ;; - rog-ally|allyx) + rog-ally|allyx|zen4) echo "Making ROG Ally X (Zen 4) optimized build of Eden" ARCH="rog-ally-x" - ARCH_FLAGS="-march=znver3 -mtune=znver4" # GH actions runner is a Zen 3 CPU, so a small workaround + ARCH_FLAGS="-march=znver4 -mtune=znver4" ;; legacy) echo "Making amd64 generic build of Eden" @@ -36,8 +35,13 @@ case "$1" in ARCH=armv9 ARCH_FLAGS="-march=armv9-a -mtune=generic -w" ;; + native) + echo "Making native build of Eden" + ARCH="$(uname -m)" + ARCH_FLAGS="-march=native -mtune=native" + ;; *) - echo "Invalid target $1 specified, must be one of amd64, steamdeck, allyx, rog-ally, legacy, aarch64, armv9" + echo "Invalid target $1 specified, must be one of native, amd64, steamdeck, zen2, allyx, rog-ally, zen4, legacy, aarch64, armv9" exit 1 ;; esac diff --git a/.ci/linux/package.sh b/.ci/linux/package.sh index 21fd72cb08..41e07ea207 100755 --- a/.ci/linux/package.sh +++ b/.ci/linux/package.sh @@ -16,11 +16,11 @@ case "$1" in echo "Packaging amd64-v3 optimized build of Eden" ARCH="amd64_v3" ;; - steamdeck) + steamdeck|zen2) echo "Packaging Steam Deck (Zen 2) optimized build of Eden" ARCH="steamdeck" ;; - rog-ally|allyx) + rog-ally|allyx|zen4) echo "Packaging ROG Ally X (Zen 4) optimized build of Eden" ARCH="rog-ally-x" ;; @@ -36,6 +36,11 @@ case "$1" in echo "Packaging armv9-a build of Eden" ARCH=armv9 ;; + native) + echo "Packaging native build of Eden" + ARCH="$BASE_ARCH" + ;; + esac export BUILDDIR="$2" diff --git a/.ci/windows/cygwin.bat b/.ci/windows/cygwin.bat deleted file mode 100755 index e772780fac..0000000000 --- a/.ci/windows/cygwin.bat +++ /dev/null @@ -1,19 +0,0 @@ -echo off - -call C:\tools\cygwin\cygwinsetup.exe -q -P autoconf,automake,libtool,make,pkg-config - -REM Create wrapper batch files for Cygwin tools in a directory that will be in PATH -REM uncomment this for first-run only -REM call mkdir C:\cygwin-wrappers - -REM Create autoconf.bat wrapper -call echo @echo off > C:\cygwin-wrappers\autoconf.bat -call echo C:\tools\cygwin\bin\bash.exe -l -c "autoconf %%*" >> C:\cygwin-wrappers\autoconf.bat - -REM Add other wrappers if needed for other Cygwin tools -call echo @echo off > C:\cygwin-wrappers\automake.bat -call echo C:\tools\cygwin\bin\bash.exe -l -c "automake %%*" >> C:\cygwin-wrappers\automake.bat - -REM Add the wrappers directory to PATH -call echo C:\cygwin-wrappers>>"%GITHUB_PATH%" -call echo C:\tools\cygwin\bin>>"%GITHUB_PATH%" diff --git a/.ci/windows/install-vulkan-sdk.ps1 b/.ci/windows/install-vulkan-sdk.ps1 index de218d90ad..ca8c64b5cd 100755 --- a/.ci/windows/install-vulkan-sdk.ps1 +++ b/.ci/windows/install-vulkan-sdk.ps1 @@ -3,7 +3,7 @@ $ErrorActionPreference = "Stop" -$VulkanSDKVer = "1.3.250.1" +$VulkanSDKVer = "1.4.321.1" $ExeFile = "VulkanSDK-$VulkanSDKVer-Installer.exe" $Uri = "https://sdk.lunarg.com/sdk/download/$VulkanSDKVer/windows/$ExeFile" $Destination = "./$ExeFile" diff --git a/.ci/windows/qt-envvars.sh b/.ci/windows/qt-envvars.sh deleted file mode 100755 index ec314b863f..0000000000 --- a/.ci/windows/qt-envvars.sh +++ /dev/null @@ -1,7 +0,0 @@ -# This is specific to the CI runner and is unlikely to work on your machine. -QTDIR="/c/Qt/6.9.0/msvc2022_64" - -export QT_ROOT_DIR="$QTDIR" -export QT_PLUGIN_PATH="$QTDIR/plugins" -export QML2_IMPORT_PATH="$QTDIR/qml" -export PATH="${PATH};$QTDIR/bin" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4417d3f132..83881117ac 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later # Build directory -[Bb]uild*/ +/[Bb]uild*/ doc-build/ AppDir/ uruntime diff --git a/README.md b/README.md index 3cf94390fb..ce60e9e18f 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,13 @@


- Eden + Eden
Eden

-

Eden is the world's most popular open-source Nintendo Switch emulator, forked from the Yuzu emulator — started by former Citron developer Camille LaVey and the Eden team. +

Eden is a open-source Nintendo Switch emulator, forked from the Yuzu emulator — started by former Citron developer Camille LaVey and the Eden team.
It is written in C++ with portability in mind, and we actively maintain builds for Windows, Linux and Android.

@@ -54,13 +54,15 @@ You can also contact any of the developers on Discord to learn more about the cu ## Building -* **Windows**: [Windows Building Guide](https://git.eden-emu.dev/eden-emu/eden/wiki/Building-for-Windows.-) -* **Linux**: [Linux Building Guide](https://git.eden-emu.dev/eden-emu/eden/wiki/Building-for-Linux.-) -* **Android**: [Android Building Guide](https://git.eden-emu.dev/eden-emu/eden/wiki/Building-for-Android.-) +* **Windows**: [Windows Building Guide](./docs/build/Windows.md) +* **Linux**: [Linux Building Guide](./docs/build/Linux.md) +* **Android**: [Android Building Guide](./docs/build/Android.md) +* **FreeBSD**: [FreeBSD Building Guide](./docs/build/FreeBSD.md) +* **macOS**: [macOS Building Guide](./docs/build/macOS.md) ## Download -You will be able to download the latest releases from [here](https://github.com/eden-emulator/Releases/releases). +You can download the latest releases from [here](https://github.com/eden-emulator/Releases/releases). ## Support diff --git a/docs/build/Android.md b/docs/build/Android.md new file mode 100644 index 0000000000..4bb1c868b6 --- /dev/null +++ b/docs/build/Android.md @@ -0,0 +1,42 @@ +# Note: These build instructions are a work-in-progress. + +## Dependencies +* [Android Studio](https://developer.android.com/studio) +* [NDK 25.2.9519653 and CMake 3.22.1](https://developer.android.com/studio/projects/install-ndk#default-version) +* [Git](https://git-scm.com/download) + +### WINDOWS ONLY - Additional Dependencies + * **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select "Desktop development with C++" support in the installer. Make sure to update to the latest version if already installed.** + * **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.** + - A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`. + +## Cloning Eden with Git +``` +git clone --recursive https://git.eden-emu.dev/eden-emu/eden.git +``` +Eden by default will be cloned into - +* `C:\Users\\eden` on Windows +* `~/eden` on Linux +* And wherever on macOS + +## Building +1. Start Android Studio, on the startup dialog select `Open`. +2. Navigate to the `eden/src/android` directory and click on `OK`. +3. In `Build > Select Build Variant`, select `release` or `relWithDebInfo` as the "Active build variant". +4. Build the project with `Build > Make Project` or run it on an Android device with `Run > Run 'app'`. + +## Building with Terminal +1. Download the SDK and NDK from Android Studio. +2. Navigate to SDK and NDK paths. +3. Then set ANDROID_SDK_ROOT and ANDROID_NDK_ROOT in terminal via +`export ANDROID_SDK_ROOT=path/to/sdk` +`export ANDROID_NDK_ROOT=path/to/ndk`. +4. Navigate to `eden/src/android`. +5. Then Build with `./gradlew assemblerelWithDebInfo`. +6. To build the optimised build use `./gradlew assembleGenshinSpoofRelWithDebInfo`. + +### Script +A convenience script for building is provided in `.ci/android/build.sh`. The built APK can be put into an `artifacts` directory via `.ci/android/package.sh`. On Windows, these must be done in the Git Bash or MinGW terminal. + +### Additional Resources +https://developer.android.com/studio/intro diff --git a/docs/build/FreeBSD.md b/docs/build/FreeBSD.md new file mode 100644 index 0000000000..9e95a6d143 --- /dev/null +++ b/docs/build/FreeBSD.md @@ -0,0 +1,81 @@ +## One word of caution before proceeding. +This is not the usual or preferred way to build programs on FreeBSD. +As of writing there is no official fresh port available for eden-emu, but it is in the works. +After it is available you can find a link to the eden-emu fresh port here and on Escarys github repo. +See this build as an App Image alternative for FreeBSD. + +## Dependencies. +Before we start we need some dependencies. +These dependencies are generally needed to build eden-emu on FreeBSD. + +``` +devel/cmake +devel/sdl20 +devel/boost-libs +devel/catch2 +devel/libfmt +devel/nlohmann-json +devel/ninja +devel/nasm +devel/autoconf +devel/pkg-config +devel/qt6-base + +multimedia/ffnvcodec-headers +multimedia/ffmpeg + +audio/opus + +archivers/liblz4 + +lang/gcc12 + +graphics/glslang +graphics/vulkan-utility-libraries +``` + +--- + +### Build preparations: +Run the following command to clone eden with git: +```sh +git clone --recursive https://git.eden-emu.dev/eden-emu/eden +``` +You usually want to add the `--recursive` parameter as it also takes care of the external dependencies for you. + +Now change into the eden directory and create a build directory there: +```sh +cd eden +mkdir build +``` + +Change into that build directory: +```sh +cd build +``` + +Now choose one option either 1 or 2, but not both as one option overwrites the other. + +#### 1. Building in Release Mode (usually preferred and the most performant choice): +```sh +cmake .. -GNinja -DYUZU_TESTS=OFF +``` + +#### 2. Building in Release Mode with debugging symbols (useful if you want to debug errors for a eventual fix): +```sh +cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=ON +``` + +Build the emulator locally: +```sh +ninja +``` + +Optional: If you wish to install eden globally onto your system issue the following command: +```sh +sudo ninja install +``` +OR +```sh +doas -- ninja install +``` diff --git a/docs/build/Linux.md b/docs/build/Linux.md new file mode 100644 index 0000000000..4aad874ac2 --- /dev/null +++ b/docs/build/Linux.md @@ -0,0 +1,135 @@ +### Dependencies + +You'll need to download and install the following to build Eden: + + * [GCC](https://gcc.gnu.org/) v11+ (for C++20 support) & misc + * If GCC 12 is installed, [Clang](https://clang.llvm.org/) v14+ is required for compiling + * [CMake](https://www.cmake.org/) 3.22+ + +The following are handled by Eden's externals: + + * [FFmpeg](https://ffmpeg.org/) + * [SDL2](https://www.libsdl.org/download-2.0.php) 2.0.18+ + * [opus](https://opus-codec.org/downloads/) + +All other dependencies will be downloaded by [vcpkg](https://vcpkg.io/) if needed: + + * [Boost](https://www.boost.org/users/download/) 1.79.0+ + * [Catch2](https://github.com/catchorg/Catch2) 2.13.7 - 2.13.9 + * [fmt](https://fmt.dev/) 8.0.1+ + * [lz4](http://www.lz4.org) 1.8+ + * [nlohmann_json](https://github.com/nlohmann/json) 3.8+ + * [OpenSSL](https://www.openssl.org/source/) + * [ZLIB](https://www.zlib.net/) 1.2+ + * [zstd](https://facebook.github.io/zstd/) 1.5+ + +If an ARM64 build is intended, export `VCPKG_FORCE_SYSTEM_BINARIES=1`. + +Dependencies are listed here as commands that can be copied/pasted. Of course, they should be inspected before being run. + +- Arch / Manjaro: + - `sudo pacman -Syu --needed base-devel boost catch2 cmake ffmpeg fmt git glslang libzip lz4 mbedtls ninja nlohmann-json openssl opus qt6-base qt6-multimedia sdl2 zlib zstd zip unzip` + - Building with QT Web Engine requires `qt6-webengine` as well. + - Proper wayland support requires `qt6-wayland` + - GCC 11 or later is required. +- Ubuntu / Linux Mint / Debian: + - `sudo apt-get install autoconf cmake g++-11 gcc-11 git glslang-tools libasound2 libboost-context-dev libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qtbase6-dev qtbase6-private-dev qtwebengine6-dev qtmultimedia6-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev` + - Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required. + - Users need to manually specify building with QT Web Engine enabled. This is done using the parameter `-DYUZU_USE_QT_WEB_ENGINE=ON` when running CMake. + - Users need to manually specify building with GCC 11. This can be done by adding the parameters `-DCMAKE_C_COMPILER=gcc-11 -DCMAKE_CXX_COMPILER=g++-11` when running CMake. i.e. + - Users need to manually disable building SDL2 from externals if they intend to use the version provided by their system by adding the parameters `-DYUZU_USE_EXTERNAL_SDL2=OFF` + +``` +git submodule update --init --recursive +cmake .. -GNinja -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_CXX_COMPILER=g++-11 +``` + +- Fedora: + - `sudo dnf install autoconf ccache cmake fmt-devel gcc{,-c++} glslang hidapi-devel json-devel libtool libusb1-devel libzstd-devel lz4-devel nasm ninja-build openssl-devel pulseaudio-libs-devel qt5-linguist qt5-qtbase{-private,}-devel qt5-qtwebengine-devel qt5-qtmultimedia-devel speexdsp-devel wayland-devel zlib-devel ffmpeg-devel libXext-devel` + - Fedora 32 or later is required. + - Due to GCC 12, Fedora 36 or later users need to install `clang`, and configure CMake to use it via `-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang` + - CMake arguments to force system libraries: + - SDL2: `-DYUZU_USE_BUNDLED_SDL2=OFF -DYUZU_USE_EXTERNAL_SDL2=OFF` + - FFmpeg: `-DYUZU_USE_EXTERNAL_FFMPEG=OFF` + - [RPM Fusion](https://rpmfusion.org/) (free) is required to install `ffmpeg-devel` + +### Cloning Eden with Git + +**Master:** + + ```bash + git clone --recursive https://git.eden-emu.dev/eden-emu/eden + cd eden + ``` + +The `--recursive` option automatically clones the required Git submodules. + +### Building Eden in Release Mode (Optimised) + +If you need to run ctests, you can disable `-DYUZU_TESTS=OFF` and install Catch2. + +```bash +mkdir build && cd build +cmake .. -GNinja -DYUZU_TESTS=OFF +ninja +sudo ninja install +``` +You may also want to include support for Discord Rich Presence by adding `-DUSE_DISCORD_PRESENCE=ON` after `cmake ..` + +`-DYUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS=OFF` might be needed if ninja command failed with `undefined reference to symbol 'spvOptimizerOptionsCreate`, reason currently unknown + +Optionally, you can use `cmake-gui ..` to adjust various options (e.g. disable the Qt GUI). + +### Building Eden in Debug Mode (Slow) + +```bash +mkdir build && cd build +cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DYUZU_TESTS=OFF +ninja +``` + +### Building with debug symbols + +```bash +mkdir build && cd build +cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU -DYUZU_TESTS=OFF +ninja +``` + +### Building with Scripts +A convenience script for building is provided in `.ci/linux/build.sh`. You must provide an arch target for optimization, e.g. `.ci/linux/build.sh amd64`. Valid targets: +- `legacy`: x86_64 generic, only needed for CPUs older than 2013 or so +- `amd64`: x86_64-v3, for CPUs newer than 2013 or so +- `steamdeck` / `zen2`: For Steam Deck or Zen >= 2 AMD CPUs (untested on Intel) +- `rog-ally` / `allyx` / `zen4`: For ROG Ally X or Zen >= 4 AMD CPUs (untested on Intel) +- `aarch64`: For armv8-a CPUs, older than mid-2021 or so +- `armv9`: For armv9-a CPUs, newer than mid-2021 or so +- `native`: Optimize to your native host architecture + +Extra flags to pass to CMake should be passed after the arch target. + +Additional environment variables can be used to control building: +- `NPROC`: Number of threads to use for compilation (defaults to all) +- `TARGET`: Set to `appimage` to disable standalone `eden-cli` and `eden-room` executables +- `BUILD_TYPE`: Sets the build type to use. Defaults to `Release` + +The following environment variables are boolean flags. Set to `true` to enable or `false` to disable: +- `DEVEL` (default FALSE): Disable Qt update checker +- `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine +- `USE_MULTIMEDIA` (default TRUE): Enable Qt Multimedia + +After building, an AppImage can be packaged via `.ci/linux/package.sh`. This script takes the same arch targets as the build script. If the build was created in a different directory, you can specify its path relative to the source directory, e.g. `.ci/linux/package.sh amd64 build-appimage`. Additionally, set the `DEVEL` environment variable to `true` to change the app name to `Eden Nightly`. + +### Running without installing + +After building, the binaries `eden` and `eden-cmd` (depending on your build options) will end up in `build/bin/`. + + ```bash + # SDL + cd build/bin/ + ./eden-cmd + + # Qt + cd build/bin/ + ./eden + ``` diff --git a/docs/build/Windows.md b/docs/build/Windows.md new file mode 100644 index 0000000000..117398ea09 --- /dev/null +++ b/docs/build/Windows.md @@ -0,0 +1,195 @@ +# THIS GUIDE IS INTENDED FOR DEVELOPERS ONLY, SUPPORT WILL ONLY BE GIVEN IF YOU'RE A DEVELOPER. + +## Method I: MSVC Build for Windows + +### Minimal Dependencies + +On Windows, all library dependencies are automatically included within the `externals` folder, or can be downloaded on-demand. To build Eden, you need to install: + + * **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select C++ support in the installer. Make sure to update to the latest version if already installed.** + * **[CMake](https://cmake.org/download/)** - Used to generate Visual Studio project files. Does not matter if either 32-bit or 64-bit version is installed. + * **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.** + - A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`. + + ![2](https://i.imgur.com/giDwuTm.png) + + * **Git** - We recommend [Git for Windows](https://gitforwindows.org). + + ![3](https://i.imgur.com/UeSzkBw.png) + + * While installing Git Bash, you should tell it to include Git in your system path. (Choose the "Git from the command line and also from 3rd-party software" option.) If you missed that, don't worry, you'll just have to manually tell CMake where your git.exe is, since it's used to include version info into the built executable. + + ![4](https://i.imgur.com/x0rRs1t.png) + +### Cloning Eden with Git + +**Master:** + ```cmd + git clone --recursive https://git.eden-emu.dev/eden-emu/eden + cd eden + ``` + + ![9](https://i.imgur.com/CcxIAht.png) + +* *(Note: eden by default downloads to `C:\Users\\eden` (Master) + +### Building + +* Open the CMake GUI application and point it to the `eden` (Master) + + ![10](https://i.imgur.com/qOslIWv.png) + +* For the build directory, use a `/build` subdirectory inside the source directory or some other directory of your choice. (Tell CMake to create it.) + +* Click the "Configure" button and choose `Visual Studio 17 2022`, with `x64` for the optional platform. + + ![12](https://i.imgur.com/DKiREaK.png) + + * *(Note: If you used GitHub's own app to clone, run `git submodule update --init --recursive` to get the remaining dependencies)* + +* If you get an error about missing packages, enable `YUZU_USE_BUNDLED_VCPKG`, and then click Configure again. + + * *(You may also want to disable `YUZU_TESTS` in this case since Catch2 is not yet supported with this.)* + + ![13](https://user-images.githubusercontent.com/22451773/180585999-07316d6e-9751-4d11-b957-1cf57cd7cd58.png) + +* Click "Generate" to create the project files. + + ![15](https://i.imgur.com/5LKg92k.png) + +* Open the solution file `yuzu.sln` in Visual Studio 2022, which is located in the build folder. + + ![16](https://i.imgur.com/208yMml.png) + +* Depending if you want a graphical user interface or not (`eden` has the graphical user interface, while `eden-cmd` doesn't), select `eden` or `eden-cmd` in the Solution Explorer, right-click and `Set as StartUp Project`. + + ![17](https://i.imgur.com/nPMajnn.png) ![18](https://i.imgur.com/BDMLzRZ.png) + +* Select the appropriate build type, Debug for debug purposes or Release for performance (in case of doubt choose Release). + + ![19](https://i.imgur.com/qxg4roC.png) + +* Right-click the project you want to build and press Build in the submenu or press F5. + + ![20](https://i.imgur.com/CkQgOFW.png) + +## Method II: MinGW-w64 Build with MSYS2 + +### Prerequisites to install + +* [MSYS2](https://www.msys2.org) +* [Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows) - **Make sure to select Latest SDK.** +* Make sure to follow the instructions and update to the latest version by running `pacman -Syu` as many times as needed. + +### Install eden dependencies for MinGW-w64 + +* Open the `MSYS2 MinGW 64-bit` (mingw64.exe) shell +* Download and install all dependencies using: `pacman -Syu git make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-cmake mingw-w64-x86_64-python-pip mingw-w64-x86_64-qt6 mingw-w64-x86_64-toolchain autoconf libtool automake-wrapper` +* Add MinGW binaries to the PATH: `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc` +* Add glslangValidator to the PATH: `echo 'PATH=$(readlink -e /c/VulkanSDK/*/Bin/):$PATH' >> ~/.bashrc` + +### Clone the eden repository with Git + + ```bash + git clone --recursive https://git.eden-emu.dev/eden-emu/eden + cd eden + ``` + +### Run the following commands to build eden (dynamically linked build) + +```bash +mkdir build && cd build +cmake -G "MSYS Makefiles" -DYUZU_USE_BUNDLED_VCPKG=ON -DYUZU_TESTS=OFF .. +make -j$(nproc) +# test eden out with +./bin/eden.exe +``` + +* *(Note: This build is not a static build meaning that you need to include all of the DLLs with the .exe in order to use it!)* + +e.g. +```Bash +cp externals/ffmpeg-*/bin/*.dll bin/ +``` + +Bonus Note: Running programs from inside `MSYS2 MinGW x64` shell has a different %PATH% than directly from explorer. This different %PATH% has the locations of the other DLLs required. +![image](https://user-images.githubusercontent.com/190571/165000848-005e8428-8a82-41b1-bb4d-4ce7797cdac8.png) + + +### Building without Qt (Optional) + +Doesn't require the rather large Qt dependency, but you will lack a GUI frontend: + + * Pass the `-DENABLE_QT=no` flag to cmake + +## Method III: CLion Environment Setup + +### Minimal Dependencies + +To build eden, you need to install the following: + +* [CLion](https://www.jetbrains.com/clion/) - This IDE is not free; for a free alternative, check Method I +* [Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows) - Make sure to select the Latest SDK. + +### Cloning eden with CLion + +* Clone the Repository: + +![1](https://user-images.githubusercontent.com/42481638/216899046-0d41d7d6-8e4d-4ed2-9587-b57088af5214.png) +![2](https://user-images.githubusercontent.com/42481638/216899061-b2ea274a-e88c-40ae-bf0b-4450b46e9fea.png) +![3](https://user-images.githubusercontent.com/42481638/216899076-0e5988c4-d431-4284-a5ff-9ecff973db76.png) + + + +### Building & Setup + +* Once Cloned, You will be taken to a prompt like the image below: + +![4](https://user-images.githubusercontent.com/42481638/216899092-3fe4cec6-a540-44e3-9e1e-3de9c2fffc2f.png) + +* Set the settings to the image below: +* Change `Build type: Release` +* Change `Name: Release` +* Change `Toolchain Visual Studio` +* Change `Generator: Let CMake decide` +* Change `Build directory: build` + +![5](https://user-images.githubusercontent.com/42481638/216899164-6cee8482-3d59-428f-b1bc-e6dc793c9b20.png) + +* Click OK; now Clion will build a directory and index your code to allow for IntelliSense. Please be patient. +* Once this process has been completed (No loading bar bottom right), you can now build eden +* In the top right, click on the drop-down menu, select all configurations, then select eden + +![6](https://user-images.githubusercontent.com/42481638/216899226-975048e9-bc6d-4ec1-bc2d-bd8a1e15ed04.png) + +* Now run by clicking the play button or pressing Shift+F10, and eden will auto-launch once built. + +![7](https://user-images.githubusercontent.com/42481638/216899275-d514ec6a-e563-470e-81e2-3e04f0429b68.png) + +## Building from the command line with MSVC + +```cmd +git clone --recursive https://git.eden-emu.dev/eden-emu/eden +cd eden +mkdir build +cd build +cmake .. -G "Visual Studio 17 2022" -A x64 +cmake --build . --config Release +``` + +### Building with Scripts +A convenience script for building is provided in `.ci/windows/build.sh`. You must run this with Bash, e.g. Git Bash or MinGW TTY. To use this script, you must have windeployqt installed (usually bundled with Qt) and set the `WINDEPLOYQT` environment variable to its canonical Bash location, e.g. `WINDEPLOYQT="/c/Qt/6.9.1/msvc2022_64/bin/windeployqt6.exe" .ci/windows/build.sh`. + +Extra CMake flags should be placed in the arguments of the script. + +Additional environment variables can be used to control building: +- `BUILD_TYPE`: Sets the build type to use. Defaults to `Release` + +The following environment variables are boolean flags. Set to `true` to enable or `false` to disable: +- `DEVEL` (default FALSE): Disable Qt update checker +- `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine +- `USE_MULTIMEDIA` (default TRUE): Enable Qt Multimedia +- `BUNDLE_QT` (default FALSE): Use bundled Qt + * Note that using system Qt requires you to include the Qt CMake directory in `CMAKE_PREFIX_PATH`, e.g. `.ci/windows/build.sh -DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6` + +After building, a zip can be packaged via `.ci/windows/package.sh`. Note that you must have 7-zip installed and in your PATH. The resulting zip will be placed into `artifacts` in the source directory. diff --git a/docs/build/macOS.md b/docs/build/macOS.md new file mode 100644 index 0000000000..6cb62273cb --- /dev/null +++ b/docs/build/macOS.md @@ -0,0 +1,105 @@ +Please note this article is intended for development, and eden on macOS is not currently ready for regular use. + +This article was written for developers. eden support for macOS is not ready for casual use. + +## Method I: ninja +--- +If you are compiling on Intel Mac or are using a Rosetta Homebrew installation, you must replace all references of `/opt/homebrew` to `/usr/local`. + +Install dependencies from Homebrew: +```sh +brew install autoconf automake boost ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl2 speexdsp zlib zlib zstd cmake Catch2 molten-vk vulkan-loader +``` + +Clone the repo +```sh +git clone --recursive https://git.eden-emu.dev/eden-emu/eden + +cd eden +``` + +Build for release +```sh +mkdir build && cd build + +export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake" + +export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib + +cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=ON -DENABLE_LIBUSB=OFF -DCLANG_FORMAT=ON -DSDL2_DISABLE_INSTALL=ON -DSDL_ALTIVEC=ON + +ninja +``` + +You may also want to include support for Discord Rich Presence by adding `-DUSE_DISCORD_PRESENCE=ON` after `cmake ..` + +Build with debug symbols (vcpkg is not currently used due to broken boost-context library): +```sh +mkdir build && cd build +export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake" +cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF +ninja +``` + +Run the output: +``` +bin/eden.app/Contents/MacOS/eden +``` + +## Method II: Xcode + +--- +If you are compiling on Intel Mac or are using a Rosetta Homebrew installation, you must replace all references of `/opt/homebrew` to `/usr/local`. + +Install dependencies from Homebrew: +```sh +brew install autoconf automake boost ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl2 speexdsp zlib zlib zstd cmake Catch2 molten-vk vulkan-loader +``` + +Clone the repo +```sh +git clone --recursive https://git.eden-emu.dev/eden-emu/eden + +cd eden +``` + +Build for release +```sh +mkdir build && cd build + +export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake" + +export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib + +cmake .. -GXcode -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=ON -DENABLE_LIBUSB=OFF -DCLANG_FORMAT=ON -DSDL2_DISABLE_INSTALL=ON -DSDL_ALTIVEC=ON + +xcodebuild build -project eden.xcodeproj -scheme "eden" -configuration "RelWithDebInfo" +``` + +You may also want to include support for Discord Rich Presence by adding `-DUSE_DISCORD_PRESENCE=ON` after `cmake ..` + +Build with debug symbols (vcpkg is not currently used due to broken boost-context library): +```sh +mkdir build && cd build +export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake" +cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF +ninja +``` + +Run the output: +``` +bin/eden.app/Contents/MacOS/eden +``` + +--- + +To run with MoltenVK, install additional dependencies: +```sh +brew install molten-vk vulkan-loader +``` + +Run with Vulkan loader path: +```sh +export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib +bin/eden.app/Contents/MacOS/eden +``` \ No newline at end of file From fb459c75d93fa50e67c051cb6d724d5a362291f2 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 23 Jul 2025 09:54:00 +0200 Subject: [PATCH 38/41] [wifi] conditionally enable (#90) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/90 Co-authored-by: lizzie Co-committed-by: lizzie --- CMakeLists.txt | 1 + src/core/CMakeLists.txt | 3 ++- src/core/internal_network/wifi_scanner.cpp | 10 +++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 124a5ac80a..599388110f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ option(ENABLE_QT_UPDATE_CHECKER "Enable update checker for the Qt frontend" OFF) CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) +option(ENABLE_WIFI_SCAN "Enable WiFi scanning" OFF) if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" OFF) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d830cc381d..8e167c51d9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1150,8 +1150,9 @@ add_library(core STATIC tools/renderdoc.h ) -if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +if (ENABLE_WIFI_SCAN) # find_package(libiw REQUIRED) + target_compile_definitions(core PRIVATE -DENABLE_WIFI_SCAN) target_link_libraries(core PRIVATE iw) endif() diff --git a/src/core/internal_network/wifi_scanner.cpp b/src/core/internal_network/wifi_scanner.cpp index df9bc92910..f4b1738e69 100644 --- a/src/core/internal_network/wifi_scanner.cpp +++ b/src/core/internal_network/wifi_scanner.cpp @@ -19,7 +19,7 @@ using namespace std::chrono_literals; #endif namespace Network { - +#ifdef ENABLE_WIFI_SCAN #ifdef _WIN32 static u8 QualityToPercent(DWORD q) { return static_cast(q); @@ -173,16 +173,20 @@ static std::vector ScanWifiLinux(std::chrono::milliseconds de return out; } #endif /* linux */ +#endif std::vector ScanWifiNetworks(std::chrono::milliseconds deadline) { -#ifdef _WIN32 +#ifdef ENABLE_WIFI_SCAN +#if defined(_WIN32) return ScanWifiWin(deadline); #elif defined(__linux__) && !defined(ANDROID) return ScanWifiLinux(deadline); #else - std::this_thread::sleep_for(deadline); return {}; // unsupported host, pretend no results #endif +#else + return {}; // disabled, pretend no results +#endif } } // namespace Network From 23c77a0d4f884057abff59f4df4b0e9ceb508136 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 23 Jul 2025 09:54:26 +0200 Subject: [PATCH 39/41] [cmake] patches from debian upstream (#99) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/99 Co-authored-by: lizzie Co-committed-by: lizzie --- CMakeLists.txt | 4 ++-- externals/CMakeLists.txt | 2 +- src/core/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 599388110f..b28fbc7137 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,7 +347,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) # Enforce the search mode of non-required packages for better and shorter failure messages find_package(enet 1.3 MODULE) find_package(fmt 8 REQUIRED) -find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle) +find_package(LLVM MODULE COMPONENTS Demangle) find_package(lz4 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED) find_package(Opus 1.3 MODULE) @@ -415,7 +415,7 @@ if(ENABLE_OPENSSL) find_package(OpenSSL 1.1.1 REQUIRED) endif() -if (UNIX AND NOT APPLE) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") find_package(gamemode 1.7 MODULE) endif() diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 7dacba23b6..3668193071 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -211,7 +211,7 @@ if (ANDROID) endif() endif() -if (UNIX AND NOT APPLE AND NOT TARGET gamemode::headers) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT TARGET gamemode::headers) add_library(gamemode INTERFACE) target_include_directories(gamemode INTERFACE gamemode) add_library(gamemode::headers ALIAS gamemode) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8e167c51d9..974c05dec9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1212,7 +1212,7 @@ if (HAS_NCE) arm/nce/patcher.h arm/nce/visitor_base.h ) - target_link_libraries(core PRIVATE merry::mcl merry::oaknut) + target_link_libraries(core PRIVATE merry::oaknut) endif() if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) From e1763a726ed43df366a5ee65803bdacb0e5bb28e Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 23 Jul 2025 10:00:29 +0200 Subject: [PATCH 40/41] [build, cmake] port to solaris (#96) Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/96 Co-authored-by: lizzie Co-committed-by: lizzie --- .gitmodules | 4 +- CMakeLists.txt | 4 +- docs/Solaris.md | 94 +++++++++++++++++++ .../backend/exception_handler_posix.cpp | 13 ++- .../tzdb_to_nx/externals/tz/CMakeLists.txt | 4 +- .../nx_tzdb/tzdb_to_nx/src/tzdb2nx/main.cpp | 9 +- .../nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.cpp | 37 ++++---- .../nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.h | 39 ++++---- externals/renderdoc/renderdoc_app.h | 11 +-- src/audio_core/sink/sdl2_sink.cpp | 4 + src/common/minicoro.h | 5 +- src/core/hle/service/caps/caps_manager.cpp | 7 +- src/core/hle/service/psc/time/common.h | 9 +- .../internal_network/network_interface.cpp | 41 ++++---- src/network/CMakeLists.txt | 5 + src/yuzu_cmd/CMakeLists.txt | 5 + 16 files changed, 215 insertions(+), 76 deletions(-) create mode 100644 docs/Solaris.md diff --git a/.gitmodules b/.gitmodules index 1a6e6574a3..371b62a605 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,7 +12,7 @@ url = https://github.com/KhronosGroup/Vulkan-Headers.git [submodule "xbyak"] path = externals/xbyak - url = https://github.com/herumi/xbyak.git + url = https://github.com/Lizzie841/xbyak.git [submodule "opus"] path = externals/opus url = https://github.com/xiph/opus.git @@ -51,7 +51,7 @@ url = https://github.com/Lizzie841/unordered_dense.git [submodule "externals/dynarmic/externals/xbyak"] path = externals/dynarmic/externals/xbyak - url = https://github.com/herumi/xbyak.git + url = https://github.com/Lizzie841/xbyak.git [submodule "externals/dynarmic/externals/zycore-c"] path = externals/dynarmic/externals/zycore-c url = https://github.com/zyantific/zycore-c.git diff --git a/CMakeLists.txt b/CMakeLists.txt index b28fbc7137..bb571235a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,7 @@ endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" OFF) else() - option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON) + option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON) endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") @@ -123,7 +123,7 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available CMAKE_DEPENDENT_OPTION(USE_SYSTEM_MOLTENVK "Use the system MoltenVK lib (instead of the bundled one)" OFF "APPLE" OFF) set(DEFAULT_ENABLE_OPENSSL ON) -if (ANDROID OR WIN32 OR APPLE) +if (ANDROID OR WIN32 OR APPLE OR ${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") set(DEFAULT_ENABLE_OPENSSL OFF) endif() option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL}) diff --git a/docs/Solaris.md b/docs/Solaris.md new file mode 100644 index 0000000000..c7daa2279b --- /dev/null +++ b/docs/Solaris.md @@ -0,0 +1,94 @@ +# Building for Solaris + +## Dependencies. +Always consult [the OpenIndiana package list](https://pkg.openindiana.org/hipster/en/index.shtml) to cross-verify availability. + +Run the usual update + install of essential toolings: `sudo pkg update && sudo pkg install git cmake`. + +- **gcc**: `sudo pkg install developer/gcc-14`. +- **clang**: Version 20 is broken, use `sudo pkg install developer/clang-19`. + +Then install the libraies: `sudo pkg install qt6 boost glslang libzip library/lz4 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm`. + +fmtlib is not available on repositories and has to be manually built: +```sh +git clone --recurisve --depth=1 https://github.com/fmtlib/fmt.git +cd fmt +cmake -DCMAKE_BUILD_TYPE=Release -B build +cmake --build build +sudo cmake --install build +``` + +pkg lz4 doesn't provide a proper CMakeFile to find the library, has to also be manually built: +```sh +git clone --depth=1 https://github.com/lz4/lz4.git +cd lz4 +gmake +sudo gmake install +``` + +Same goes for zstd: +```sh +git clone --depth=1 https://github.com/facebook/zstd.git +cd zstd +cmake -DCMAKE_BUILD_TYPE=Release -B build0 -S build/cmake +cmake --build build0 +cd build0 +sudo gmake install +``` + +pkg SDL2 is also not nice to work with on CMake, save yourself some pain and compile it yourself: +```sh +git clone --depth=1 --branch=release-2.32.8 https://github.com/libsdl-org/SDL +cmake -DCMAKE_BUILD_TYPE=Release -B build +cmake --build build +sudo cmake --install build +``` + +Audio is broken in OpenIndiana [see this issue](https://github.com/libsdl-org/SDL/issues/13405), go into `SDL/CMakeLists.txt` and comment out lines 1468: +```diff ++# set(SDL_AUDIO_DRIVER_SUNAUDIO 1) ++# file(GLOB SUN_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/sun/*.c) ++# list(APPEND SOURCE_FILES ${SUN_AUDIO_SOURCES}) ++# set(HAVE_SDL_AUDIO TRUE) +``` +For Solaris this issue does not exist - however PulseAudio crashes on Solaris - so use a different backend. + +--- + +### Build preparations: +Run the following command to clone eden with git: +```sh +git clone --recursive https://git.eden-emu.dev/eden-emu/eden +``` +You usually want to add the `--recursive` parameter as it also takes care of the external dependencies for you. + +Now change into the eden directory and create a build directory there: +```sh +cd eden +mkdir build +``` + +Change into that build directory: `cd build` + +Now choose one option either 1 or 2, but not both as one option overwrites the other. + +### Building + +```sh +# Needed for some dependencies that call cc directly (tz) +echo '#!/bin/sh' >cc +echo 'gcc $@' >>cc +chmod +x cc +export PATH="$PATH:$PWD" +``` + +- **Configure**: `cmake -B build -DYUZU_TESTS=OFF -DYUZU_USE_BUNDLED_SDL2=OFF -DYUZU_USE_EXTERNAL_SDL2=OFF -DYUZU_USE_LLVM_DEMANGLE=OFF -DYUZU_USE_QT_MULTIMEDIA=OFF -DYUZU_USE_QT_WEB_ENGINE=OFF -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_USE_BUNDLED_QT=OFF -DENABLE_QT=OFF -DSDL_AUDIO=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_QT_UPDATE_CHECKER=OFF`. +- **Build**: `cmake --build build`. +- **Installing**: `sudo cmake --install build`. + +### Notes + +- Modify the generated ffmpeg.make (in build dir) if using multiple threads (base system `make` doesn't use `-j4`, so change for `gmake`). +- If using OpenIndiana, due to a bug in SDL2 cmake configuration; Audio driver defaults to SunOS ``, which does not exist on OpenIndiana. +- Enabling OpenSSL requires compiling OpenSSL manually instead of using the provided one from repositores. diff --git a/externals/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp b/externals/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp index 2d6d6ea6d8..8a523267e5 100644 --- a/externals/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp +++ b/externals/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2019 MerryMage * SPDX-License-Identifier: 0BSD @@ -13,6 +16,9 @@ # ifndef __OpenBSD__ # include # endif +# ifdef __sun__ +# include +# endif #endif #include @@ -145,9 +151,9 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { #ifndef MCL_ARCHITECTURE_RISCV ucontext_t* ucontext = reinterpret_cast(raw_context); -# ifndef __OpenBSD__ +#ifndef __OpenBSD__ auto& mctx = ucontext->uc_mcontext; -# endif +#endif #endif #if defined(MCL_ARCHITECTURE_X86_64) @@ -167,6 +173,9 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { # elif defined(__OpenBSD__) # define CTX_RIP (ucontext->sc_rip) # define CTX_RSP (ucontext->sc_rsp) +# elif defined(__sun__) +# define CTX_RIP (mctx.gregs[REG_RIP]) +# define CTX_RSP (mctx.gregs[REG_RSP]) # else # error "Unknown platform" # endif diff --git a/externals/nx_tzdb/tzdb_to_nx/externals/tz/CMakeLists.txt b/externals/nx_tzdb/tzdb_to_nx/externals/tz/CMakeLists.txt index 948fe69a23..2312b34dd6 100644 --- a/externals/nx_tzdb/tzdb_to_nx/externals/tz/CMakeLists.txt +++ b/externals/nx_tzdb/tzdb_to_nx/externals/tz/CMakeLists.txt @@ -24,8 +24,8 @@ if (NOT EXISTS "${TZ_DIR}" OR NOT EXISTS "${TZIF_LIST_FILE}") # separate directory before building. execute_process( COMMAND - ${GIT_PROGRAM} clone --depth 1 "file://${TZ_SOURCE_DIR}" "${TZ_TMP_SOURCE_DIR}" - COMMAND_ERROR_IS_FATAL ANY + ${GIT_PROGRAM} clone --depth=1 "file://${TZ_SOURCE_DIR}" "${TZ_TMP_SOURCE_DIR}" + # No need to be fatal, on SunOS this works fine - COMMAND_ERROR_IS_FATAL ANY ) if (APPLE) diff --git a/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/main.cpp b/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/main.cpp index c1825970ac..12c12ac268 100644 --- a/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/main.cpp +++ b/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/main.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + #include "tzif.h" #include #include @@ -7,7 +10,7 @@ #include #include #include -#include +#include #include constexpr std::size_t ten_megabytes{(1 << 20) * 10}; @@ -92,7 +95,7 @@ int main(int argc, char *argv[]) { } } - u_int8_t *buf = new u_int8_t[filesize]; + std::uint8_t *buf = new std::uint8_t[filesize]; filesize = read(f, buf, filesize); if (filesize == static_cast(-1)) { @@ -124,7 +127,7 @@ int main(int argc, char *argv[]) { delete[] buf; - std::vector output_buffer; + std::vector output_buffer; tzif_data->ReformatNintendo(output_buffer); filename = "(stdout)"; diff --git a/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.cpp b/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.cpp index 4d2b842741..476afcbc8e 100644 --- a/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.cpp +++ b/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.cpp @@ -1,14 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + #include "tzif.h" #include #include #include -#include +#include namespace Tzif { -static std::size_t SkipToVersion2(const u_int8_t *data, std::size_t size) { +static std::size_t SkipToVersion2(const std::uint8_t *data, std::size_t size) { char magic[5]; - const u_int8_t *p{data}; + const std::uint8_t *p{data}; std::memcpy(magic, data, 4); magic[4] = '\0'; @@ -28,15 +31,15 @@ static std::size_t SkipToVersion2(const u_int8_t *data, std::size_t size) { } template constexpr static void SwapEndianess(Type *value) { - u_int8_t *data = reinterpret_cast(value); + std::uint8_t *data = reinterpret_cast(value); union { - u_int8_t data[sizeof(Type)]; + std::uint8_t data[sizeof(Type)]; Type value; } temp; - for (u_int32_t i = 0; i < sizeof(Type); i++) { - u_int32_t alt_index = sizeof(Type) - i - 1; + for (std::uint32_t i = 0; i < sizeof(Type); i++) { + std::uint32_t alt_index = sizeof(Type) - i - 1; temp.data[alt_index] = data[i]; } @@ -52,13 +55,13 @@ static void FlipHeader(Header &header) { SwapEndianess(&header.charcnt); } -std::unique_ptr ReadData(const u_int8_t *data, std::size_t size) { +std::unique_ptr ReadData(const std::uint8_t *data, std::size_t size) { const std::size_t v2_offset = SkipToVersion2(data, size); if (v2_offset == static_cast(-1)) { return nullptr; } - const u_int8_t *p = data + v2_offset; + const std::uint8_t *p = data + v2_offset; Header header; std::memcpy(&header, p, sizeof(header)); @@ -67,10 +70,10 @@ std::unique_ptr ReadData(const u_int8_t *data, std::size_t size) { FlipHeader(header); const std::size_t data_block_length = - header.timecnt * sizeof(int64_t) + header.timecnt * sizeof(u_int8_t) + + header.timecnt * sizeof(int64_t) + header.timecnt * sizeof(std::uint8_t) + header.typecnt * sizeof(TimeTypeRecord) + - header.charcnt * sizeof(int8_t) + header.isstdcnt * sizeof(u_int8_t) + - header.isutcnt * sizeof(u_int8_t); + header.charcnt * sizeof(int8_t) + header.isstdcnt * sizeof(std::uint8_t) + + header.isutcnt * sizeof(std::uint8_t); if (v2_offset + data_block_length + sizeof(Header) > size) { return nullptr; @@ -81,7 +84,7 @@ std::unique_ptr ReadData(const u_int8_t *data, std::size_t size) { const auto copy = [](std::unique_ptr &array, int length, - const u_int8_t *const &ptr) -> const u_int8_t * { + const std::uint8_t *const &ptr) -> const std::uint8_t * { const std::size_t region_length = length * sizeof(Type); array = std::make_unique(length); std::memcpy(array.get(), ptr, region_length); @@ -110,16 +113,16 @@ std::unique_ptr ReadData(const u_int8_t *data, std::size_t size) { return impl; } -static void PushToBuffer(std::vector &buffer, const void *data, +static void PushToBuffer(std::vector &buffer, const void *data, std::size_t size) { - const u_int8_t *p{reinterpret_cast(data)}; + const std::uint8_t *p{reinterpret_cast(data)}; for (std::size_t i = 0; i < size; i++) { buffer.push_back(*p); p++; } } -void DataImpl::ReformatNintendo(std::vector &buffer) const { +void DataImpl::ReformatNintendo(std::vector &buffer) const { buffer.clear(); Header header_copy{header}; @@ -131,7 +134,7 @@ void DataImpl::ReformatNintendo(std::vector &buffer) const { PushToBuffer(buffer, transition_times.get(), header.timecnt * sizeof(int64_t)); PushToBuffer(buffer, transition_types.get(), - header.timecnt * sizeof(u_int8_t)); + header.timecnt * sizeof(std::uint8_t)); PushToBuffer(buffer, local_time_type_records.get(), header.typecnt * sizeof(TimeTypeRecord)); PushToBuffer(buffer, time_zone_designations.get(), diff --git a/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.h b/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.h index 62ff3afe15..a6eb32a896 100644 --- a/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.h +++ b/externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.h @@ -1,22 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include #include -#include +#include #include namespace Tzif { typedef struct { char magic[4]; - u_int8_t version; - u_int8_t reserved[15]; - u_int32_t isutcnt; - u_int32_t isstdcnt; - u_int32_t leapcnt; - u_int32_t timecnt; - u_int32_t typecnt; - u_int32_t charcnt; + std::uint8_t version; + std::uint8_t reserved[15]; + std::uint32_t isutcnt; + std::uint32_t isstdcnt; + std::uint32_t leapcnt; + std::uint32_t timecnt; + std::uint32_t typecnt; + std::uint32_t charcnt; } Header; static_assert(sizeof(Header) == 0x2c); @@ -34,9 +37,9 @@ public: #pragma pack(push, 1) typedef struct { - u_int32_t utoff; - u_int8_t dst; - u_int8_t idx; + std::uint32_t utoff; + std::uint8_t dst; + std::uint8_t idx; } TimeTypeRecord; #pragma pack(pop) static_assert(sizeof(TimeTypeRecord) == 0x6); @@ -46,7 +49,7 @@ public: explicit Data() = default; virtual ~Data() = default; - virtual void ReformatNintendo(std::vector &buffer) const = 0; + virtual void ReformatNintendo(std::vector &buffer) const = 0; }; class DataImpl : public Data { @@ -54,19 +57,19 @@ public: explicit DataImpl() = default; ~DataImpl() override = default; - void ReformatNintendo(std::vector &buffer) const override; + void ReformatNintendo(std::vector &buffer) const override; Header header; Footer footer; std::unique_ptr transition_times; - std::unique_ptr transition_types; + std::unique_ptr transition_types; std::unique_ptr local_time_type_records; std::unique_ptr time_zone_designations; - std::unique_ptr standard_indicators; - std::unique_ptr ut_indicators; + std::unique_ptr standard_indicators; + std::unique_ptr ut_indicators; }; -std::unique_ptr ReadData(const u_int8_t *data, std::size_t size); +std::unique_ptr ReadData(const std::uint8_t *data, std::size_t size); } // namespace Tzif diff --git a/externals/renderdoc/renderdoc_app.h b/externals/renderdoc/renderdoc_app.h index 84ff62b5db..f4eb12526e 100644 --- a/externals/renderdoc/renderdoc_app.h +++ b/externals/renderdoc/renderdoc_app.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Baldur Karlsson // SPDX-License-Identifier: MIT @@ -38,14 +41,8 @@ #if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) #define RENDERDOC_CC __cdecl -#elif defined(__linux__) -#define RENDERDOC_CC -#elif defined(__APPLE__) -#define RENDERDOC_CC -#elif defined(__FreeBSD__) -#define RENDERDOC_CC #else -#error "Unknown platform" +#define RENDERDOC_CC #endif #ifdef __cplusplus diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index 7dd155ff0c..ffdd77042e 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -1,8 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include + #include #include "audio_core/common/common.h" diff --git a/src/common/minicoro.h b/src/common/minicoro.h index 9f1dbcafb3..f4113dcbe5 100644 --- a/src/common/minicoro.h +++ b/src/common/minicoro.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* Minimal asymmetric stackful cross-platform coroutine library in pure C. minicoro - v0.2.0 - 15/Nov/2023 @@ -467,7 +470,7 @@ extern "C" { #ifdef MCO_NO_MULTITHREAD #define MCO_THREAD_LOCAL #else -#ifdef thread_local +#if defined(thread_local) || __STDC_VERSION__ >= 202311L || defined(__sun__) #define MCO_THREAD_LOCAL thread_local #elif __STDC_VERSION__ >= 201112 && !defined(__STDC_NO_THREADS__) #define MCO_THREAD_LOCAL _Thread_local diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp index e636d90bc4..1825d345a6 100644 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -440,8 +443,8 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span struct fmt::formatter : fmt::formatter { template auto format(const Service::PSC::Time::CalendarTime& calendar, FormatContext& ctx) const { - return fmt::format_to(ctx.out(), "[{:02}/{:02}/{:04} {:02}:{:02}:{:02}]", calendar.day, - calendar.month, calendar.year, calendar.hour, calendar.minute, - calendar.second); + return fmt::format_to(ctx.out(), "[{:02}/{:02}/{:04} {:02}:{:02}:{:02}]", u8(calendar.day), + u8(calendar.month), u16(calendar.year), u8(calendar.hour), u8(calendar.minute), + u8(calendar.second)); } }; diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index 42def7af92..f62381b9e3 100644 --- a/src/core/internal_network/network_interface.cpp +++ b/src/core/internal_network/network_interface.cpp @@ -27,7 +27,7 @@ namespace Network { #ifdef _WIN32 -std::vector GetAvailableNetworkInterfaces() { +std::vector GetAvailableNetworkInterfaces() { ULONG buf_size = 0; if (GetAdaptersAddresses( @@ -47,7 +47,7 @@ std::vector GetAvailableNetworkInterfaces() { return {}; } - std::vector result; + std::vector result; for (auto* a = addrs; a; a = a->Next) { @@ -80,7 +80,7 @@ std::vector GetAvailableNetworkInterfaces() { break; } - result.emplace_back(NetworkInterface{ + result.emplace_back(Network::NetworkInterface{ .name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}), .ip_address = ip, .subnet_mask = mask, @@ -94,7 +94,7 @@ std::vector GetAvailableNetworkInterfaces() { #else -std::vector GetAvailableNetworkInterfaces() { +std::vector GetAvailableNetworkInterfaces() { struct ifaddrs* ifaddr = nullptr; if (getifaddrs(&ifaddr) != 0) { @@ -103,7 +103,7 @@ std::vector GetAvailableNetworkInterfaces() { return {}; } - std::vector result; + std::vector result; for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { @@ -121,7 +121,7 @@ std::vector GetAvailableNetworkInterfaces() { #ifdef ANDROID // On Android, we can't reliably get gateway info from /proc/net/route // Just use 0 as the gateway address - result.emplace_back(NetworkInterface{ + result.emplace_back(Network::NetworkInterface{ .name{ifa->ifa_name}, .ip_address{Common::BitCast(*ifa->ifa_addr).sin_addr}, .subnet_mask{Common::BitCast(*ifa->ifa_netmask).sin_addr}, @@ -134,11 +134,15 @@ std::vector GetAvailableNetworkInterfaces() { if (!file.is_open()) { LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); - result.emplace_back(NetworkInterface{ - .name{ifa->ifa_name}, - .ip_address{Common::BitCast(*ifa->ifa_addr).sin_addr}, - .subnet_mask{Common::BitCast(*ifa->ifa_netmask).sin_addr}, - .gateway{in_addr{.s_addr = gateway}}}); + // Solaris defines s_addr as a macro, can't use special C++ shenanigans here + in_addr gateway_0; + gateway_0.s_addr = gateway; + result.emplace_back(Network::NetworkInterface{ + .name = ifa->ifa_name, + .ip_address = Common::BitCast(*ifa->ifa_addr).sin_addr, + .subnet_mask = Common::BitCast(*ifa->ifa_netmask).sin_addr, + .gateway = gateway_0 + }); continue; } @@ -183,11 +187,14 @@ std::vector GetAvailableNetworkInterfaces() { gateway = 0; } - result.emplace_back(NetworkInterface{ - .name{ifa->ifa_name}, - .ip_address{Common::BitCast(*ifa->ifa_addr).sin_addr}, - .subnet_mask{Common::BitCast(*ifa->ifa_netmask).sin_addr}, - .gateway{in_addr{.s_addr = gateway}}}); + in_addr gateway_0; + gateway_0.s_addr = gateway; + result.emplace_back(Network::NetworkInterface{ + .name = ifa->ifa_name, + .ip_address = Common::BitCast(*ifa->ifa_addr).sin_addr, + .subnet_mask = Common::BitCast(*ifa->ifa_netmask).sin_addr, + .gateway = gateway_0 + }); #endif // ANDROID } @@ -197,7 +204,7 @@ std::vector GetAvailableNetworkInterfaces() { #endif // _WIN32 -std::optional GetSelectedNetworkInterface() { +std::optional GetSelectedNetworkInterface() { const auto& selected_network_interface = Settings::values.network_interface.GetValue(); const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 8e306219fb..1657feaa5f 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -25,6 +25,11 @@ if (ENABLE_WEB_SERVICE) target_link_libraries(network PRIVATE web_service) endif() +# Solaris uses /lib/amd64/libsocket.so and /lib/amd64/libnsl.so +if (${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") + target_link_libraries(network PRIVATE socket nsl) +endif() + if (YUZU_USE_PRECOMPILED_HEADERS) target_precompile_headers(network PRIVATE precompiled_headers.h) endif() diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index ebd8fd7387..9d1e08c04c 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -40,6 +40,11 @@ target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR}) target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2 Vulkan::Headers) +# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so +if (${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") + target_link_libraries(yuzu-cmd PRIVATE X11) +endif() + if(UNIX AND NOT APPLE) install(TARGETS yuzu-cmd) endif() From 016ebf3cd880f6cfac5ec538da5c75879e129230 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 23 Jul 2025 17:49:51 +0200 Subject: [PATCH 41/41] [cmake, build] fix android and aarch64 (#103) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/103 Co-authored-by: lizzie Co-committed-by: lizzie --- CMakeLists.txt | 2 +- externals/CMakeLists.txt | 2 +- src/core/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb571235a1..184993b96a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -415,7 +415,7 @@ if(ENABLE_OPENSSL) find_package(OpenSSL 1.1.1 REQUIRED) endif() -if (CMAKE_SYSTEM_NAME STREQUAL "Linux") +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT ANDROID) find_package(gamemode 1.7 MODULE) endif() diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 3668193071..c08c9b7f2d 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -211,7 +211,7 @@ if (ANDROID) endif() endif() -if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT TARGET gamemode::headers) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT ANDROID AND NOT TARGET gamemode::headers) add_library(gamemode INTERFACE) target_include_directories(gamemode INTERFACE gamemode) add_library(gamemode::headers ALIAS gamemode) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 974c05dec9..8e167c51d9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1212,7 +1212,7 @@ if (HAS_NCE) arm/nce/patcher.h arm/nce/visitor_base.h ) - target_link_libraries(core PRIVATE merry::oaknut) + target_link_libraries(core PRIVATE merry::mcl merry::oaknut) endif() if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)