From d3997bad9b84579938d8cdb44b1d17cfab7bbcce Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 4 Oct 2023 13:11:05 -0400 Subject: qt: implement automatic crash dump support --- src/common/fs/fs_paths.h | 1 + src/common/fs/path_util.cpp | 1 + src/common/fs/path_util.h | 1 + src/common/settings.h | 1 - src/yuzu/CMakeLists.txt | 10 +- src/yuzu/breakpad.cpp | 77 +++++++++++ src/yuzu/breakpad.h | 10 ++ src/yuzu/configuration/configure_debug.cpp | 18 --- src/yuzu/configuration/configure_debug.ui | 7 - src/yuzu/main.cpp | 19 +-- src/yuzu/mini_dump.cpp | 202 ----------------------------- src/yuzu/mini_dump.h | 19 --- 12 files changed, 101 insertions(+), 265 deletions(-) create mode 100644 src/yuzu/breakpad.cpp create mode 100644 src/yuzu/breakpad.h delete mode 100644 src/yuzu/mini_dump.cpp delete mode 100644 src/yuzu/mini_dump.h (limited to 'src') diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h index 61bac9eba..1a3f6ab45 100644 --- a/src/common/fs/fs_paths.h +++ b/src/common/fs/fs_paths.h @@ -13,6 +13,7 @@ #define AMIIBO_DIR "amiibo" #define CACHE_DIR "cache" #define CONFIG_DIR "config" +#define CRASH_DUMPS_DIR "crash_dumps" #define DUMP_DIR "dump" #define KEYS_DIR "keys" #define LOAD_DIR "load" diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index dce219fcf..fbac4d80c 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -119,6 +119,7 @@ public: GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR); GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache); GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config); + GenerateYuzuPath(YuzuPath::CrashDumpsDir, yuzu_path / CRASH_DUMPS_DIR); GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR); GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR); diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index ba28964d0..036e475aa 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h @@ -15,6 +15,7 @@ enum class YuzuPath { AmiiboDir, // Where Amiibo backups are stored. CacheDir, // Where cached filesystem data is stored. ConfigDir, // Where config files are stored. + CrashDumpsDir, // Where crash dumps are stored. DumpDir, // Where dumped data is stored. KeysDir, // Where key files are stored. LoadDir, // Where cheat/mod files are stored. diff --git a/src/common/settings.h b/src/common/settings.h index 98ab0ec2e..6a3fe47c9 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -500,7 +500,6 @@ struct Values { linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false}; Setting enable_all_controllers{linkage, false, "enable_all_controllers", Category::Debugging}; - Setting create_crash_dumps{linkage, false, "create_crash_dumps", Category::Debugging}; Setting perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging}; // Miscellaneous diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 8f86a1553..d23c1e9d0 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -225,14 +225,14 @@ add_executable(yuzu yuzu.rc ) -if (WIN32 AND YUZU_CRASH_DUMPS) +if (YUZU_CRASH_DUMPS) target_sources(yuzu PRIVATE - mini_dump.cpp - mini_dump.h + breakpad.cpp + breakpad.h ) - target_link_libraries(yuzu PRIVATE ${DBGHELP_LIBRARY}) - target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP) + target_link_libraries(yuzu PRIVATE libbreakpad_client) + target_compile_definitions(yuzu PRIVATE YUZU_CRASH_DUMPS) endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") diff --git a/src/yuzu/breakpad.cpp b/src/yuzu/breakpad.cpp new file mode 100644 index 000000000..0f6a71ab0 --- /dev/null +++ b/src/yuzu/breakpad.cpp @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#if defined(_WIN32) +#include +#elif defined(__linux__) +#include +#else +#error Minidump creation not supported on this platform +#endif + +#include "common/fs/fs_paths.h" +#include "common/fs/path_util.h" +#include "yuzu/breakpad.h" + +namespace Breakpad { + +static void PruneDumpDirectory(const std::filesystem::path& dump_path) { + // Code in this function should be exception-safe. + struct Entry { + std::filesystem::path path; + std::filesystem::file_time_type last_write_time; + }; + std::vector existing_dumps; + + // Get existing entries. + std::error_code ec; + std::filesystem::directory_iterator dir(dump_path, ec); + for (auto& entry : dir) { + if (entry.is_regular_file()) { + existing_dumps.push_back(Entry{ + .path = entry.path(), + .last_write_time = entry.last_write_time(ec), + }); + } + } + + // Sort descending by creation date. + std::ranges::stable_sort(existing_dumps, [](const auto& a, const auto& b) { + return a.last_write_time > b.last_write_time; + }); + + // Delete older dumps. + for (size_t i = 5; i < existing_dumps.size(); i++) { + std::filesystem::remove(existing_dumps[i].path, ec); + } +} + +#if defined(__linux__) +[[noreturn]] bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, + bool succeeded) { + // Prevent time- and space-consuming core dumps from being generated, as we have + // already generated a minidump and a core file will not be useful anyway. + _exit(1); +} +#endif + +void InstallCrashHandler() { + // Write crash dumps to profile directory. + const auto dump_path = GetYuzuPath(Common::FS::YuzuPath::CrashDumpsDir); + PruneDumpDirectory(dump_path); + +#if defined(_WIN32) + // TODO: If we switch to MinGW builds for Windows, this needs to be wrapped in a C API. + static google_breakpad::ExceptionHandler eh{dump_path, nullptr, nullptr, nullptr, + google_breakpad::ExceptionHandler::HANDLER_ALL}; +#elif defined(__linux__) + static google_breakpad::MinidumpDescriptor descriptor{dump_path}; + static google_breakpad::ExceptionHandler eh{descriptor, nullptr, DumpCallback, + nullptr, true, -1}; +#endif +} + +} // namespace Breakpad diff --git a/src/yuzu/breakpad.h b/src/yuzu/breakpad.h new file mode 100644 index 000000000..0f911aa9c --- /dev/null +++ b/src/yuzu/breakpad.h @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Breakpad { + +void InstallCrashHandler(); + +} diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index b22fda746..ef421c754 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -27,16 +27,6 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent) connect(ui->toggle_gdbstub, &QCheckBox::toggled, [&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); }); - - connect(ui->create_crash_dumps, &QCheckBox::stateChanged, [&](int) { - if (crash_dump_warning_shown) { - return; - } - QMessageBox::warning(this, tr("Restart Required"), - tr("yuzu is required to restart in order to apply this setting."), - QMessageBox::Ok, QMessageBox::Ok); - crash_dump_warning_shown = true; - }); } ConfigureDebug::~ConfigureDebug() = default; @@ -89,13 +79,6 @@ void ConfigureDebug::SetConfiguration() { ui->disable_web_applet->setEnabled(false); ui->disable_web_applet->setText(tr("Web applet not compiled")); #endif - -#ifdef YUZU_DBGHELP - ui->create_crash_dumps->setChecked(Settings::values.create_crash_dumps.GetValue()); -#else - ui->create_crash_dumps->setEnabled(false); - ui->create_crash_dumps->setText(tr("MiniDump creation not compiled")); -#endif } void ConfigureDebug::ApplyConfiguration() { @@ -107,7 +90,6 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked(); Settings::values.reporting_services = ui->reporting_services->isChecked(); Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked(); - Settings::values.create_crash_dumps = ui->create_crash_dumps->isChecked(); Settings::values.quest_flag = ui->quest_flag->isChecked(); Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 66b8b7459..76fe98924 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -471,13 +471,6 @@ - - - - Create Minidump After Crash - - - diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 16fa92e2c..eb69da4ba 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -155,8 +155,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/util/clickable_label.h" #include "yuzu/vk_device_info.h" -#ifdef YUZU_DBGHELP -#include "yuzu/mini_dump.h" +#ifdef YUZU_CRASH_DUMPS +#include "yuzu/breakpad.h" #endif using namespace Common::Literals; @@ -5054,22 +5054,15 @@ int main(int argc, char* argv[]) { return 0; } -#ifdef YUZU_DBGHELP - PROCESS_INFORMATION pi; - if (!is_child && Settings::values.create_crash_dumps.GetValue() && - MiniDump::SpawnDebuggee(argv[0], pi)) { - // Delete the config object so that it doesn't save when the program exits - config.reset(nullptr); - MiniDump::DebugDebuggee(pi); - return 0; - } -#endif - if (StartupChecks(argv[0], &has_broken_vulkan, Settings::values.perform_vulkan_check.GetValue())) { return 0; } +#ifdef YUZU_CRASH_DUMPS + Breakpad::InstallCrashHandler(); +#endif + Common::DetachedTasks detached_tasks; MicroProfileOnThreadCreate("Frontend"); SCOPE_EXIT({ MicroProfileShutdown(); }); diff --git a/src/yuzu/mini_dump.cpp b/src/yuzu/mini_dump.cpp deleted file mode 100644 index a34dc6a9c..000000000 --- a/src/yuzu/mini_dump.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// SPDX-FileCopyrightText: 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include -#include "yuzu/mini_dump.h" -#include "yuzu/startup_checks.h" - -// dbghelp.h must be included after windows.h -#include - -namespace MiniDump { - -void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, - EXCEPTION_POINTERS* pep) { - char file_name[255]; - const std::time_t the_time = std::time(nullptr); - std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time)); - - // Open the file - HANDLE file_handle = CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - - if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) { - fmt::print(stderr, "CreateFileA failed. Error: {}", GetLastError()); - return; - } - - // Create the minidump - const MINIDUMP_TYPE dump_type = MiniDumpNormal; - - const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle, - dump_type, (pep != 0) ? info : 0, 0, 0); - - if (write_dump_status) { - fmt::print(stderr, "MiniDump created: {}", file_name); - } else { - fmt::print(stderr, "MiniDumpWriteDump failed. Error: {}", GetLastError()); - } - - // Close the file - CloseHandle(file_handle); -} - -void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { - EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; - - HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId); - if (thread_handle == nullptr) { - fmt::print(stderr, "OpenThread failed ({})", GetLastError()); - return; - } - - // Get child process context - CONTEXT context = {}; - context.ContextFlags = CONTEXT_ALL; - if (!GetThreadContext(thread_handle, &context)) { - fmt::print(stderr, "GetThreadContext failed ({})", GetLastError()); - return; - } - - // Create exception pointers for minidump - EXCEPTION_POINTERS ep; - ep.ExceptionRecord = &record; - ep.ContextRecord = &context; - - MINIDUMP_EXCEPTION_INFORMATION info; - info.ThreadId = deb_ev.dwThreadId; - info.ExceptionPointers = &ep; - info.ClientPointers = false; - - CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep); - - if (CloseHandle(thread_handle) == 0) { - fmt::print(stderr, "error: CloseHandle(thread_handle) failed ({})", GetLastError()); - } -} - -bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { - std::memset(&pi, 0, sizeof(pi)); - - // Don't debug if we are already being debugged - if (IsDebuggerPresent()) { - return false; - } - - if (!SpawnChild(arg0, &pi, 0)) { - fmt::print(stderr, "warning: continuing without crash dumps"); - return false; - } - - const bool can_debug = DebugActiveProcess(pi.dwProcessId); - if (!can_debug) { - fmt::print(stderr, - "warning: DebugActiveProcess failed ({}), continuing without crash dumps", - GetLastError()); - return false; - } - - return true; -} - -static const char* ExceptionName(DWORD exception) { - switch (exception) { - case EXCEPTION_ACCESS_VIOLATION: - return "EXCEPTION_ACCESS_VIOLATION"; - case EXCEPTION_DATATYPE_MISALIGNMENT: - return "EXCEPTION_DATATYPE_MISALIGNMENT"; - case EXCEPTION_BREAKPOINT: - return "EXCEPTION_BREAKPOINT"; - case EXCEPTION_SINGLE_STEP: - return "EXCEPTION_SINGLE_STEP"; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; - case EXCEPTION_FLT_DENORMAL_OPERAND: - return "EXCEPTION_FLT_DENORMAL_OPERAND"; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; - case EXCEPTION_FLT_INEXACT_RESULT: - return "EXCEPTION_FLT_INEXACT_RESULT"; - case EXCEPTION_FLT_INVALID_OPERATION: - return "EXCEPTION_FLT_INVALID_OPERATION"; - case EXCEPTION_FLT_OVERFLOW: - return "EXCEPTION_FLT_OVERFLOW"; - case EXCEPTION_FLT_STACK_CHECK: - return "EXCEPTION_FLT_STACK_CHECK"; - case EXCEPTION_FLT_UNDERFLOW: - return "EXCEPTION_FLT_UNDERFLOW"; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - return "EXCEPTION_INT_DIVIDE_BY_ZERO"; - case EXCEPTION_INT_OVERFLOW: - return "EXCEPTION_INT_OVERFLOW"; - case EXCEPTION_PRIV_INSTRUCTION: - return "EXCEPTION_PRIV_INSTRUCTION"; - case EXCEPTION_IN_PAGE_ERROR: - return "EXCEPTION_IN_PAGE_ERROR"; - case EXCEPTION_ILLEGAL_INSTRUCTION: - return "EXCEPTION_ILLEGAL_INSTRUCTION"; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; - case EXCEPTION_STACK_OVERFLOW: - return "EXCEPTION_STACK_OVERFLOW"; - case EXCEPTION_INVALID_DISPOSITION: - return "EXCEPTION_INVALID_DISPOSITION"; - case EXCEPTION_GUARD_PAGE: - return "EXCEPTION_GUARD_PAGE"; - case EXCEPTION_INVALID_HANDLE: - return "EXCEPTION_INVALID_HANDLE"; - default: - return "unknown exception type"; - } -} - -void DebugDebuggee(PROCESS_INFORMATION& pi) { - DEBUG_EVENT deb_ev = {}; - - while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) { - const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE); - if (!wait_success) { - fmt::print(stderr, "error: WaitForDebugEvent failed ({})", GetLastError()); - return; - } - - switch (deb_ev.dwDebugEventCode) { - case OUTPUT_DEBUG_STRING_EVENT: - case CREATE_PROCESS_DEBUG_EVENT: - case CREATE_THREAD_DEBUG_EVENT: - case EXIT_PROCESS_DEBUG_EVENT: - case EXIT_THREAD_DEBUG_EVENT: - case LOAD_DLL_DEBUG_EVENT: - case RIP_EVENT: - case UNLOAD_DLL_DEBUG_EVENT: - // Continue on all other debug events - ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); - break; - case EXCEPTION_DEBUG_EVENT: - EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; - - // We want to generate a crash dump if we are seeing the same exception again. - if (!deb_ev.u.Exception.dwFirstChance) { - fmt::print(stderr, "Creating MiniDump on ExceptionCode: 0x{:08x} {}\n", - record.ExceptionCode, ExceptionName(record.ExceptionCode)); - DumpFromDebugEvent(deb_ev, pi); - } - - // Continue without handling the exception. - // Lets the debuggee use its own exception handler. - // - If one does not exist, we will see the exception once more where we make a minidump - // for. Then when it reaches here again, yuzu will probably crash. - // - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an - // infinite loop of exceptions. - ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); - break; - } - } -} - -} // namespace MiniDump diff --git a/src/yuzu/mini_dump.h b/src/yuzu/mini_dump.h deleted file mode 100644 index d6b6cca84..000000000 --- a/src/yuzu/mini_dump.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include - -namespace MiniDump { - -void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, - EXCEPTION_POINTERS* pep); - -void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi); -bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi); -void DebugDebuggee(PROCESS_INFORMATION& pi); - -} // namespace MiniDump -- cgit v1.2.3 From 0b7593d352ce786b2a6d0610b9b921f329b59175 Mon Sep 17 00:00:00 2001 From: flodavid Date: Sun, 15 Oct 2023 22:20:37 +0200 Subject: yuzu: Improve behavior when clicking on controller box in Controller applet - Apply changes on Controller configuration of commit 9524d70 to Controller applet - Fix regression of this previous commit: Enabling a controller in its tab did not activate previous controllers Signed-off-by: flodavid --- src/yuzu/applets/qt_controller.cpp | 52 +++++++++++++++++----- src/yuzu/applets/qt_controller.h | 4 ++ src/yuzu/configuration/configure_input.cpp | 59 +++++++++++++++---------- src/yuzu/configuration/configure_input.h | 7 ++- src/yuzu/configuration/configure_input_player.h | 5 +-- 5 files changed, 87 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index ca0e14fad..515cb7ce6 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -155,18 +155,27 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( UpdateBorderColor(i); connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) { - if (checked) { - // Hide eventual error message about number of controllers - ui->labelError->setVisible(false); - for (std::size_t index = 0; index <= i; ++index) { - connected_controller_checkboxes[index]->setChecked(checked); - } - } else { - for (std::size_t index = i; index < NUM_PLAYERS; ++index) { - connected_controller_checkboxes[index]->setChecked(checked); - } + // Reconnect current controller if it was the last one checked + // (player number was reduced by more than one) + const bool reconnect_first = !checked && i < player_groupboxes.size() - 1 && + player_groupboxes[i + 1]->isChecked(); + + // Ensures that connecting a controller changes the number of players + if (connected_controller_checkboxes[i]->isChecked() != checked) { + // Ensures that the players are always connected in sequential order + PropagatePlayerNumberChanged(i, checked, reconnect_first); } }); + connect(connected_controller_checkboxes[i], &QCheckBox::clicked, [this, i](bool checked) { + // Reconnect current controller if it was the last one checked + // (player number was reduced by more than one) + const bool reconnect_first = !checked && + i < connected_controller_checkboxes.size() - 1 && + connected_controller_checkboxes[i + 1]->isChecked(); + + // Ensures that the players are always connected in sequential order + PropagatePlayerNumberChanged(i, checked, reconnect_first); + }); connect(emulated_controllers[i], qOverload(&QComboBox::currentIndexChanged), [this, i](int) { @@ -668,6 +677,29 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) { } } +void QtControllerSelectorDialog::PropagatePlayerNumberChanged(size_t player_index, bool checked, + bool reconnect_current) { + connected_controller_checkboxes[player_index]->setChecked(checked); + // Hide eventual error message about number of controllers + ui->labelError->setVisible(false); + + if (checked) { + // Check all previous buttons when checked + if (player_index > 0) { + PropagatePlayerNumberChanged(player_index - 1, checked); + } + } else { + // Unchecked all following buttons when unchecked + if (player_index < connected_controller_checkboxes.size() - 1) { + PropagatePlayerNumberChanged(player_index + 1, checked); + } + } + + if (reconnect_current) { + connected_controller_checkboxes[player_index]->setCheckState(Qt::Checked); + } +} + void QtControllerSelectorDialog::DisableUnsupportedPlayers() { const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h index 7f0673d06..e5372495d 100644 --- a/src/yuzu/applets/qt_controller.h +++ b/src/yuzu/applets/qt_controller.h @@ -100,6 +100,10 @@ private: // Updates the console mode. void UpdateDockedState(bool is_handheld); + // Enable preceding controllers or disable following ones + void PropagatePlayerNumberChanged(size_t player_index, bool checked, + bool reconnect_current = false); + // Disables and disconnects unsupported players based on the given parameters. void DisableUnsupportedPlayers(); diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 5a48e388b..3dcad2701 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -101,13 +101,13 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, }; - player_connected = { + connected_controller_checkboxes = { ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected, ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected, ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, }; - std::array player_connected_labels = { + std::array connected_controller_labels = { ui->label, ui->label_3, ui->label_4, ui->label_5, ui->label_6, ui->label_7, ui->label_8, ui->label_9, }; @@ -115,23 +115,37 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, for (std::size_t i = 0; i < player_tabs.size(); ++i) { player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); player_tabs[i]->layout()->addWidget(player_controllers[i]); - connect(player_connected[i], &QCheckBox::clicked, [this, i](int checked) { - // Ensures that the controllers are always connected in sequential order - this->propagateMouseClickOnPlayers(i, checked, true); + connect(player_controllers[i], &ConfigureInputPlayer::Connected, [this, i](bool checked) { + // Ensures that connecting a controller changes the number of players + if (connected_controller_checkboxes[i]->isChecked() != checked) { + // Ensures that the players are always connected in sequential order + PropagatePlayerNumberChanged(i, checked); + } + }); + connect(connected_controller_checkboxes[i], &QCheckBox::clicked, [this, i](bool checked) { + // Reconnect current controller if it was the last one checked + // (player number was reduced by more than one) + const bool reconnect_first = !checked && + i < connected_controller_checkboxes.size() - 1 && + connected_controller_checkboxes[i + 1]->isChecked(); + + // Ensures that the players are always connected in sequential order + PropagatePlayerNumberChanged(i, checked, reconnect_first); }); connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, &ConfigureInput::UpdateAllInputDevices); connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this, &ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection); - connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) { + connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) { + // Keep activated controllers synced with the "Connected Controllers" checkboxes player_controllers[i]->ConnectPlayer(state == Qt::Checked); }); // Remove/hide all the elements that exceed max_players, if applicable. if (i >= max_players) { ui->tabWidget->removeTab(static_cast(max_players)); - player_connected[i]->hide(); - player_connected_labels[i]->hide(); + connected_controller_checkboxes[i]->hide(); + connected_controller_labels[i]->hide(); } } // Only the first player can choose handheld mode so connect the signal just to player 1 @@ -175,28 +189,25 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, LoadConfiguration(); } -void ConfigureInput::propagateMouseClickOnPlayers(size_t player_index, bool checked, bool origin) { - // Origin has already been toggled - if (!origin) { - player_connected[player_index]->setChecked(checked); - } +void ConfigureInput::PropagatePlayerNumberChanged(size_t player_index, bool checked, + bool reconnect_current) { + connected_controller_checkboxes[player_index]->setChecked(checked); if (checked) { // Check all previous buttons when checked if (player_index > 0) { - propagateMouseClickOnPlayers(player_index - 1, checked, false); + PropagatePlayerNumberChanged(player_index - 1, checked); } } else { // Unchecked all following buttons when unchecked - if (player_index < player_tabs.size() - 1) { - // Reconnect current player if it was the last one checked - // (player number was reduced by more than one) - if (origin && player_connected[player_index + 1]->checkState() == Qt::Checked) { - player_connected[player_index]->setCheckState(Qt::Checked); - } - propagateMouseClickOnPlayers(player_index + 1, checked, false); + if (player_index < connected_controller_checkboxes.size() - 1) { + PropagatePlayerNumberChanged(player_index + 1, checked); } } + + if (reconnect_current) { + connected_controller_checkboxes[player_index]->setCheckState(Qt::Checked); + } } QList ConfigureInput::GetSubTabs() const { @@ -249,17 +260,17 @@ void ConfigureInput::LoadConfiguration() { } void ConfigureInput::LoadPlayerControllerIndices() { - for (std::size_t i = 0; i < player_connected.size(); ++i) { + for (std::size_t i = 0; i < connected_controller_checkboxes.size(); ++i) { if (i == 0) { auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); if (handheld->IsConnected()) { - player_connected[i]->setChecked(true); + connected_controller_checkboxes[i]->setChecked(true); continue; } } const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(i); - player_connected[i]->setChecked(controller->IsConnected()); + connected_controller_checkboxes[i]->setChecked(controller->IsConnected()); } } diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index abb7f7089..136cd3a0a 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -56,7 +56,9 @@ private: void UpdateDockedState(bool is_handheld); void UpdateAllInputDevices(); void UpdateAllInputProfiles(std::size_t player_index); - void propagateMouseClickOnPlayers(size_t player_index, bool origin, bool checked); + // Enable preceding controllers or disable following ones + void PropagatePlayerNumberChanged(size_t player_index, bool checked, + bool reconnect_current = false); /// Load configuration settings. void LoadConfiguration(); @@ -71,7 +73,8 @@ private: std::array player_controllers; std::array player_tabs; - std::array player_connected; + // Checkboxes representing the "Connected Controllers". + std::array connected_controller_checkboxes; ConfigureInputAdvanced* advanced; Core::System& system; diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index d4df43d73..d3255d2b4 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -75,7 +75,7 @@ public: void ClearAll(); signals: - /// Emitted when this controller is connected by the user. + /// Emitted when this controller is (dis)connected by the user. void Connected(bool connected); /// Emitted when the Handheld mode is selected (undocked with dual joycons attached). void HandheldStateChanged(bool is_handheld); @@ -183,9 +183,6 @@ private: /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum. std::vector> index_controller_type_pairs; - static constexpr int PLAYER_COUNT = 8; - std::array player_connected_checkbox; - /// This will be the the setting function when an input is awaiting configuration. std::optional> input_setter; -- cgit v1.2.3 From 689f346e9728bde1944808dc0b1984349e9895cf Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 20 Oct 2023 10:17:16 -0400 Subject: nvnflinger: fix reporting and freeing of preallocated buffers Co-authored-by: Kelebek1 --- src/core/hle/service/nvnflinger/buffer_queue_core.cpp | 6 +++--- src/core/hle/service/nvnflinger/buffer_queue_producer.cpp | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp index 2dbe29616..ed66f6f5b 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp @@ -41,7 +41,7 @@ bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock& lk) s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer. if (!use_async_buffer) { - return max_acquired_buffer_count; + return 0; } if (dequeue_buffer_cannot_block || async) { @@ -52,7 +52,7 @@ s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { } s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const { - return GetMinUndequeuedBufferCountLocked(async) + 1; + return GetMinUndequeuedBufferCountLocked(async); } s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const { @@ -61,7 +61,7 @@ s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const { if (override_max_buffer_count != 0) { ASSERT(override_max_buffer_count >= min_buffer_count); - max_buffer_count = override_max_buffer_count; + return override_max_buffer_count; } // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index dc6917d5d..6e7a49658 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -134,7 +134,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); if (async && core->override_max_buffer_count) { if (core->override_max_buffer_count < max_buffer_count) { - LOG_ERROR(Service_Nvnflinger, "async mode is invalid with buffer count override"); + *found = BufferQueueCore::INVALID_BUFFER_SLOT; return Status::BadValue; } } @@ -142,7 +142,8 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St // Free up any buffers that are in slots beyond the max buffer count for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { ASSERT(slots[s].buffer_state == BufferState::Free); - if (slots[s].graphic_buffer != nullptr) { + if (slots[s].graphic_buffer != nullptr && slots[s].buffer_state == BufferState::Free && + !slots[s].is_preallocated) { core->FreeBufferLocked(s); *return_flags |= Status::ReleaseAllBuffers; } -- cgit v1.2.3 From 8c59543ee32c8bff575bab7ec1e70f76f8eda437 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 21 Oct 2023 16:47:43 -0400 Subject: kernel: update KProcess --- src/core/arm/arm_interface.cpp | 6 +- src/core/core.cpp | 8 - src/core/debugger/debugger.cpp | 24 +- src/core/debugger/gdbstub.cpp | 42 +- src/core/file_sys/program_metadata.cpp | 10 +- src/core/file_sys/program_metadata.h | 15 +- .../kernel/board/nintendo/nx/k_system_control.cpp | 59 + .../kernel/board/nintendo/nx/k_system_control.h | 13 + src/core/hle/kernel/k_capabilities.h | 4 +- src/core/hle/kernel/k_condition_variable.cpp | 22 +- src/core/hle/kernel/k_condition_variable.h | 9 +- src/core/hle/kernel/k_interrupt_manager.cpp | 2 +- src/core/hle/kernel/k_memory_manager.cpp | 125 +- src/core/hle/kernel/k_memory_manager.h | 12 +- src/core/hle/kernel/k_page_table.cpp | 14 +- src/core/hle/kernel/k_page_table.h | 4 +- src/core/hle/kernel/k_process.cpp | 1512 +++++++++++++------- src/core/hle/kernel/k_process.h | 724 +++++----- src/core/hle/kernel/k_scheduler.cpp | 4 +- src/core/hle/kernel/k_system_resource.cpp | 87 +- src/core/hle/kernel/k_thread.cpp | 16 +- src/core/hle/kernel/k_thread.h | 1 + src/core/hle/kernel/kernel.cpp | 54 +- src/core/hle/kernel/kernel.h | 3 - src/core/hle/kernel/svc.cpp | 2 +- src/core/hle/kernel/svc/svc_info.cpp | 28 +- src/core/hle/kernel/svc/svc_lock.cpp | 4 +- src/core/hle/kernel/svc/svc_physical_memory.cpp | 4 +- src/core/hle/kernel/svc/svc_synchronization.cpp | 2 +- src/core/hle/kernel/svc/svc_thread.cpp | 7 +- src/core/hle/kernel/svc_generator.py | 2 +- src/core/hle/kernel/svc_types.h | 46 +- src/core/hle/service/kernel_helpers.cpp | 6 +- src/core/hle/service/nvnflinger/nvnflinger.cpp | 15 +- src/core/hle/service/nvnflinger/nvnflinger.h | 3 - src/core/hle/service/pm/pm.cpp | 2 +- src/core/reporter.cpp | 2 +- src/yuzu/debugger/wait_tree.cpp | 2 +- src/yuzu/main.cpp | 2 +- 39 files changed, 1846 insertions(+), 1051 deletions(-) (limited to 'src') diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 0c012f094..5e27dde58 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -86,9 +86,9 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector symbols; for (const auto& module : modules) { - symbols.insert_or_assign( - module.second, Symbols::GetSymbols(module.first, system.ApplicationMemory(), - system.ApplicationProcess()->Is64BitProcess())); + symbols.insert_or_assign(module.second, + Symbols::GetSymbols(module.first, system.ApplicationMemory(), + system.ApplicationProcess()->Is64Bit())); } for (auto& entry : out) { diff --git a/src/core/core.cpp b/src/core/core.cpp index d7e2efbd7..296727ed7 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -309,16 +309,8 @@ struct System::Impl { telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); - // Create a resource limit for the process. - const auto physical_memory_size = - kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); - auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size); - // Create the process. auto main_process = Kernel::KProcess::Create(system.Kernel()); - ASSERT(Kernel::KProcess::Initialize(main_process, system, "main", - Kernel::KProcess::ProcessType::Userland, resource_limit) - .IsSuccess()); Kernel::KProcess::Register(system.Kernel(), main_process); kernel.MakeApplicationProcess(main_process); const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index a1589fecb..0e270eb50 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -258,20 +258,20 @@ private: Kernel::KScopedSchedulerLock sl{system.Kernel()}; // Put all threads to sleep on next scheduler round. - for (auto* thread : ThreadList()) { - thread->RequestSuspend(Kernel::SuspendType::Debug); + for (auto& thread : ThreadList()) { + thread.RequestSuspend(Kernel::SuspendType::Debug); } } void ResumeEmulation(Kernel::KThread* except = nullptr) { // Wake up all threads. - for (auto* thread : ThreadList()) { - if (thread == except) { + for (auto& thread : ThreadList()) { + if (std::addressof(thread) == except) { continue; } - thread->SetStepState(Kernel::StepState::NotStepping); - thread->Resume(Kernel::SuspendType::Debug); + thread.SetStepState(Kernel::StepState::NotStepping); + thread.Resume(Kernel::SuspendType::Debug); } } @@ -283,13 +283,17 @@ private: } void UpdateActiveThread() { - const auto& threads{ThreadList()}; - if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) { - state->active_thread = threads.front(); + auto& threads{ThreadList()}; + for (auto& thread : threads) { + if (std::addressof(thread) == state->active_thread) { + // Thread is still alive, no need to update. + return; + } } + state->active_thread = std::addressof(threads.front()); } - const std::list& ThreadList() { + Kernel::KProcess::ThreadList& ThreadList() { return system.ApplicationProcess()->GetThreadList(); } diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 2076aa8a2..6f5f5156b 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -109,7 +109,7 @@ static std::string EscapeXML(std::string_view data) { GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) : DebuggerFrontend(backend_), system{system_} { - if (system.ApplicationProcess()->Is64BitProcess()) { + if (system.ApplicationProcess()->Is64Bit()) { arch = std::make_unique(); } else { arch = std::make_unique(); @@ -446,10 +446,10 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) { // See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp static std::optional GetNameFromThreadType32(Core::Memory::Memory& memory, - const Kernel::KThread* thread) { + const Kernel::KThread& thread) { // Read thread type from TLS - const VAddr tls_thread_type{memory.Read32(thread->GetTlsAddress() + 0x1fc)}; - const VAddr argument_thread_type{thread->GetArgument()}; + const VAddr tls_thread_type{memory.Read32(thread.GetTlsAddress() + 0x1fc)}; + const VAddr argument_thread_type{thread.GetArgument()}; if (argument_thread_type && tls_thread_type != argument_thread_type) { // Probably not created by nnsdk, no name available. @@ -477,10 +477,10 @@ static std::optional GetNameFromThreadType32(Core::Memory::Memory& } static std::optional GetNameFromThreadType64(Core::Memory::Memory& memory, - const Kernel::KThread* thread) { + const Kernel::KThread& thread) { // Read thread type from TLS - const VAddr tls_thread_type{memory.Read64(thread->GetTlsAddress() + 0x1f8)}; - const VAddr argument_thread_type{thread->GetArgument()}; + const VAddr tls_thread_type{memory.Read64(thread.GetTlsAddress() + 0x1f8)}; + const VAddr argument_thread_type{thread.GetArgument()}; if (argument_thread_type && tls_thread_type != argument_thread_type) { // Probably not created by nnsdk, no name available. @@ -508,16 +508,16 @@ static std::optional GetNameFromThreadType64(Core::Memory::Memory& } static std::optional GetThreadName(Core::System& system, - const Kernel::KThread* thread) { - if (system.ApplicationProcess()->Is64BitProcess()) { + const Kernel::KThread& thread) { + if (system.ApplicationProcess()->Is64Bit()) { return GetNameFromThreadType64(system.ApplicationMemory(), thread); } else { return GetNameFromThreadType32(system.ApplicationMemory(), thread); } } -static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { - switch (thread->GetWaitReasonForDebugging()) { +static std::string_view GetThreadWaitReason(const Kernel::KThread& thread) { + switch (thread.GetWaitReasonForDebugging()) { case Kernel::ThreadWaitReasonForDebugging::Sleep: return "Sleep"; case Kernel::ThreadWaitReasonForDebugging::IPC: @@ -535,8 +535,8 @@ static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { } } -static std::string GetThreadState(const Kernel::KThread* thread) { - switch (thread->GetState()) { +static std::string GetThreadState(const Kernel::KThread& thread) { + switch (thread.GetState()) { case Kernel::ThreadState::Initialized: return "Initialized"; case Kernel::ThreadState::Waiting: @@ -604,7 +604,7 @@ void GDBStub::HandleQuery(std::string_view command) { const auto& threads = system.ApplicationProcess()->GetThreadList(); std::vector thread_ids; for (const auto& thread : threads) { - thread_ids.push_back(fmt::format("{:x}", thread->GetThreadId())); + thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId())); } SendReply(fmt::format("m{}", fmt::join(thread_ids, ","))); } else if (command.starts_with("sThreadInfo")) { @@ -616,14 +616,14 @@ void GDBStub::HandleQuery(std::string_view command) { buffer += ""; const auto& threads = system.ApplicationProcess()->GetThreadList(); - for (const auto* thread : threads) { + for (const auto& thread : threads) { auto thread_name{GetThreadName(system, thread)}; if (!thread_name) { - thread_name = fmt::format("Thread {:d}", thread->GetThreadId()); + thread_name = fmt::format("Thread {:d}", thread.GetThreadId()); } buffer += fmt::format(R"({})", - thread->GetThreadId(), thread->GetActiveCore(), + thread.GetThreadId(), thread.GetActiveCore(), EscapeXML(*thread_name), GetThreadState(thread)); } @@ -850,10 +850,10 @@ void GDBStub::HandleRcmd(const std::vector& command) { } Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { - const auto& threads{system.ApplicationProcess()->GetThreadList()}; - for (auto* thread : threads) { - if (thread->GetThreadId() == thread_id) { - return thread; + auto& threads{system.ApplicationProcess()->GetThreadList()}; + for (auto& thread : threads) { + if (thread.GetThreadId() == thread_id) { + return std::addressof(thread); } } diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 8e291ff67..763a44fee 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -104,16 +104,16 @@ Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) { } /*static*/ ProgramMetadata ProgramMetadata::GetDefault() { - // Allow use of cores 0~3 and thread priorities 1~63. - constexpr u32 default_thread_info_capability = 0x30007F7; + // Allow use of cores 0~3 and thread priorities 16~63. + constexpr u32 default_thread_info_capability = 0x30043F7; ProgramMetadata result; result.LoadManual( true /*is_64_bit*/, FileSys::ProgramAddressSpaceType::Is39Bit /*address_space*/, - 0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x00100000 /*main_thread_stack_size*/, - 0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/, - 0x1FE00000 /*system_resource_size*/, {default_thread_info_capability} /*capabilities*/); + 0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x100000 /*main_thread_stack_size*/, + 0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/, 0 /*system_resource_size*/, + {default_thread_info_capability} /*capabilities*/); return result; } diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 9f8e74b13..76ee97d78 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -73,6 +73,9 @@ public: u64 GetFilesystemPermissions() const; u32 GetSystemResourceSize() const; const KernelCapabilityDescriptors& GetKernelCapabilities() const; + const std::array& GetName() const { + return npdm_header.application_name; + } void Print() const; @@ -164,14 +167,14 @@ private: u32_le unk_size_2; }; - Header npdm_header; - AciHeader aci_header; - AcidHeader acid_header; + Header npdm_header{}; + AciHeader aci_header{}; + AcidHeader acid_header{}; - FileAccessControl acid_file_access; - FileAccessHeader aci_file_access; + FileAccessControl acid_file_access{}; + FileAccessHeader aci_file_access{}; - KernelCapabilityDescriptors aci_kernel_capabilities; + KernelCapabilityDescriptors aci_kernel_capabilities{}; }; } // namespace FileSys diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp index 4cfdf4558..59364efa1 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp @@ -8,7 +8,11 @@ #include "core/hle/kernel/board/nintendo/nx/k_system_control.h" #include "core/hle/kernel/board/nintendo/nx/secure_monitor.h" +#include "core/hle/kernel/k_memory_manager.h" +#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_trace.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc_results.h" namespace Kernel::Board::Nintendo::Nx { @@ -30,6 +34,8 @@ constexpr const std::size_t RequiredNonSecureSystemMemorySize = constexpr const std::size_t RequiredNonSecureSystemMemorySizeWithFatal = RequiredNonSecureSystemMemorySize + impl::RequiredNonSecureSystemMemorySizeViFatal; +constexpr const std::size_t SecureAlignment = 128_KiB; + namespace { using namespace Common::Literals; @@ -183,4 +189,57 @@ u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) { return GenerateUniformRange(min, max, GenerateRandomU64); } +size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) { + if (pool == static_cast(KMemoryManager::Pool::Applet)) { + return 0; + } else { + // return KSystemControlBase::CalculateRequiredSecureMemorySize(size, pool); + return size; + } +} + +Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size, + u32 pool) { + // Applet secure memory is handled separately. + UNIMPLEMENTED_IF(pool == static_cast(KMemoryManager::Pool::Applet)); + + // Ensure the size is aligned. + const size_t alignment = + (pool == static_cast(KMemoryManager::Pool::System) ? PageSize : SecureAlignment); + R_UNLESS(Common::IsAligned(size, alignment), ResultInvalidSize); + + // Allocate the memory. + const size_t num_pages = size / PageSize; + const KPhysicalAddress paddr = kernel.MemoryManager().AllocateAndOpenContinuous( + num_pages, alignment / PageSize, + KMemoryManager::EncodeOption(static_cast(pool), + KMemoryManager::Direction::FromFront)); + R_UNLESS(paddr != 0, ResultOutOfMemory); + + // Ensure we don't leak references to the memory on error. + ON_RESULT_FAILURE { + kernel.MemoryManager().Close(paddr, num_pages); + }; + + // We succeeded. + *out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr); + R_SUCCEED(); +} + +void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size, + u32 pool) { + // Applet secure memory is handled separately. + UNIMPLEMENTED_IF(pool == static_cast(KMemoryManager::Pool::Applet)); + + // Ensure the size is aligned. + const size_t alignment = + (pool == static_cast(KMemoryManager::Pool::System) ? PageSize : SecureAlignment); + ASSERT(Common::IsAligned(GetInteger(address), alignment)); + ASSERT(Common::IsAligned(size, alignment)); + + // Close the secure region's pages. + kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address), + size / PageSize); +} + } // namespace Kernel::Board::Nintendo::Nx diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h index b477e8193..ff1feec70 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h @@ -4,6 +4,11 @@ #pragma once #include "core/hle/kernel/k_typed_address.h" +#include "core/hle/result.h" + +namespace Kernel { +class KernelCore; +} namespace Kernel::Board::Nintendo::Nx { @@ -25,8 +30,16 @@ public: static std::size_t GetMinimumNonSecureSystemPoolSize(); }; + // Randomness. static u64 GenerateRandomRange(u64 min, u64 max); static u64 GenerateRandomU64(); + + // Secure Memory. + static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool); + static Result AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size, + u32 pool); + static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size, + u32 pool); }; } // namespace Kernel::Board::Nintendo::Nx diff --git a/src/core/hle/kernel/k_capabilities.h b/src/core/hle/kernel/k_capabilities.h index de766c811..ebd4eedb1 100644 --- a/src/core/hle/kernel/k_capabilities.h +++ b/src/core/hle/kernel/k_capabilities.h @@ -200,8 +200,8 @@ private: RawCapabilityValue raw; BitField<0, 15, CapabilityType> id; - BitField<15, 4, u32> major_version; - BitField<19, 13, u32> minor_version; + BitField<15, 4, u32> minor_version; + BitField<19, 13, u32> major_version; }; union HandleTable { diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index efbac0e6a..7633a51fb 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -107,12 +107,12 @@ KConditionVariable::KConditionVariable(Core::System& system) KConditionVariable::~KConditionVariable() = default; -Result KConditionVariable::SignalToAddress(KProcessAddress addr) { - KThread* owner_thread = GetCurrentThreadPointer(m_kernel); +Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress addr) { + KThread* owner_thread = GetCurrentThreadPointer(kernel); // Signal the address. { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Remove waiter thread. bool has_waiters{}; @@ -133,7 +133,7 @@ Result KConditionVariable::SignalToAddress(KProcessAddress addr) { // Write the value to userspace. Result result{ResultSuccess}; - if (WriteToUser(m_kernel, addr, std::addressof(next_value))) [[likely]] { + if (WriteToUser(kernel, addr, std::addressof(next_value))) [[likely]] { result = ResultSuccess; } else { result = ResultInvalidCurrentMemory; @@ -148,28 +148,28 @@ Result KConditionVariable::SignalToAddress(KProcessAddress addr) { } } -Result KConditionVariable::WaitForAddress(Handle handle, KProcessAddress addr, u32 value) { - KThread* cur_thread = GetCurrentThreadPointer(m_kernel); - ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(m_kernel); +Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, + u32 value) { + KThread* cur_thread = GetCurrentThreadPointer(kernel); + ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel); // Wait for the address. KThread* owner_thread{}; { - KScopedSchedulerLock sl(m_kernel); + KScopedSchedulerLock sl(kernel); // Check if the thread should terminate. R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested); // Read the tag from userspace. u32 test_tag{}; - R_UNLESS(ReadFromUser(m_kernel, std::addressof(test_tag), addr), - ResultInvalidCurrentMemory); + R_UNLESS(ReadFromUser(kernel, std::addressof(test_tag), addr), ResultInvalidCurrentMemory); // If the tag isn't the handle (with wait mask), we're done. R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask)); // Get the lock owner thread. - owner_thread = GetCurrentProcess(m_kernel) + owner_thread = GetCurrentProcess(kernel) .GetHandleTable() .GetObjectWithoutPseudoHandle(handle) .ReleasePointerUnsafe(); diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h index 8c2f3ae51..2620c8e39 100644 --- a/src/core/hle/kernel/k_condition_variable.h +++ b/src/core/hle/kernel/k_condition_variable.h @@ -24,11 +24,12 @@ public: explicit KConditionVariable(Core::System& system); ~KConditionVariable(); - // Arbitration - Result SignalToAddress(KProcessAddress addr); - Result WaitForAddress(Handle handle, KProcessAddress addr, u32 value); + // Arbitration. + static Result SignalToAddress(KernelCore& kernel, KProcessAddress addr); + static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, + u32 value); - // Condition variable + // Condition variable. void Signal(u64 cv_key, s32 count); Result Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout); diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp index fe6a20168..22d79569a 100644 --- a/src/core/hle/kernel/k_interrupt_manager.cpp +++ b/src/core/hle/kernel/k_interrupt_manager.cpp @@ -22,7 +22,7 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) { KScopedSchedulerLock sl{kernel}; // Pin the current thread. - process->PinCurrentThread(core_id); + process->PinCurrentThread(); // Set the interrupt flag for the thread. GetCurrentThread(kernel).SetInterruptFlag(); diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index 637558e10..cdc5572d8 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp @@ -11,6 +11,7 @@ #include "core/hle/kernel/initial_process.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_page_group.h" +#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" @@ -168,11 +169,37 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage } Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) { - UNREACHABLE(); + const u32 pool_index = static_cast(pool); + + // Lock the pool. + KScopedLightLock lk(m_pool_locks[pool_index]); + + // Check that we don't already have an optimized process. + R_UNLESS(!m_has_optimized_process[pool_index], ResultBusy); + + // Set the optimized process id. + m_optimized_process_ids[pool_index] = process_id; + m_has_optimized_process[pool_index] = true; + + // Clear the management area for the optimized process. + for (auto* manager = this->GetFirstManager(pool, Direction::FromFront); manager != nullptr; + manager = this->GetNextManager(manager, Direction::FromFront)) { + manager->InitializeOptimizedMemory(m_system.Kernel()); + } + + R_SUCCEED(); } void KMemoryManager::FinalizeOptimizedMemory(u64 process_id, Pool pool) { - UNREACHABLE(); + const u32 pool_index = static_cast(pool); + + // Lock the pool. + KScopedLightLock lk(m_pool_locks[pool_index]); + + // If the process was optimized, clear it. + if (m_has_optimized_process[pool_index] && m_optimized_process_ids[pool_index] == process_id) { + m_has_optimized_process[pool_index] = false; + } } KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, @@ -207,7 +234,7 @@ KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, siz // Maintain the optimized memory bitmap, if we should. if (m_has_optimized_process[static_cast(pool)]) { - UNIMPLEMENTED(); + chosen_manager->TrackUnoptimizedAllocation(m_system.Kernel(), allocated_block, num_pages); } // Open the first reference to the pages. @@ -255,7 +282,8 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, // Maintain the optimized memory bitmap, if we should. if (unoptimized) { - UNIMPLEMENTED(); + cur_manager->TrackUnoptimizedAllocation(m_system.Kernel(), allocated_block, + pages_per_alloc); } num_pages -= pages_per_alloc; @@ -358,8 +386,8 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 // Process part or all of the block. const size_t cur_pages = std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); - any_new = - manager.ProcessOptimizedAllocation(cur_address, cur_pages, fill_pattern); + any_new = manager.ProcessOptimizedAllocation(m_system.Kernel(), cur_address, + cur_pages, fill_pattern); // Advance. cur_address += cur_pages * PageSize; @@ -382,7 +410,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 // Track some or all of the current pages. const size_t cur_pages = std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); - manager.TrackOptimizedAllocation(cur_address, cur_pages); + manager.TrackOptimizedAllocation(m_system.Kernel(), cur_address, cur_pages); // Advance. cur_address += cur_pages * PageSize; @@ -427,17 +455,86 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size, return total_management_size; } -void KMemoryManager::Impl::TrackUnoptimizedAllocation(KPhysicalAddress block, size_t num_pages) { - UNREACHABLE(); +void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) { + auto optimize_pa = + KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); + auto* optimize_map = kernel.System().DeviceMemory().GetPointer(optimize_pa); + + std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); } -void KMemoryManager::Impl::TrackOptimizedAllocation(KPhysicalAddress block, size_t num_pages) { - UNREACHABLE(); +void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, + size_t num_pages) { + auto optimize_pa = + KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); + auto* optimize_map = kernel.System().DeviceMemory().GetPointer(optimize_pa); + + // Get the range we're tracking. + size_t offset = this->GetPageOffset(block); + const size_t last = offset + num_pages - 1; + + // Track. + while (offset <= last) { + // Mark the page as not being optimized-allocated. + optimize_map[offset / Common::BitSize()] &= + ~(u64(1) << (offset % Common::BitSize())); + + offset++; + } +} + +void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, + size_t num_pages) { + auto optimize_pa = + KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); + auto* optimize_map = kernel.System().DeviceMemory().GetPointer(optimize_pa); + + // Get the range we're tracking. + size_t offset = this->GetPageOffset(block); + const size_t last = offset + num_pages - 1; + + // Track. + while (offset <= last) { + // Mark the page as being optimized-allocated. + optimize_map[offset / Common::BitSize()] |= + (u64(1) << (offset % Common::BitSize())); + + offset++; + } } -bool KMemoryManager::Impl::ProcessOptimizedAllocation(KPhysicalAddress block, size_t num_pages, - u8 fill_pattern) { - UNREACHABLE(); +bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, + size_t num_pages, u8 fill_pattern) { + auto& device_memory = kernel.System().DeviceMemory(); + auto optimize_pa = + KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); + auto* optimize_map = device_memory.GetPointer(optimize_pa); + + // We want to return whether any pages were newly allocated. + bool any_new = false; + + // Get the range we're processing. + size_t offset = this->GetPageOffset(block); + const size_t last = offset + num_pages - 1; + + // Process. + while (offset <= last) { + // Check if the page has been optimized-allocated before. + if ((optimize_map[offset / Common::BitSize()] & + (u64(1) << (offset % Common::BitSize()))) == 0) { + // If not, it's new. + any_new = true; + + // Fill the page. + auto* ptr = device_memory.GetPointer(m_heap.GetAddress()); + std::memset(ptr + offset * PageSize, fill_pattern, PageSize); + } + + offset++; + } + + // Return the number of pages we processed. + return any_new; } size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) { diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h index 7e4b41319..c5a487af9 100644 --- a/src/core/hle/kernel/k_memory_manager.h +++ b/src/core/hle/kernel/k_memory_manager.h @@ -216,14 +216,14 @@ private: m_heap.SetInitialUsedSize(reserved_size); } - void InitializeOptimizedMemory() { - UNIMPLEMENTED(); - } + void InitializeOptimizedMemory(KernelCore& kernel); - void TrackUnoptimizedAllocation(KPhysicalAddress block, size_t num_pages); - void TrackOptimizedAllocation(KPhysicalAddress block, size_t num_pages); + void TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, + size_t num_pages); + void TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, size_t num_pages); - bool ProcessOptimizedAllocation(KPhysicalAddress block, size_t num_pages, u8 fill_pattern); + bool ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, + size_t num_pages, u8 fill_pattern); constexpr Pool GetPool() const { return m_pool; diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 217ccbae3..1d47bdf6b 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -82,14 +82,14 @@ public: using namespace Common::Literals; -constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) { +constexpr size_t GetAddressSpaceWidthFromType(Svc::CreateProcessFlag as_type) { switch (as_type) { - case FileSys::ProgramAddressSpaceType::Is32Bit: - case FileSys::ProgramAddressSpaceType::Is32BitNoMap: + case Svc::CreateProcessFlag::AddressSpace32Bit: + case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias: return 32; - case FileSys::ProgramAddressSpaceType::Is36Bit: + case Svc::CreateProcessFlag::AddressSpace64BitDeprecated: return 36; - case FileSys::ProgramAddressSpaceType::Is39Bit: + case Svc::CreateProcessFlag::AddressSpace64Bit: return 39; default: ASSERT(false); @@ -105,7 +105,7 @@ KPageTable::KPageTable(Core::System& system_) KPageTable::~KPageTable() = default; -Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, +Result KPageTable::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_addr, size_t code_size, KSystemResource* system_resource, @@ -133,7 +133,7 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type ASSERT(code_addr + code_size - 1 <= end - 1); // Adjust heap/alias size if we don't have an alias region - if (as_type == FileSys::ProgramAddressSpaceType::Is32BitNoMap) { + if (as_type == Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias) { heap_region_size += alias_region_size; alias_region_size = 0; } diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 3d64b6fb0..66f16faaf 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -63,7 +63,7 @@ public: explicit KPageTable(Core::System& system_); ~KPageTable(); - Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, + Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_addr, size_t code_size, KSystemResource* system_resource, KResourceLimit* resource_limit, @@ -400,7 +400,7 @@ public: constexpr size_t GetAliasCodeRegionSize() const { return m_alias_code_region_end - m_alias_code_region_start; } - size_t GetNormalMemorySize() { + size_t GetNormalMemorySize() const { KScopedLightLock lk(m_general_lock); return GetHeapSize() + m_mapped_physical_memory_size; } diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 7fa34d693..1f4b0755d 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -1,119 +1,731 @@ -// SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include -#include #include -#include "common/alignment.h" -#include "common/assert.h" -#include "common/logging/log.h" #include "common/scope_exit.h" #include "common/settings.h" #include "core/core.h" -#include "core/file_sys/program_metadata.h" -#include "core/hle/kernel/code_set.h" -#include "core/hle/kernel/k_memory_block_manager.h" -#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_resource_limit.h" -#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory_info.h" -#include "core/hle/kernel/k_thread.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/svc_results.h" -#include "core/memory.h" +#include "core/hle/kernel/k_thread_local_page.h" +#include "core/hle/kernel/k_thread_queue.h" +#include "core/hle/kernel/k_worker_task_manager.h" namespace Kernel { + namespace { -/** - * Sets up the primary application thread - * - * @param system The system instance to create the main thread under. - * @param owner_process The parent process for the main thread - * @param priority The priority to give the main thread - */ -void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, - KProcessAddress stack_top) { - const KProcessAddress entry_point = owner_process.GetEntryPoint(); - ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1)); - - KThread* thread = KThread::Create(system.Kernel()); - SCOPE_EXIT({ thread->Close(); }); - - ASSERT(KThread::InitializeUserThread(system, thread, entry_point, 0, stack_top, priority, - owner_process.GetIdealCoreId(), - std::addressof(owner_process)) - .IsSuccess()); - - // Register 1 must be a handle to the main thread - Handle thread_handle{}; - owner_process.GetHandleTable().Add(std::addressof(thread_handle), thread); - - thread->GetContext32().cpu_registers[0] = 0; - thread->GetContext64().cpu_registers[0] = 0; - thread->GetContext32().cpu_registers[1] = thread_handle; - thread->GetContext64().cpu_registers[1] = thread_handle; - - if (system.DebuggerEnabled()) { - thread->RequestSuspend(SuspendType::Debug); + +Result TerminateChildren(KernelCore& kernel, KProcess* process, + const KThread* thread_to_not_terminate) { + // Request that all children threads terminate. + { + KScopedLightLock proc_lk(process->GetListLock()); + KScopedSchedulerLock sl(kernel); + + if (thread_to_not_terminate != nullptr && + process->GetPinnedThread(GetCurrentCoreId(kernel)) == thread_to_not_terminate) { + // NOTE: Here Nintendo unpins the current thread instead of the thread_to_not_terminate. + // This is valid because the only caller which uses non-nullptr as argument uses + // GetCurrentThreadPointer(), but it's still notable because it seems incorrect at + // first glance. + process->UnpinCurrentThread(); + } + + auto& thread_list = process->GetThreadList(); + for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { + if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) { + if (thread->GetState() != ThreadState::Terminated) { + thread->RequestTerminate(); + } + } + } } - // Run our thread. - void(thread->Run()); + // Wait for all children threads to terminate. + while (true) { + // Get the next child. + KThread* cur_child = nullptr; + { + KScopedLightLock proc_lk(process->GetListLock()); + + auto& thread_list = process->GetThreadList(); + for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { + if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) { + if (thread->GetState() != ThreadState::Terminated) { + if (thread->Open()) { + cur_child = thread; + break; + } + } + } + } + } + + // If we didn't find any non-terminated children, we're done. + if (cur_child == nullptr) { + break; + } + + // Terminate and close the thread. + SCOPE_EXIT({ cur_child->Close(); }); + + if (const Result terminate_result = cur_child->Terminate(); + ResultTerminationRequested == terminate_result) { + R_THROW(terminate_result); + } + } + + R_SUCCEED(); } -} // Anonymous namespace -Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, - ProcessType type, KResourceLimit* res_limit) { - auto& kernel = system.Kernel(); +class ThreadQueueImplForKProcessEnterUserException final : public KThreadQueue { +private: + KThread** m_exception_thread; - process->name = std::move(process_name); - process->m_resource_limit = res_limit; - process->m_system_resource_address = 0; - process->m_state = State::Created; - process->m_program_id = 0; - process->m_process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() - : kernel.CreateNewUserProcessID(); - process->m_capabilities.InitializeForMetadatalessProcess(); - process->m_is_initialized = true; +public: + explicit ThreadQueueImplForKProcessEnterUserException(KernelCore& kernel, KThread** t) + : KThreadQueue(kernel), m_exception_thread(t) {} + + virtual void EndWait(KThread* waiting_thread, Result wait_result) override { + // Set the exception thread. + *m_exception_thread = waiting_thread; + + // Invoke the base end wait handler. + KThreadQueue::EndWait(waiting_thread, wait_result); + } + + virtual void CancelWait(KThread* waiting_thread, Result wait_result, + bool cancel_timer_task) override { + // Remove the thread as a waiter on its mutex owner. + waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread); + + // Invoke the base cancel wait handler. + KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); + } +}; +void GenerateRandom(std::span out_random) { std::mt19937 rng(Settings::values.rng_seed_enabled ? Settings::values.rng_seed.GetValue() : static_cast(std::time(nullptr))); std::uniform_int_distribution distribution; - std::generate(process->m_random_entropy.begin(), process->m_random_entropy.end(), - [&] { return distribution(rng); }); + std::generate(out_random.begin(), out_random.end(), [&] { return distribution(rng); }); +} + +} // namespace + +void KProcess::Finalize() { + // Delete the process local region. + this->DeleteThreadLocalRegion(m_plr_address); + + // Get the used memory size. + const size_t used_memory_size = this->GetUsedNonSystemUserPhysicalMemorySize(); + + // Finalize the page table. + m_page_table.Finalize(); + + // Finish using our system resource. + if (m_system_resource) { + if (m_system_resource->IsSecureResource()) { + // Finalize optimized memory. If memory wasn't optimized, this is a no-op. + m_kernel.MemoryManager().FinalizeOptimizedMemory(this->GetId(), m_memory_pool); + } + + m_system_resource->Close(); + m_system_resource = nullptr; + } + + // Free all shared memory infos. + { + auto it = m_shared_memory_list.begin(); + while (it != m_shared_memory_list.end()) { + KSharedMemoryInfo* info = std::addressof(*it); + KSharedMemory* shmem = info->GetSharedMemory(); - kernel.AppendNewProcess(process); + while (!info->Close()) { + shmem->Close(); + } + shmem->Close(); + + it = m_shared_memory_list.erase(it); + KSharedMemoryInfo::Free(m_kernel, info); + } + } + + // Our thread local page list must be empty at this point. + ASSERT(m_partially_used_tlp_tree.empty()); + ASSERT(m_fully_used_tlp_tree.empty()); + + // Release memory to the resource limit. + if (m_resource_limit != nullptr) { + ASSERT(used_memory_size >= m_memory_release_hint); + m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, used_memory_size, + used_memory_size - m_memory_release_hint); + m_resource_limit->Close(); + } + + // Perform inherited finalization. + KSynchronizationObject::Finalize(); +} + +Result KProcess::Initialize(const Svc::CreateProcessParameter& params, KResourceLimit* res_limit, + bool is_real) { + // TODO: remove this special case + if (is_real) { + // Create and clear the process local region. + R_TRY(this->CreateThreadLocalRegion(std::addressof(m_plr_address))); + this->GetMemory().ZeroBlock(m_plr_address, Svc::ThreadLocalRegionSize); + } + + // Copy in the name from parameters. + static_assert(sizeof(params.name) < sizeof(m_name)); + std::memcpy(m_name.data(), params.name.data(), sizeof(params.name)); + m_name[sizeof(params.name)] = 0; + + // Set misc fields. + m_state = State::Created; + m_main_thread_stack_size = 0; + m_used_kernel_memory_size = 0; + m_ideal_core_id = 0; + m_flags = params.flags; + m_version = params.version; + m_program_id = params.program_id; + m_code_address = params.code_address; + m_code_size = params.code_num_pages * PageSize; + m_is_application = True(params.flags & Svc::CreateProcessFlag::IsApplication); + + // Set thread fields. + for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + m_running_threads[i] = nullptr; + m_pinned_threads[i] = nullptr; + m_running_thread_idle_counts[i] = 0; + m_running_thread_switch_counts[i] = 0; + } + + // Set max memory based on address space type. + switch ((params.flags & Svc::CreateProcessFlag::AddressSpaceMask)) { + case Svc::CreateProcessFlag::AddressSpace32Bit: + case Svc::CreateProcessFlag::AddressSpace64BitDeprecated: + case Svc::CreateProcessFlag::AddressSpace64Bit: + m_max_process_memory = m_page_table.GetHeapRegionSize(); + break; + case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias: + m_max_process_memory = m_page_table.GetHeapRegionSize() + m_page_table.GetAliasRegionSize(); + break; + default: + UNREACHABLE(); + } + + // Generate random entropy. + GenerateRandom(m_entropy); // Clear remaining fields. - process->m_num_running_threads = 0; - process->m_is_signaled = false; - process->m_exception_thread = nullptr; - process->m_is_suspended = false; - process->m_schedule_count = 0; - process->m_is_handle_table_initialized = false; - process->m_is_hbl = false; + m_num_running_threads = 0; + m_num_process_switches = 0; + m_num_thread_switches = 0; + m_num_fpu_switches = 0; + m_num_supervisor_calls = 0; + m_num_ipc_messages = 0; + + m_is_signaled = false; + m_exception_thread = nullptr; + m_is_suspended = false; + m_memory_release_hint = 0; + m_schedule_count = 0; + m_is_handle_table_initialized = false; + + // Open a reference to our resource limit. + m_resource_limit = res_limit; + m_resource_limit->Open(); + + // We're initialized! + m_is_initialized = true; + + R_SUCCEED(); +} + +Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPageGroup& pg, + std::span caps, KResourceLimit* res_limit, + KMemoryManager::Pool pool, bool immortal) { + ASSERT(res_limit != nullptr); + ASSERT((params.code_num_pages * PageSize) / PageSize == + static_cast(params.code_num_pages)); + + // Set members. + m_memory_pool = pool; + m_is_default_application_system_resource = false; + m_is_immortal = immortal; + + // Setup our system resource. + if (const size_t system_resource_num_pages = params.system_resource_num_pages; + system_resource_num_pages != 0) { + // Create a secure system resource. + KSecureSystemResource* secure_resource = KSecureSystemResource::Create(m_kernel); + R_UNLESS(secure_resource != nullptr, ResultOutOfResource); + + ON_RESULT_FAILURE { + secure_resource->Close(); + }; + + // Initialize the secure resource. + R_TRY(secure_resource->Initialize(system_resource_num_pages * PageSize, res_limit, + m_memory_pool)); + + // Set our system resource. + m_system_resource = secure_resource; + } else { + // Use the system-wide system resource. + const bool is_app = True(params.flags & Svc::CreateProcessFlag::IsApplication); + m_system_resource = std::addressof(is_app ? m_kernel.GetAppSystemResource() + : m_kernel.GetSystemSystemResource()); + + m_is_default_application_system_resource = is_app; + + // Open reference to the system resource. + m_system_resource->Open(); + } + + // Ensure we clean up our secure resource, if we fail. + ON_RESULT_FAILURE { + m_system_resource->Close(); + m_system_resource = nullptr; + }; + + // Setup page table. + { + const auto as_type = params.flags & Svc::CreateProcessFlag::AddressSpaceMask; + const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); + const bool enable_das_merge = + False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); + R_TRY(m_page_table.InitializeForProcess( + as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, + params.code_num_pages * PageSize, m_system_resource, res_limit, this->GetMemory())); + } + ON_RESULT_FAILURE_2 { + m_page_table.Finalize(); + }; + + // Ensure we can insert the code region. + R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize, + KMemoryState::Code), + ResultInvalidMemoryRegion); + + // Map the code region. + R_TRY(m_page_table.MapPageGroup(params.code_address, pg, KMemoryState::Code, + KMemoryPermission::KernelRead)); + + // Initialize capabilities. + R_TRY(m_capabilities.InitializeForKip(caps, std::addressof(m_page_table))); + + // Initialize the process id. + m_process_id = m_kernel.CreateNewUserProcessID(); + ASSERT(InitialProcessIdMin <= m_process_id); + ASSERT(m_process_id <= InitialProcessIdMax); + + // Initialize the rest of the process. + R_TRY(this->Initialize(params, res_limit, true)); - // Open a reference to the resource limit. - process->m_resource_limit->Open(); + // We succeeded! + R_SUCCEED(); +} + +Result KProcess::Initialize(const Svc::CreateProcessParameter& params, + std::span user_caps, KResourceLimit* res_limit, + KMemoryManager::Pool pool) { + ASSERT(res_limit != nullptr); + + // Set members. + m_memory_pool = pool; + m_is_default_application_system_resource = false; + m_is_immortal = false; + + // Get the memory sizes. + const size_t code_num_pages = params.code_num_pages; + const size_t system_resource_num_pages = params.system_resource_num_pages; + const size_t code_size = code_num_pages * PageSize; + const size_t system_resource_size = system_resource_num_pages * PageSize; + + // Reserve memory for our code resource. + KScopedResourceReservation memory_reservation( + res_limit, Svc::LimitableResource::PhysicalMemoryMax, code_size); + R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); + + // Setup our system resource. + if (system_resource_num_pages != 0) { + // Create a secure system resource. + KSecureSystemResource* secure_resource = KSecureSystemResource::Create(m_kernel); + R_UNLESS(secure_resource != nullptr, ResultOutOfResource); + + ON_RESULT_FAILURE { + secure_resource->Close(); + }; + + // Initialize the secure resource. + R_TRY(secure_resource->Initialize(system_resource_size, res_limit, m_memory_pool)); + + // Set our system resource. + m_system_resource = secure_resource; + + } else { + // Use the system-wide system resource. + const bool is_app = True(params.flags & Svc::CreateProcessFlag::IsApplication); + m_system_resource = std::addressof(is_app ? m_kernel.GetAppSystemResource() + : m_kernel.GetSystemSystemResource()); + + m_is_default_application_system_resource = is_app; + + // Open reference to the system resource. + m_system_resource->Open(); + } + + // Ensure we clean up our secure resource, if we fail. + ON_RESULT_FAILURE { + m_system_resource->Close(); + m_system_resource = nullptr; + }; + + // Setup page table. + { + const auto as_type = params.flags & Svc::CreateProcessFlag::AddressSpaceMask; + const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); + const bool enable_das_merge = + False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); + R_TRY(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, + !enable_aslr, pool, params.code_address, code_size, + m_system_resource, res_limit, this->GetMemory())); + } + ON_RESULT_FAILURE_2 { + m_page_table.Finalize(); + }; + + // Ensure we can insert the code region. + R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code), + ResultInvalidMemoryRegion); + // Map the code region. + R_TRY(m_page_table.MapPages(params.code_address, code_num_pages, KMemoryState::Code, + KMemoryPermission::KernelRead | KMemoryPermission::NotMapped)); + + // Initialize capabilities. + R_TRY(m_capabilities.InitializeForUser(user_caps, std::addressof(m_page_table))); + + // Initialize the process id. + m_process_id = m_kernel.CreateNewUserProcessID(); + ASSERT(ProcessIdMin <= m_process_id); + ASSERT(m_process_id <= ProcessIdMax); + + // If we should optimize memory allocations, do so. + if (m_system_resource->IsSecureResource() && + True(params.flags & Svc::CreateProcessFlag::OptimizeMemoryAllocation)) { + R_TRY(m_kernel.MemoryManager().InitializeOptimizedMemory(m_process_id, pool)); + } + + // Initialize the rest of the process. + R_TRY(this->Initialize(params, res_limit, true)); + + // We succeeded, so commit our memory reservation. + memory_reservation.Commit(); R_SUCCEED(); } void KProcess::DoWorkerTaskImpl() { - UNIMPLEMENTED(); + // Terminate child threads. + TerminateChildren(m_kernel, this, nullptr); + + // Finalize the handle table, if we're not immortal. + if (!m_is_immortal && m_is_handle_table_initialized) { + this->FinalizeHandleTable(); + } + + // Finish termination. + this->FinishTermination(); +} + +Result KProcess::StartTermination() { + // Finalize the handle table when we're done, if the process isn't immortal. + SCOPE_EXIT({ + if (!m_is_immortal) { + this->FinalizeHandleTable(); + } + }); + + // Terminate child threads other than the current one. + R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel))); } -KResourceLimit* KProcess::GetResourceLimit() const { - return m_resource_limit; +void KProcess::FinishTermination() { + // Only allow termination to occur if the process isn't immortal. + if (!m_is_immortal) { + // Release resource limit hint. + if (m_resource_limit != nullptr) { + m_memory_release_hint = this->GetUsedNonSystemUserPhysicalMemorySize(); + m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, 0, + m_memory_release_hint); + } + + // Change state. + { + KScopedSchedulerLock sl(m_kernel); + this->ChangeState(State::Terminated); + } + + // Close. + this->Close(); + } +} + +void KProcess::Exit() { + // Determine whether we need to start terminating + bool needs_terminate = false; + { + KScopedLightLock lk(m_state_lock); + KScopedSchedulerLock sl(m_kernel); + + ASSERT(m_state != State::Created); + ASSERT(m_state != State::CreatedAttached); + ASSERT(m_state != State::Crashed); + ASSERT(m_state != State::Terminated); + if (m_state == State::Running || m_state == State::RunningAttached || + m_state == State::DebugBreak) { + this->ChangeState(State::Terminating); + needs_terminate = true; + } + } + + // If we need to start termination, do so. + if (needs_terminate) { + this->StartTermination(); + + // Register the process as a work task. + m_kernel.WorkerTaskManager().AddTask(m_kernel, KWorkerTaskManager::WorkerType::Exit, this); + } + + // Exit the current thread. + GetCurrentThread(m_kernel).Exit(); +} + +Result KProcess::Terminate() { + // Determine whether we need to start terminating. + bool needs_terminate = false; + { + KScopedLightLock lk(m_state_lock); + + // Check whether we're allowed to terminate. + R_UNLESS(m_state != State::Created, ResultInvalidState); + R_UNLESS(m_state != State::CreatedAttached, ResultInvalidState); + + KScopedSchedulerLock sl(m_kernel); + + if (m_state == State::Running || m_state == State::RunningAttached || + m_state == State::Crashed || m_state == State::DebugBreak) { + this->ChangeState(State::Terminating); + needs_terminate = true; + } + } + + // If we need to terminate, do so. + if (needs_terminate) { + // Start termination. + if (R_SUCCEEDED(this->StartTermination())) { + // Finish termination. + this->FinishTermination(); + } else { + // Register the process as a work task. + m_kernel.WorkerTaskManager().AddTask(m_kernel, KWorkerTaskManager::WorkerType::Exit, + this); + } + } + + R_SUCCEED(); +} + +Result KProcess::AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size) { + // Lock ourselves, to prevent concurrent access. + KScopedLightLock lk(m_state_lock); + + // Try to find an existing info for the memory. + KSharedMemoryInfo* info = nullptr; + for (auto it = m_shared_memory_list.begin(); it != m_shared_memory_list.end(); ++it) { + if (it->GetSharedMemory() == shmem) { + info = std::addressof(*it); + break; + } + } + + // If we didn't find an info, create one. + if (info == nullptr) { + // Allocate a new info. + info = KSharedMemoryInfo::Allocate(m_kernel); + R_UNLESS(info != nullptr, ResultOutOfResource); + + // Initialize the info and add it to our list. + info->Initialize(shmem); + m_shared_memory_list.push_back(*info); + } + + // Open a reference to the shared memory and its info. + shmem->Open(); + info->Open(); + + R_SUCCEED(); +} + +void KProcess::RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size) { + // Lock ourselves, to prevent concurrent access. + KScopedLightLock lk(m_state_lock); + + // Find an existing info for the memory. + KSharedMemoryInfo* info = nullptr; + auto it = m_shared_memory_list.begin(); + for (; it != m_shared_memory_list.end(); ++it) { + if (it->GetSharedMemory() == shmem) { + info = std::addressof(*it); + break; + } + } + ASSERT(info != nullptr); + + // Close a reference to the info and its memory. + if (info->Close()) { + m_shared_memory_list.erase(it); + KSharedMemoryInfo::Free(m_kernel, info); + } + + shmem->Close(); +} + +Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { + KThreadLocalPage* tlp = nullptr; + KProcessAddress tlr = 0; + + // See if we can get a region from a partially used TLP. + { + KScopedSchedulerLock sl(m_kernel); + + if (auto it = m_partially_used_tlp_tree.begin(); it != m_partially_used_tlp_tree.end()) { + tlr = it->Reserve(); + ASSERT(tlr != 0); + + if (it->IsAllUsed()) { + tlp = std::addressof(*it); + m_partially_used_tlp_tree.erase(it); + m_fully_used_tlp_tree.insert(*tlp); + } + + *out = tlr; + R_SUCCEED(); + } + } + + // Allocate a new page. + tlp = KThreadLocalPage::Allocate(m_kernel); + R_UNLESS(tlp != nullptr, ResultOutOfMemory); + ON_RESULT_FAILURE { + KThreadLocalPage::Free(m_kernel, tlp); + }; + + // Initialize the new page. + R_TRY(tlp->Initialize(m_kernel, this)); + + // Reserve a TLR. + tlr = tlp->Reserve(); + ASSERT(tlr != 0); + + // Insert into our tree. + { + KScopedSchedulerLock sl(m_kernel); + if (tlp->IsAllUsed()) { + m_fully_used_tlp_tree.insert(*tlp); + } else { + m_partially_used_tlp_tree.insert(*tlp); + } + } + + // We succeeded! + *out = tlr; + R_SUCCEED(); +} + +Result KProcess::DeleteThreadLocalRegion(KProcessAddress addr) { + KThreadLocalPage* page_to_free = nullptr; + + // Release the region. + { + KScopedSchedulerLock sl(m_kernel); + + // Try to find the page in the partially used list. + auto it = m_partially_used_tlp_tree.find_key(Common::AlignDown(GetInteger(addr), PageSize)); + if (it == m_partially_used_tlp_tree.end()) { + // If we don't find it, it has to be in the fully used list. + it = m_fully_used_tlp_tree.find_key(Common::AlignDown(GetInteger(addr), PageSize)); + R_UNLESS(it != m_fully_used_tlp_tree.end(), ResultInvalidAddress); + + // Release the region. + it->Release(addr); + + // Move the page out of the fully used list. + KThreadLocalPage* tlp = std::addressof(*it); + m_fully_used_tlp_tree.erase(it); + if (tlp->IsAllFree()) { + page_to_free = tlp; + } else { + m_partially_used_tlp_tree.insert(*tlp); + } + } else { + // Release the region. + it->Release(addr); + + // Handle the all-free case. + KThreadLocalPage* tlp = std::addressof(*it); + if (tlp->IsAllFree()) { + m_partially_used_tlp_tree.erase(it); + page_to_free = tlp; + } + } + } + + // If we should free the page it was in, do so. + if (page_to_free != nullptr) { + page_to_free->Finalize(); + + KThreadLocalPage::Free(m_kernel, page_to_free); + } + + R_SUCCEED(); +} + +bool KProcess::ReserveResource(Svc::LimitableResource which, s64 value) { + if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { + return rl->Reserve(which, value); + } else { + return true; + } +} + +bool KProcess::ReserveResource(Svc::LimitableResource which, s64 value, s64 timeout) { + if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { + return rl->Reserve(which, value, timeout); + } else { + return true; + } +} + +void KProcess::ReleaseResource(Svc::LimitableResource which, s64 value) { + if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { + rl->Release(which, value); + } +} + +void KProcess::ReleaseResource(Svc::LimitableResource which, s64 value, s64 hint) { + if (KResourceLimit* rl = this->GetResourceLimit(); rl != nullptr) { + rl->Release(which, value, hint); + } } void KProcess::IncrementRunningThreadCount() { ASSERT(m_num_running_threads.load() >= 0); + ++m_num_running_threads; } @@ -121,48 +733,71 @@ void KProcess::DecrementRunningThreadCount() { ASSERT(m_num_running_threads.load() > 0); if (const auto prev = m_num_running_threads--; prev == 1) { - // TODO(bunnei): Process termination to be implemented when multiprocess is supported. + this->Terminate(); } } -u64 KProcess::GetTotalPhysicalMemoryAvailable() { - const u64 capacity{m_resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) + - m_page_table.GetNormalMemorySize() + GetSystemResourceSize() + m_image_size + - m_main_thread_stack_size}; - if (const auto pool_size = m_kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); - capacity != pool_size) { - LOG_WARNING(Kernel, "capacity {} != application pool size {}", capacity, pool_size); - } - if (capacity < m_memory_usage_capacity) { - return capacity; +bool KProcess::EnterUserException() { + // Get the current thread. + KThread* cur_thread = GetCurrentThreadPointer(m_kernel); + ASSERT(this == cur_thread->GetOwnerProcess()); + + // Check that we haven't already claimed the exception thread. + if (m_exception_thread == cur_thread) { + return false; } - return m_memory_usage_capacity; -} -u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() { - return this->GetTotalPhysicalMemoryAvailable() - this->GetSystemResourceSize(); -} + // Create the wait queue we'll be using. + ThreadQueueImplForKProcessEnterUserException wait_queue(m_kernel, + std::addressof(m_exception_thread)); -u64 KProcess::GetTotalPhysicalMemoryUsed() { - return m_image_size + m_main_thread_stack_size + m_page_table.GetNormalMemorySize() + - this->GetSystemResourceSize(); + // Claim the exception thread. + { + // Lock the scheduler. + KScopedSchedulerLock sl(m_kernel); + + // Check that we're not terminating. + if (cur_thread->IsTerminationRequested()) { + return false; + } + + // If we don't have an exception thread, we can just claim it directly. + if (m_exception_thread == nullptr) { + m_exception_thread = cur_thread; + KScheduler::SetSchedulerUpdateNeeded(m_kernel); + return true; + } + + // Otherwise, we need to wait until we don't have an exception thread. + + // Add the current thread as a waiter on the current exception thread. + cur_thread->SetKernelAddressKey( + reinterpret_cast(std::addressof(m_exception_thread)) | 1); + m_exception_thread->AddWaiter(cur_thread); + + // Wait to claim the exception thread. + cur_thread->BeginWait(std::addressof(wait_queue)); + } + + // If our wait didn't end due to thread termination, we succeeded. + return ResultTerminationRequested != cur_thread->GetWaitResult(); } -u64 KProcess::GetTotalPhysicalMemoryUsedWithoutSystemResource() { - return this->GetTotalPhysicalMemoryUsed() - this->GetSystemResourceSize(); +bool KProcess::LeaveUserException() { + return this->ReleaseUserException(GetCurrentThreadPointer(m_kernel)); } bool KProcess::ReleaseUserException(KThread* thread) { - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl(m_kernel); if (m_exception_thread == thread) { m_exception_thread = nullptr; // Remove waiter thread. - bool has_waiters{}; + bool has_waiters; if (KThread* next = thread->RemoveKernelWaiterByKey( std::addressof(has_waiters), - reinterpret_cast(std::addressof(m_exception_thread))); + reinterpret_cast(std::addressof(m_exception_thread)) | 1); next != nullptr) { next->EndWait(ResultSuccess); } @@ -175,133 +810,185 @@ bool KProcess::ReleaseUserException(KThread* thread) { } } -void KProcess::PinCurrentThread(s32 core_id) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - - // Get the current thread. - KThread* cur_thread = - m_kernel.Scheduler(static_cast(core_id)).GetSchedulerCurrentThread(); - - // If the thread isn't terminated, pin it. - if (!cur_thread->IsTerminationRequested()) { - // Pin it. - this->PinThread(core_id, cur_thread); - cur_thread->Pin(core_id); +void KProcess::RegisterThread(KThread* thread) { + KScopedLightLock lk(m_list_lock); - // An update is needed. - KScheduler::SetSchedulerUpdateNeeded(m_kernel); - } + m_thread_list.push_back(*thread); } -void KProcess::UnpinCurrentThread(s32 core_id) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); +void KProcess::UnregisterThread(KThread* thread) { + KScopedLightLock lk(m_list_lock); - // Get the current thread. - KThread* cur_thread = - m_kernel.Scheduler(static_cast(core_id)).GetSchedulerCurrentThread(); + m_thread_list.erase(m_thread_list.iterator_to(*thread)); +} - // Unpin it. - cur_thread->Unpin(); - this->UnpinThread(core_id, cur_thread); +size_t KProcess::GetUsedUserPhysicalMemorySize() const { + const size_t norm_size = m_page_table.GetNormalMemorySize(); + const size_t other_size = m_code_size + m_main_thread_stack_size; + const size_t sec_size = this->GetRequiredSecureMemorySizeNonDefault(); - // An update is needed. - KScheduler::SetSchedulerUpdateNeeded(m_kernel); + return norm_size + other_size + sec_size; } -void KProcess::UnpinThread(KThread* thread) { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); +size_t KProcess::GetTotalUserPhysicalMemorySize() const { + // Get the amount of free and used size. + const size_t free_size = + m_resource_limit->GetFreeValue(Svc::LimitableResource::PhysicalMemoryMax); + const size_t max_size = m_max_process_memory; + + // Determine used size. + // NOTE: This does *not* check this->IsDefaultApplicationSystemResource(), unlike + // GetUsedUserPhysicalMemorySize(). + const size_t norm_size = m_page_table.GetNormalMemorySize(); + const size_t other_size = m_code_size + m_main_thread_stack_size; + const size_t sec_size = this->GetRequiredSecureMemorySize(); + const size_t used_size = norm_size + other_size + sec_size; + + // NOTE: These function calls will recalculate, introducing a race...it is unclear why Nintendo + // does it this way. + if (used_size + free_size > max_size) { + return max_size; + } else { + return free_size + this->GetUsedUserPhysicalMemorySize(); + } +} - // Get the thread's core id. - const auto core_id = thread->GetActiveCore(); +size_t KProcess::GetUsedNonSystemUserPhysicalMemorySize() const { + const size_t norm_size = m_page_table.GetNormalMemorySize(); + const size_t other_size = m_code_size + m_main_thread_stack_size; - // Unpin it. - this->UnpinThread(core_id, thread); - thread->Unpin(); + return norm_size + other_size; +} - // An update is needed. - KScheduler::SetSchedulerUpdateNeeded(m_kernel); +size_t KProcess::GetTotalNonSystemUserPhysicalMemorySize() const { + // Get the amount of free and used size. + const size_t free_size = + m_resource_limit->GetFreeValue(Svc::LimitableResource::PhysicalMemoryMax); + const size_t max_size = m_max_process_memory; + + // Determine used size. + // NOTE: This does *not* check this->IsDefaultApplicationSystemResource(), unlike + // GetUsedUserPhysicalMemorySize(). + const size_t norm_size = m_page_table.GetNormalMemorySize(); + const size_t other_size = m_code_size + m_main_thread_stack_size; + const size_t sec_size = this->GetRequiredSecureMemorySize(); + const size_t used_size = norm_size + other_size + sec_size; + + // NOTE: These function calls will recalculate, introducing a race...it is unclear why Nintendo + // does it this way. + if (used_size + free_size > max_size) { + return max_size - this->GetRequiredSecureMemorySizeNonDefault(); + } else { + return free_size + this->GetUsedNonSystemUserPhysicalMemorySize(); + } } -Result KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] KProcessAddress address, - [[maybe_unused]] size_t size) { +Result KProcess::Run(s32 priority, size_t stack_size) { // Lock ourselves, to prevent concurrent access. KScopedLightLock lk(m_state_lock); - // Try to find an existing info for the memory. - KSharedMemoryInfo* shemen_info = nullptr; - const auto iter = std::find_if( - m_shared_memory_list.begin(), m_shared_memory_list.end(), - [shmem](const KSharedMemoryInfo* info) { return info->GetSharedMemory() == shmem; }); - if (iter != m_shared_memory_list.end()) { - shemen_info = *iter; - } + // Validate that we're in a state where we can initialize. + const auto state = m_state; + R_UNLESS(state == State::Created || state == State::CreatedAttached, ResultInvalidState); - if (shemen_info == nullptr) { - shemen_info = KSharedMemoryInfo::Allocate(m_kernel); - R_UNLESS(shemen_info != nullptr, ResultOutOfMemory); + // Place a tentative reservation of a thread for this process. + KScopedResourceReservation thread_reservation(this, Svc::LimitableResource::ThreadCountMax); + R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached); - shemen_info->Initialize(shmem); - m_shared_memory_list.push_back(shemen_info); - } + // Ensure that we haven't already allocated stack. + ASSERT(m_main_thread_stack_size == 0); - // Open a reference to the shared memory and its info. - shmem->Open(); - shemen_info->Open(); + // Ensure that we're allocating a valid stack. + stack_size = Common::AlignUp(stack_size, PageSize); + R_UNLESS(stack_size + m_code_size <= m_max_process_memory, ResultOutOfMemory); + R_UNLESS(stack_size + m_code_size >= m_code_size, ResultOutOfMemory); - R_SUCCEED(); -} + // Place a tentative reservation of memory for our new stack. + KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax, + stack_size); + R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached); -void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] KProcessAddress address, - [[maybe_unused]] size_t size) { - // Lock ourselves, to prevent concurrent access. - KScopedLightLock lk(m_state_lock); + // Allocate and map our stack. + KProcessAddress stack_top = 0; + if (stack_size) { + KProcessAddress stack_bottom; + R_TRY(m_page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize, + KMemoryState::Stack, KMemoryPermission::UserReadWrite)); - KSharedMemoryInfo* shemen_info = nullptr; - const auto iter = std::find_if( - m_shared_memory_list.begin(), m_shared_memory_list.end(), - [shmem](const KSharedMemoryInfo* info) { return info->GetSharedMemory() == shmem; }); - if (iter != m_shared_memory_list.end()) { - shemen_info = *iter; + stack_top = stack_bottom + stack_size; + m_main_thread_stack_size = stack_size; } - ASSERT(shemen_info != nullptr); + // Ensure our stack is safe to clean up on exit. + ON_RESULT_FAILURE { + if (m_main_thread_stack_size) { + ASSERT(R_SUCCEEDED(m_page_table.UnmapPages(stack_top - m_main_thread_stack_size, + m_main_thread_stack_size / PageSize, + KMemoryState::Stack))); + m_main_thread_stack_size = 0; + } + }; + + // Set our maximum heap size. + R_TRY(m_page_table.SetMaxHeapSize(m_max_process_memory - + (m_main_thread_stack_size + m_code_size))); - if (shemen_info->Close()) { - m_shared_memory_list.erase(iter); - KSharedMemoryInfo::Free(m_kernel, shemen_info); - } + // Initialize our handle table. + R_TRY(this->InitializeHandleTable(m_capabilities.GetHandleTableSize())); + ON_RESULT_FAILURE_2 { + this->FinalizeHandleTable(); + }; - // Close a reference to the shared memory. - shmem->Close(); -} + // Create a new thread for the process. + KThread* main_thread = KThread::Create(m_kernel); + R_UNLESS(main_thread != nullptr, ResultOutOfResource); + SCOPE_EXIT({ main_thread->Close(); }); + + // Initialize the thread. + R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0, + stack_top, priority, m_ideal_core_id, this)); + + // Register the thread, and commit our reservation. + KThread::Register(m_kernel, main_thread); + thread_reservation.Commit(); + + // Add the thread to our handle table. + Handle thread_handle; + R_TRY(m_handle_table.Add(std::addressof(thread_handle), main_thread)); + + // Set the thread arguments. + main_thread->GetContext32().cpu_registers[0] = 0; + main_thread->GetContext64().cpu_registers[0] = 0; + main_thread->GetContext32().cpu_registers[1] = thread_handle; + main_thread->GetContext64().cpu_registers[1] = thread_handle; + + // Update our state. + this->ChangeState((state == State::Created) ? State::Running : State::RunningAttached); + ON_RESULT_FAILURE_2 { + this->ChangeState(state); + }; -void KProcess::RegisterThread(KThread* thread) { - KScopedLightLock lk{m_list_lock}; + // Suspend for debug, if we should. + if (m_kernel.System().DebuggerEnabled()) { + main_thread->RequestSuspend(SuspendType::Debug); + } - m_thread_list.push_back(thread); -} + // Run our thread. + R_TRY(main_thread->Run()); -void KProcess::UnregisterThread(KThread* thread) { - KScopedLightLock lk{m_list_lock}; + // Open a reference to represent that we're running. + this->Open(); - m_thread_list.remove(thread); -} + // We succeeded! Commit our memory reservation. + mem_reservation.Commit(); -u64 KProcess::GetFreeThreadCount() const { - if (m_resource_limit != nullptr) { - const auto current_value = - m_resource_limit->GetCurrentValue(LimitableResource::ThreadCountMax); - const auto limit_value = m_resource_limit->GetLimitValue(LimitableResource::ThreadCountMax); - return limit_value - current_value; - } else { - return 0; - } + R_SUCCEED(); } Result KProcess::Reset() { // Lock the process and the scheduler. KScopedLightLock lk(m_state_lock); - KScopedSchedulerLock sl{m_kernel}; + KScopedSchedulerLock sl(m_kernel); // Validate that we're in a state that we can reset. R_UNLESS(m_state != State::Terminated, ResultInvalidState); @@ -312,37 +999,39 @@ Result KProcess::Reset() { R_SUCCEED(); } -Result KProcess::SetActivity(ProcessActivity activity) { +Result KProcess::SetActivity(Svc::ProcessActivity activity) { // Lock ourselves and the scheduler. - KScopedLightLock lk{m_state_lock}; - KScopedLightLock list_lk{m_list_lock}; - KScopedSchedulerLock sl{m_kernel}; + KScopedLightLock lk(m_state_lock); + KScopedLightLock list_lk(m_list_lock); + KScopedSchedulerLock sl(m_kernel); // Validate our state. R_UNLESS(m_state != State::Terminating, ResultInvalidState); R_UNLESS(m_state != State::Terminated, ResultInvalidState); // Either pause or resume. - if (activity == ProcessActivity::Paused) { + if (activity == Svc::ProcessActivity::Paused) { // Verify that we're not suspended. R_UNLESS(!m_is_suspended, ResultInvalidState); // Suspend all threads. - for (auto* thread : this->GetThreadList()) { - thread->RequestSuspend(SuspendType::Process); + auto end = this->GetThreadList().end(); + for (auto it = this->GetThreadList().begin(); it != end; ++it) { + it->RequestSuspend(SuspendType::Process); } // Set ourselves as suspended. this->SetSuspended(true); } else { - ASSERT(activity == ProcessActivity::Runnable); + ASSERT(activity == Svc::ProcessActivity::Runnable); // Verify that we're suspended. R_UNLESS(m_is_suspended, ResultInvalidState); // Resume all threads. - for (auto* thread : this->GetThreadList()) { - thread->Resume(SuspendType::Process); + auto end = this->GetThreadList().end(); + for (auto it = this->GetThreadList().begin(); it != end; ++it) { + it->Resume(SuspendType::Process); } // Set ourselves as resumed. @@ -352,263 +1041,179 @@ Result KProcess::SetActivity(ProcessActivity activity) { R_SUCCEED(); } -Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, - bool is_hbl) { - m_program_id = metadata.GetTitleID(); - m_ideal_core = metadata.GetMainThreadCore(); - m_is_64bit_process = metadata.Is64BitProgram(); - m_system_resource_size = metadata.GetSystemResourceSize(); - m_image_size = code_size; - m_is_hbl = is_hbl; - - if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit) { - // For 39-bit processes, the ASLR region starts at 0x800'0000 and is ~512GiB large. - // However, some (buggy) programs/libraries like skyline incorrectly depend on the - // existence of ASLR pages before the entry point, so we will adjust the load address - // to point to about 2GiB into the ASLR region. - m_code_address = 0x8000'0000; - } else { - // All other processes can be mapped at the beginning of the code region. - if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is36Bit) { - m_code_address = 0x800'0000; - } else { - m_code_address = 0x20'0000; - } - } +void KProcess::PinCurrentThread() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - KScopedResourceReservation memory_reservation( - m_resource_limit, LimitableResource::PhysicalMemoryMax, code_size + m_system_resource_size); - if (!memory_reservation.Succeeded()) { - LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", - code_size + m_system_resource_size); - R_RETURN(ResultLimitReached); - } - // Initialize process address space - if (const Result result{m_page_table.InitializeForProcess( - metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application, - this->GetEntryPoint(), code_size, std::addressof(m_kernel.GetAppSystemResource()), - m_resource_limit, m_kernel.System().ApplicationMemory())}; - result.IsError()) { - R_RETURN(result); - } - - // Map process code region - if (const Result result{m_page_table.MapProcessCode(this->GetEntryPoint(), code_size / PageSize, - KMemoryState::Code, - KMemoryPermission::None)}; - result.IsError()) { - R_RETURN(result); - } - - // Initialize process capabilities - const auto& caps{metadata.GetKernelCapabilities()}; - if (const Result result{ - m_capabilities.InitializeForUserProcess(caps.data(), caps.size(), m_page_table)}; - result.IsError()) { - R_RETURN(result); - } - - // Set memory usage capacity - switch (metadata.GetAddressSpaceType()) { - case FileSys::ProgramAddressSpaceType::Is32Bit: - case FileSys::ProgramAddressSpaceType::Is36Bit: - case FileSys::ProgramAddressSpaceType::Is39Bit: - m_memory_usage_capacity = - m_page_table.GetHeapRegionEnd() - m_page_table.GetHeapRegionStart(); - break; + // Get the current thread. + const s32 core_id = GetCurrentCoreId(m_kernel); + KThread* cur_thread = GetCurrentThreadPointer(m_kernel); - case FileSys::ProgramAddressSpaceType::Is32BitNoMap: - m_memory_usage_capacity = - (m_page_table.GetHeapRegionEnd() - m_page_table.GetHeapRegionStart()) + - (m_page_table.GetAliasRegionEnd() - m_page_table.GetAliasRegionStart()); - break; + // If the thread isn't terminated, pin it. + if (!cur_thread->IsTerminationRequested()) { + // Pin it. + this->PinThread(core_id, cur_thread); + cur_thread->Pin(core_id); - default: - ASSERT(false); - break; + // An update is needed. + KScheduler::SetSchedulerUpdateNeeded(m_kernel); } - - // Create TLS region - R_TRY(this->CreateThreadLocalRegion(std::addressof(m_plr_address))); - memory_reservation.Commit(); - - R_RETURN(m_handle_table.Initialize(m_capabilities.GetHandleTableSize())); } -void KProcess::Run(s32 main_thread_priority, u64 stack_size) { - ASSERT(this->AllocateMainThreadStack(stack_size) == ResultSuccess); - m_resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); +void KProcess::UnpinCurrentThread() { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - const std::size_t heap_capacity{m_memory_usage_capacity - - (m_main_thread_stack_size + m_image_size)}; - ASSERT(!m_page_table.SetMaxHeapSize(heap_capacity).IsError()); + // Get the current thread. + const s32 core_id = GetCurrentCoreId(m_kernel); + KThread* cur_thread = GetCurrentThreadPointer(m_kernel); - this->ChangeState(State::Running); + // Unpin it. + cur_thread->Unpin(); + this->UnpinThread(core_id, cur_thread); - SetupMainThread(m_kernel.System(), *this, main_thread_priority, m_main_thread_stack_top); + // An update is needed. + KScheduler::SetSchedulerUpdateNeeded(m_kernel); } -void KProcess::PrepareForTermination() { - this->ChangeState(State::Terminating); +void KProcess::UnpinThread(KThread* thread) { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - const auto stop_threads = [this](const std::vector& in_thread_list) { - for (auto* thread : in_thread_list) { - if (thread->GetOwnerProcess() != this) - continue; + // Get the thread's core id. + const auto core_id = thread->GetActiveCore(); - if (thread == GetCurrentThreadPointer(m_kernel)) - continue; + // Unpin it. + this->UnpinThread(core_id, thread); + thread->Unpin(); - // TODO(Subv): When are the other running/ready threads terminated? - ASSERT_MSG(thread->GetState() == ThreadState::Waiting, - "Exiting processes with non-waiting threads is currently unimplemented"); + // An update is needed. + KScheduler::SetSchedulerUpdateNeeded(m_kernel); +} - thread->Exit(); +Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, + s32 max_out_count) { + // TODO: use current memory reference + auto& memory = m_kernel.System().ApplicationMemory(); + + // Lock the list. + KScopedLightLock lk(m_list_lock); + + // Iterate over the list. + s32 count = 0; + auto end = this->GetThreadList().end(); + for (auto it = this->GetThreadList().begin(); it != end; ++it) { + // If we're within array bounds, write the id. + if (count < max_out_count) { + // Get the thread id. + KThread* thread = std::addressof(*it); + const u64 id = thread->GetId(); + + // Copy the id to userland. + memory.Write64(out_thread_ids + count * sizeof(u64), id); } - }; - - stop_threads(m_kernel.System().GlobalSchedulerContext().GetThreadList()); - - this->DeleteThreadLocalRegion(m_plr_address); - m_plr_address = 0; - if (m_resource_limit) { - m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, - m_main_thread_stack_size + m_image_size); + // Increment the count. + ++count; } - this->ChangeState(State::Terminated); + // We successfully iterated the list. + *out_num_threads = count; + R_SUCCEED(); } -void KProcess::Finalize() { - // Free all shared memory infos. - { - auto it = m_shared_memory_list.begin(); - while (it != m_shared_memory_list.end()) { - KSharedMemoryInfo* info = *it; - KSharedMemory* shmem = info->GetSharedMemory(); - - while (!info->Close()) { - shmem->Close(); - } - - shmem->Close(); - - it = m_shared_memory_list.erase(it); - KSharedMemoryInfo::Free(m_kernel, info); - } - } +void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} - // Release memory to the resource limit. - if (m_resource_limit != nullptr) { - m_resource_limit->Close(); - m_resource_limit = nullptr; - } +KProcess::KProcess(KernelCore& kernel) + : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel.System()}, + m_state_lock{kernel}, m_list_lock{kernel}, m_cond_var{kernel.System()}, + m_address_arbiter{kernel.System()}, m_handle_table{kernel} {} +KProcess::~KProcess() = default; - // Finalize the page table. - m_page_table.Finalize(); +Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, + bool is_hbl) { + // Create a resource limit for the process. + const auto physical_memory_size = + m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); + auto* res_limit = + Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size); - // Perform inherited finalization. - KSynchronizationObject::Finalize(); -} + // Ensure we maintain a clean state on exit. + SCOPE_EXIT({ res_limit->Close(); }); -Result KProcess::CreateThreadLocalRegion(KProcessAddress* out) { - KThreadLocalPage* tlp = nullptr; - KProcessAddress tlr = 0; + // Declare flags and code address. + Svc::CreateProcessFlag flag{}; + u64 code_address{}; - // See if we can get a region from a partially used TLP. - { - KScopedSchedulerLock sl{m_kernel}; + // We are an application. + flag |= Svc::CreateProcessFlag::IsApplication; - if (auto it = m_partially_used_tlp_tree.begin(); it != m_partially_used_tlp_tree.end()) { - tlr = it->Reserve(); - ASSERT(tlr != 0); + // If we are 64-bit, create as such. + if (metadata.Is64BitProgram()) { + flag |= Svc::CreateProcessFlag::Is64Bit; + } - if (it->IsAllUsed()) { - tlp = std::addressof(*it); - m_partially_used_tlp_tree.erase(it); - m_fully_used_tlp_tree.insert(*tlp); - } + // Set the address space type and code address. + switch (metadata.GetAddressSpaceType()) { + case FileSys::ProgramAddressSpaceType::Is39Bit: + flag |= Svc::CreateProcessFlag::AddressSpace64Bit; - *out = tlr; - R_SUCCEED(); - } + // For 39-bit processes, the ASLR region starts at 0x800'0000 and is ~512GiB large. + // However, some (buggy) programs/libraries like skyline incorrectly depend on the + // existence of ASLR pages before the entry point, so we will adjust the load address + // to point to about 2GiB into the ASLR region. + code_address = 0x8000'0000; + break; + case FileSys::ProgramAddressSpaceType::Is36Bit: + flag |= Svc::CreateProcessFlag::AddressSpace64BitDeprecated; + code_address = 0x800'0000; + break; + case FileSys::ProgramAddressSpaceType::Is32Bit: + flag |= Svc::CreateProcessFlag::AddressSpace32Bit; + code_address = 0x20'0000; + break; + case FileSys::ProgramAddressSpaceType::Is32BitNoMap: + flag |= Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias; + code_address = 0x20'0000; + break; } - // Allocate a new page. - tlp = KThreadLocalPage::Allocate(m_kernel); - R_UNLESS(tlp != nullptr, ResultOutOfMemory); - auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(m_kernel, tlp); }); + Svc::CreateProcessParameter params{ + .name = {}, + .version = {}, + .program_id = metadata.GetTitleID(), + .code_address = code_address, + .code_num_pages = static_cast(code_size / PageSize), + .flags = flag, + .reslimit = Svc::InvalidHandle, + .system_resource_num_pages = static_cast(metadata.GetSystemResourceSize() / PageSize), + }; - // Initialize the new page. - R_TRY(tlp->Initialize(m_kernel, this)); + // Set the process name. + const auto& name = metadata.GetName(); + static_assert(sizeof(params.name) <= sizeof(name)); + std::memcpy(params.name.data(), name.data(), sizeof(params.name)); - // Reserve a TLR. - tlr = tlp->Reserve(); - ASSERT(tlr != 0); + // Initialize for application process. + R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, + KMemoryManager::Pool::Application)); - // Insert into our tree. - { - KScopedSchedulerLock sl{m_kernel}; - if (tlp->IsAllUsed()) { - m_fully_used_tlp_tree.insert(*tlp); - } else { - m_partially_used_tlp_tree.insert(*tlp); - } - } + // Assign remaining properties. + m_is_hbl = is_hbl; + m_ideal_core_id = metadata.GetMainThreadCore(); - // We succeeded! - tlp_guard.Cancel(); - *out = tlr; + // We succeeded. R_SUCCEED(); } -Result KProcess::DeleteThreadLocalRegion(KProcessAddress addr) { - KThreadLocalPage* page_to_free = nullptr; - - // Release the region. - { - KScopedSchedulerLock sl{m_kernel}; - - // Try to find the page in the partially used list. - auto it = m_partially_used_tlp_tree.find_key(Common::AlignDown(GetInteger(addr), PageSize)); - if (it == m_partially_used_tlp_tree.end()) { - // If we don't find it, it has to be in the fully used list. - it = m_fully_used_tlp_tree.find_key(Common::AlignDown(GetInteger(addr), PageSize)); - R_UNLESS(it != m_fully_used_tlp_tree.end(), ResultInvalidAddress); - - // Release the region. - it->Release(addr); - - // Move the page out of the fully used list. - KThreadLocalPage* tlp = std::addressof(*it); - m_fully_used_tlp_tree.erase(it); - if (tlp->IsAllFree()) { - page_to_free = tlp; - } else { - m_partially_used_tlp_tree.insert(*tlp); - } - } else { - // Release the region. - it->Release(addr); - - // Handle the all-free case. - KThreadLocalPage* tlp = std::addressof(*it); - if (tlp->IsAllFree()) { - m_partially_used_tlp_tree.erase(it); - page_to_free = tlp; - } - } - } - - // If we should free the page it was in, do so. - if (page_to_free != nullptr) { - page_to_free->Finalize(); +void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { + const auto ReprotectSegment = [&](const CodeSet::Segment& segment, + Svc::MemoryPermission permission) { + m_page_table.SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); + }; - KThreadLocalPage::Free(m_kernel, page_to_free); - } + this->GetMemory().WriteBlock(base_addr, code_set.memory.data(), code_set.memory.size()); - R_SUCCEED(); + ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute); + ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read); + ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); } bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { @@ -657,71 +1262,6 @@ bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT return true; } -void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { - const auto ReprotectSegment = [&](const CodeSet::Segment& segment, - Svc::MemoryPermission permission) { - m_page_table.SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); - }; - - this->GetMemory().WriteBlock(base_addr, code_set.memory.data(), code_set.memory.size()); - - ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute); - ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read); - ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); -} - -bool KProcess::IsSignaled() const { - ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); - return m_is_signaled; -} - -KProcess::KProcess(KernelCore& kernel) - : KAutoObjectWithSlabHeapAndContainer{kernel}, m_page_table{m_kernel.System()}, - m_handle_table{m_kernel}, m_address_arbiter{m_kernel.System()}, - m_condition_var{m_kernel.System()}, m_state_lock{m_kernel}, m_list_lock{m_kernel} {} - -KProcess::~KProcess() = default; - -void KProcess::ChangeState(State new_state) { - if (m_state == new_state) { - return; - } - - m_state = new_state; - m_is_signaled = true; - this->NotifyAvailable(); -} - -Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { - // Ensure that we haven't already allocated stack. - ASSERT(m_main_thread_stack_size == 0); - - // Ensure that we're allocating a valid stack. - stack_size = Common::AlignUp(stack_size, PageSize); - // R_UNLESS(stack_size + image_size <= m_max_process_memory, ResultOutOfMemory); - R_UNLESS(stack_size + m_image_size >= m_image_size, ResultOutOfMemory); - - // Place a tentative reservation of memory for our new stack. - KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax, - stack_size); - R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached); - - // Allocate and map our stack. - if (stack_size) { - KProcessAddress stack_bottom; - R_TRY(m_page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize, - KMemoryState::Stack, KMemoryPermission::UserReadWrite)); - - m_main_thread_stack_top = stack_bottom + stack_size; - m_main_thread_stack_size = stack_size; - } - - // We succeeded! Commit our memory reservation. - mem_reservation.Commit(); - - R_SUCCEED(); -} - Core::Memory::Memory& KProcess::GetMemory() const { // TODO: per-process memory return m_kernel.System().ApplicationMemory(); diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 146e07a57..f9f755afa 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -1,59 +1,23 @@ -// SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include -#include -#include #include -#include + +#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_address_arbiter.h" -#include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/k_capabilities.h" #include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_page_table.h" -#include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/k_page_table_manager.h" +#include "core/hle/kernel/k_system_resource.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread_local_page.h" -#include "core/hle/kernel/k_typed_address.h" -#include "core/hle/kernel/k_worker_task.h" -#include "core/hle/kernel/process_capability.h" -#include "core/hle/kernel/slab_helpers.h" -#include "core/hle/result.h" - -namespace Core { -namespace Memory { -class Memory; -}; - -class System; -} // namespace Core - -namespace FileSys { -class ProgramMetadata; -} namespace Kernel { -class KernelCore; -class KResourceLimit; -class KThread; -class KSharedMemoryInfo; -class TLSPage; - -struct CodeSet; - -enum class MemoryRegion : u16 { - APPLICATION = 1, - SYSTEM = 2, - BASE = 3, -}; - -enum class ProcessActivity : u32 { - Runnable, - Paused, -}; - enum class DebugWatchpointType : u8 { None = 0, Read = 1 << 0, @@ -72,9 +36,6 @@ class KProcess final : public KAutoObjectWithSlabHeapAndContainer(Svc::ProcessState::Created), CreatedAttached = static_cast(Svc::ProcessState::CreatedAttached), @@ -86,470 +47,493 @@ public: DebugBreak = static_cast(Svc::ProcessState::DebugBreak), }; - enum : u64 { - /// Lowest allowed process ID for a kernel initial process. - InitialKIPIDMin = 1, - /// Highest allowed process ID for a kernel initial process. - InitialKIPIDMax = 80, - - /// Lowest allowed process ID for a userland process. - ProcessIDMin = 81, - /// Highest allowed process ID for a userland process. - ProcessIDMax = 0xFFFFFFFFFFFFFFFF, - }; + using ThreadList = Common::IntrusiveListMemberTraits<&KThread::m_process_list_node>::ListType; - // Used to determine how process IDs are assigned. - enum class ProcessType { - KernelInternal, - Userland, - }; + static constexpr size_t AslrAlignment = 2_MiB; - static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; +public: + static constexpr u64 InitialProcessIdMin = 1; + static constexpr u64 InitialProcessIdMax = 0x50; - static Result Initialize(KProcess* process, Core::System& system, std::string process_name, - ProcessType type, KResourceLimit* res_limit); + static constexpr u64 ProcessIdMin = InitialProcessIdMax + 1; + static constexpr u64 ProcessIdMax = std::numeric_limits::max(); - /// Gets a reference to the process' page table. - KPageTable& GetPageTable() { - return m_page_table; - } +private: + using SharedMemoryInfoList = Common::IntrusiveListBaseTraits::ListType; + using TLPTree = + Common::IntrusiveRedBlackTreeBaseTraits::TreeType; + using TLPIterator = TLPTree::iterator; - /// Gets const a reference to the process' page table. - const KPageTable& GetPageTable() const { - return m_page_table; - } +private: + KPageTable m_page_table; + std::atomic m_used_kernel_memory_size{}; + TLPTree m_fully_used_tlp_tree{}; + TLPTree m_partially_used_tlp_tree{}; + s32 m_ideal_core_id{}; + KResourceLimit* m_resource_limit{}; + KSystemResource* m_system_resource{}; + size_t m_memory_release_hint{}; + State m_state{}; + KLightLock m_state_lock; + KLightLock m_list_lock; + KConditionVariable m_cond_var; + KAddressArbiter m_address_arbiter; + std::array m_entropy{}; + bool m_is_signaled{}; + bool m_is_initialized{}; + bool m_is_application{}; + bool m_is_default_application_system_resource{}; + bool m_is_hbl{}; + std::array m_name{}; + std::atomic m_num_running_threads{}; + Svc::CreateProcessFlag m_flags{}; + KMemoryManager::Pool m_memory_pool{}; + s64 m_schedule_count{}; + KCapabilities m_capabilities{}; + u64 m_program_id{}; + u64 m_process_id{}; + KProcessAddress m_code_address{}; + size_t m_code_size{}; + size_t m_main_thread_stack_size{}; + size_t m_max_process_memory{}; + u32 m_version{}; + KHandleTable m_handle_table; + KProcessAddress m_plr_address{}; + KThread* m_exception_thread{}; + ThreadList m_thread_list{}; + SharedMemoryInfoList m_shared_memory_list{}; + bool m_is_suspended{}; + bool m_is_immortal{}; + bool m_is_handle_table_initialized{}; + std::array m_running_threads{}; + std::array m_running_thread_idle_counts{}; + std::array m_running_thread_switch_counts{}; + std::array m_pinned_threads{}; + std::array m_watchpoints{}; + std::map m_debug_page_refcounts{}; + std::atomic m_cpu_time{}; + std::atomic m_num_process_switches{}; + std::atomic m_num_thread_switches{}; + std::atomic m_num_fpu_switches{}; + std::atomic m_num_supervisor_calls{}; + std::atomic m_num_ipc_messages{}; + std::atomic m_num_ipc_replies{}; + std::atomic m_num_ipc_receives{}; - /// Gets a reference to the process' handle table. - KHandleTable& GetHandleTable() { - return m_handle_table; - } +private: + Result StartTermination(); + void FinishTermination(); - /// Gets a const reference to the process' handle table. - const KHandleTable& GetHandleTable() const { - return m_handle_table; + void PinThread(s32 core_id, KThread* thread) { + ASSERT(0 <= core_id && core_id < static_cast(Core::Hardware::NUM_CPU_CORES)); + ASSERT(thread != nullptr); + ASSERT(m_pinned_threads[core_id] == nullptr); + m_pinned_threads[core_id] = thread; } - /// Gets a reference to process's memory. - Core::Memory::Memory& GetMemory() const; - - Result SignalToAddress(KProcessAddress address) { - return m_condition_var.SignalToAddress(address); + void UnpinThread(s32 core_id, KThread* thread) { + ASSERT(0 <= core_id && core_id < static_cast(Core::Hardware::NUM_CPU_CORES)); + ASSERT(thread != nullptr); + ASSERT(m_pinned_threads[core_id] == thread); + m_pinned_threads[core_id] = nullptr; } - Result WaitForAddress(Handle handle, KProcessAddress address, u32 tag) { - return m_condition_var.WaitForAddress(handle, address, tag); - } +public: + explicit KProcess(KernelCore& kernel); + ~KProcess() override; - void SignalConditionVariable(u64 cv_key, int32_t count) { - return m_condition_var.Signal(cv_key, count); - } + Result Initialize(const Svc::CreateProcessParameter& params, KResourceLimit* res_limit, + bool is_real); - Result WaitConditionVariable(KProcessAddress address, u64 cv_key, u32 tag, s64 ns) { - R_RETURN(m_condition_var.Wait(address, cv_key, tag, ns)); - } + Result Initialize(const Svc::CreateProcessParameter& params, const KPageGroup& pg, + std::span caps, KResourceLimit* res_limit, + KMemoryManager::Pool pool, bool immortal); + Result Initialize(const Svc::CreateProcessParameter& params, std::span user_caps, + KResourceLimit* res_limit, KMemoryManager::Pool pool); + void Exit(); - Result SignalAddressArbiter(uint64_t address, Svc::SignalType signal_type, s32 value, - s32 count) { - R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count)); + const char* GetName() const { + return m_name.data(); } - Result WaitAddressArbiter(uint64_t address, Svc::ArbitrationType arb_type, s32 value, - s64 timeout) { - R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout)); + u64 GetProgramId() const { + return m_program_id; } - KProcessAddress GetProcessLocalRegionAddress() const { - return m_plr_address; + u64 GetProcessId() const { + return m_process_id; } - /// Gets the current status of the process State GetState() const { return m_state; } - /// Gets the unique ID that identifies this particular process. - u64 GetProcessId() const { - return m_process_id; + u64 GetCoreMask() const { + return m_capabilities.GetCoreMask(); + } + u64 GetPhysicalCoreMask() const { + return m_capabilities.GetPhysicalCoreMask(); + } + u64 GetPriorityMask() const { + return m_capabilities.GetPriorityMask(); } - /// Gets the program ID corresponding to this process. - u64 GetProgramId() const { - return m_program_id; + s32 GetIdealCoreId() const { + return m_ideal_core_id; + } + void SetIdealCoreId(s32 core_id) { + m_ideal_core_id = core_id; } - KProcessAddress GetEntryPoint() const { - return m_code_address; + bool CheckThreadPriority(s32 prio) const { + return ((1ULL << prio) & this->GetPriorityMask()) != 0; } - /// Gets the resource limit descriptor for this process - KResourceLimit* GetResourceLimit() const; + u32 GetCreateProcessFlags() const { + return static_cast(m_flags); + } - /// Gets the ideal CPU core ID for this process - u8 GetIdealCoreId() const { - return m_ideal_core; + bool Is64Bit() const { + return True(m_flags & Svc::CreateProcessFlag::Is64Bit); } - /// Checks if the specified thread priority is valid. - bool CheckThreadPriority(s32 prio) const { - return ((1ULL << prio) & GetPriorityMask()) != 0; + KProcessAddress GetEntryPoint() const { + return m_code_address; } - /// Gets the bitmask of allowed cores that this process' threads can run on. - u64 GetCoreMask() const { - return m_capabilities.GetCoreMask(); + size_t GetMainStackSize() const { + return m_main_thread_stack_size; } - /// Gets the bitmask of allowed thread priorities. - u64 GetPriorityMask() const { - return m_capabilities.GetPriorityMask(); + KMemoryManager::Pool GetMemoryPool() const { + return m_memory_pool; } - /// Gets the amount of secure memory to allocate for memory management. - u32 GetSystemResourceSize() const { - return m_system_resource_size; + u64 GetRandomEntropy(size_t i) const { + return m_entropy[i]; } - /// Gets the amount of secure memory currently in use for memory management. - u32 GetSystemResourceUsage() const { - // On hardware, this returns the amount of system resource memory that has - // been used by the kernel. This is problematic for Yuzu to emulate, because - // system resource memory is used for page tables -- and yuzu doesn't really - // have a way to calculate how much memory is required for page tables for - // the current process at any given time. - // TODO: Is this even worth implementing? Games may retrieve this value via - // an SDK function that gets used + available system resource size for debug - // or diagnostic purposes. However, it seems unlikely that a game would make - // decisions based on how much system memory is dedicated to its page tables. - // Is returning a value other than zero wise? - return 0; + bool IsApplication() const { + return m_is_application; } - /// Whether this process is an AArch64 or AArch32 process. - bool Is64BitProcess() const { - return m_is_64bit_process; + bool IsDefaultApplicationSystemResource() const { + return m_is_default_application_system_resource; } bool IsSuspended() const { return m_is_suspended; } - void SetSuspended(bool suspended) { m_is_suspended = suspended; } - /// Gets the total running time of the process instance in ticks. - u64 GetCPUTimeTicks() const { - return m_total_process_running_time_ticks; + Result Terminate(); + + bool IsTerminated() const { + return m_state == State::Terminated; } - /// Updates the total running time, adding the given ticks to it. - void UpdateCPUTimeTicks(u64 ticks) { - m_total_process_running_time_ticks += ticks; + bool IsPermittedSvc(u32 svc_id) const { + return m_capabilities.IsPermittedSvc(svc_id); } - /// Gets the process schedule count, used for thread yielding - s64 GetScheduledCount() const { - return m_schedule_count; + bool IsPermittedInterrupt(s32 interrupt_id) const { + return m_capabilities.IsPermittedInterrupt(interrupt_id); } - /// Increments the process schedule count, used for thread yielding. - void IncrementScheduledCount() { - ++m_schedule_count; + bool IsPermittedDebug() const { + return m_capabilities.IsPermittedDebug(); } - void IncrementRunningThreadCount(); - void DecrementRunningThreadCount(); + bool CanForceDebug() const { + return m_capabilities.CanForceDebug(); + } - void SetRunningThread(s32 core, KThread* thread, u64 idle_count) { - m_running_threads[core] = thread; - m_running_thread_idle_counts[core] = idle_count; + bool IsHbl() const { + return m_is_hbl; } - void ClearRunningThread(KThread* thread) { - for (size_t i = 0; i < m_running_threads.size(); ++i) { - if (m_running_threads[i] == thread) { - m_running_threads[i] = nullptr; - } - } + Kernel::KMemoryManager::Direction GetAllocateOption() const { + // TODO: property of the KPageTableBase + return KMemoryManager::Direction::FromFront; } - [[nodiscard]] KThread* GetRunningThread(s32 core) const { - return m_running_threads[core]; + ThreadList& GetThreadList() { + return m_thread_list; + } + const ThreadList& GetThreadList() const { + return m_thread_list; } + bool EnterUserException(); + bool LeaveUserException(); bool ReleaseUserException(KThread* thread); - [[nodiscard]] KThread* GetPinnedThread(s32 core_id) const { + KThread* GetPinnedThread(s32 core_id) const { ASSERT(0 <= core_id && core_id < static_cast(Core::Hardware::NUM_CPU_CORES)); return m_pinned_threads[core_id]; } - /// Gets 8 bytes of random data for svcGetInfo RandomEntropy - u64 GetRandomEntropy(std::size_t index) const { - return m_random_entropy.at(index); + const Svc::SvcAccessFlagSet& GetSvcPermissions() const { + return m_capabilities.GetSvcPermissions(); } - /// Retrieves the total physical memory available to this process in bytes. - u64 GetTotalPhysicalMemoryAvailable(); - - /// Retrieves the total physical memory available to this process in bytes, - /// without the size of the personal system resource heap added to it. - u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource(); - - /// Retrieves the total physical memory used by this process in bytes. - u64 GetTotalPhysicalMemoryUsed(); - - /// Retrieves the total physical memory used by this process in bytes, - /// without the size of the personal system resource heap added to it. - u64 GetTotalPhysicalMemoryUsedWithoutSystemResource(); - - /// Gets the list of all threads created with this process as their owner. - std::list& GetThreadList() { - return m_thread_list; + KResourceLimit* GetResourceLimit() const { + return m_resource_limit; } - /// Registers a thread as being created under this process, - /// adding it to this process' thread list. - void RegisterThread(KThread* thread); + bool ReserveResource(Svc::LimitableResource which, s64 value); + bool ReserveResource(Svc::LimitableResource which, s64 value, s64 timeout); + void ReleaseResource(Svc::LimitableResource which, s64 value); + void ReleaseResource(Svc::LimitableResource which, s64 value, s64 hint); - /// Unregisters a thread from this process, removing it - /// from this process' thread list. - void UnregisterThread(KThread* thread); + KLightLock& GetStateLock() { + return m_state_lock; + } + KLightLock& GetListLock() { + return m_list_lock; + } - /// Retrieves the number of available threads for this process. - u64 GetFreeThreadCount() const; - - /// Clears the signaled state of the process if and only if it's signaled. - /// - /// @pre The process must not be already terminated. If this is called on a - /// terminated process, then ResultInvalidState will be returned. - /// - /// @pre The process must be in a signaled state. If this is called on a - /// process instance that is not signaled, ResultInvalidState will be - /// returned. - Result Reset(); + KPageTable& GetPageTable() { + return m_page_table; + } + const KPageTable& GetPageTable() const { + return m_page_table; + } - /** - * Loads process-specifics configuration info with metadata provided - * by an executable. - * - * @param metadata The provided metadata to load process specific info from. - * - * @returns ResultSuccess if all relevant metadata was able to be - * loaded and parsed. Otherwise, an error code is returned. - */ - Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, - bool is_hbl); + KHandleTable& GetHandleTable() { + return m_handle_table; + } + const KHandleTable& GetHandleTable() const { + return m_handle_table; + } - /** - * Starts the main application thread for this process. - * - * @param main_thread_priority The priority for the main thread. - * @param stack_size The stack size for the main thread in bytes. - */ - void Run(s32 main_thread_priority, u64 stack_size); + size_t GetUsedUserPhysicalMemorySize() const; + size_t GetTotalUserPhysicalMemorySize() const; + size_t GetUsedNonSystemUserPhysicalMemorySize() const; + size_t GetTotalNonSystemUserPhysicalMemorySize() const; - /** - * Prepares a process for termination by stopping all of its threads - * and clearing any other resources. - */ - void PrepareForTermination(); + Result AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size); + void RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size); - void LoadModule(CodeSet code_set, KProcessAddress base_addr); + Result CreateThreadLocalRegion(KProcessAddress* out); + Result DeleteThreadLocalRegion(KProcessAddress addr); - bool IsInitialized() const override { - return m_is_initialized; + KProcessAddress GetProcessLocalRegionAddress() const { + return m_plr_address; } - static void PostDestroy(uintptr_t arg) {} - - void Finalize() override; - - u64 GetId() const override { - return GetProcessId(); + KThread* GetExceptionThread() const { + return m_exception_thread; } - bool IsHbl() const { - return m_is_hbl; + void AddCpuTime(s64 diff) { + m_cpu_time += diff; + } + s64 GetCpuTime() { + return m_cpu_time.load(); } - bool IsSignaled() const override; - - void DoWorkerTaskImpl(); + s64 GetScheduledCount() const { + return m_schedule_count; + } + void IncrementScheduledCount() { + ++m_schedule_count; + } - Result SetActivity(ProcessActivity activity); + void IncrementRunningThreadCount(); + void DecrementRunningThreadCount(); - void PinCurrentThread(s32 core_id); - void UnpinCurrentThread(s32 core_id); - void UnpinThread(KThread* thread); + size_t GetRequiredSecureMemorySizeNonDefault() const { + if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) { + auto* secure_system_resource = static_cast(m_system_resource); + return secure_system_resource->CalculateRequiredSecureMemorySize(); + } - KLightLock& GetStateLock() { - return m_state_lock; + return 0; } - Result AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size); - void RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size); - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Thread-local storage management - - // Marks the next available region as used and returns the address of the slot. - [[nodiscard]] Result CreateThreadLocalRegion(KProcessAddress* out); + size_t GetRequiredSecureMemorySize() const { + if (m_system_resource->IsSecureResource()) { + auto* secure_system_resource = static_cast(m_system_resource); + return secure_system_resource->CalculateRequiredSecureMemorySize(); + } - // Frees a used TLS slot identified by the given address - Result DeleteThreadLocalRegion(KProcessAddress addr); + return 0; + } - /////////////////////////////////////////////////////////////////////////////////////////////// - // Debug watchpoint management + size_t GetTotalSystemResourceSize() const { + if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) { + auto* secure_system_resource = static_cast(m_system_resource); + return secure_system_resource->GetSize(); + } - // Attempts to insert a watchpoint into a free slot. Returns false if none are available. - bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); + return 0; + } - // Attempts to remove the watchpoint specified by the given parameters. - bool RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); + size_t GetUsedSystemResourceSize() const { + if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) { + auto* secure_system_resource = static_cast(m_system_resource); + return secure_system_resource->GetUsedSize(); + } - const std::array& GetWatchpoints() const { - return m_watchpoints; + return 0; } - const std::string& GetName() { - return name; + void SetRunningThread(s32 core, KThread* thread, u64 idle_count, u64 switch_count) { + m_running_threads[core] = thread; + m_running_thread_idle_counts[core] = idle_count; + m_running_thread_switch_counts[core] = switch_count; } -private: - void PinThread(s32 core_id, KThread* thread) { - ASSERT(0 <= core_id && core_id < static_cast(Core::Hardware::NUM_CPU_CORES)); - ASSERT(thread != nullptr); - ASSERT(m_pinned_threads[core_id] == nullptr); - m_pinned_threads[core_id] = thread; + void ClearRunningThread(KThread* thread) { + for (size_t i = 0; i < m_running_threads.size(); ++i) { + if (m_running_threads[i] == thread) { + m_running_threads[i] = nullptr; + } + } } - void UnpinThread(s32 core_id, KThread* thread) { - ASSERT(0 <= core_id && core_id < static_cast(Core::Hardware::NUM_CPU_CORES)); - ASSERT(thread != nullptr); - ASSERT(m_pinned_threads[core_id] == thread); - m_pinned_threads[core_id] = nullptr; + const KSystemResource& GetSystemResource() const { + return *m_system_resource; } - void FinalizeHandleTable() { - // Finalize the table. - m_handle_table.Finalize(); - - // Note that the table is finalized. - m_is_handle_table_initialized = false; + const KMemoryBlockSlabManager& GetMemoryBlockSlabManager() const { + return m_system_resource->GetMemoryBlockSlabManager(); + } + const KBlockInfoManager& GetBlockInfoManager() const { + return m_system_resource->GetBlockInfoManager(); + } + const KPageTableManager& GetPageTableManager() const { + return m_system_resource->GetPageTableManager(); } - void ChangeState(State new_state); - - /// Allocates the main thread stack for the process, given the stack size in bytes. - Result AllocateMainThreadStack(std::size_t stack_size); - - /// Memory manager for this process - KPageTable m_page_table; - - /// Current status of the process - State m_state{}; + KThread* GetRunningThread(s32 core) const { + return m_running_threads[core]; + } + u64 GetRunningThreadIdleCount(s32 core) const { + return m_running_thread_idle_counts[core]; + } + u64 GetRunningThreadSwitchCount(s32 core) const { + return m_running_thread_switch_counts[core]; + } - /// The ID of this process - u64 m_process_id = 0; + void RegisterThread(KThread* thread); + void UnregisterThread(KThread* thread); - /// Title ID corresponding to the process - u64 m_program_id = 0; + Result Run(s32 priority, size_t stack_size); - /// Specifies additional memory to be reserved for the process's memory management by the - /// system. When this is non-zero, secure memory is allocated and used for page table allocation - /// instead of using the normal global page tables/memory block management. - u32 m_system_resource_size = 0; + Result Reset(); - /// Resource limit descriptor for this process - KResourceLimit* m_resource_limit{}; + void SetDebugBreak() { + if (m_state == State::RunningAttached) { + this->ChangeState(State::DebugBreak); + } + } - KVirtualAddress m_system_resource_address{}; + void SetAttached() { + if (m_state == State::DebugBreak) { + this->ChangeState(State::RunningAttached); + } + } - /// The ideal CPU core for this process, threads are scheduled on this core by default. - u8 m_ideal_core = 0; + Result SetActivity(Svc::ProcessActivity activity); - /// Contains the parsed process capability descriptors. - ProcessCapabilities m_capabilities; + void PinCurrentThread(); + void UnpinCurrentThread(); + void UnpinThread(KThread* thread); - /// Whether or not this process is AArch64, or AArch32. - /// By default, we currently assume this is true, unless otherwise - /// specified by metadata provided to the process during loading. - bool m_is_64bit_process = true; + void SignalConditionVariable(uintptr_t cv_key, int32_t count) { + return m_cond_var.Signal(cv_key, count); + } - /// Total running time for the process in ticks. - std::atomic m_total_process_running_time_ticks = 0; + Result WaitConditionVariable(KProcessAddress address, uintptr_t cv_key, u32 tag, s64 ns) { + R_RETURN(m_cond_var.Wait(address, cv_key, tag, ns)); + } - /// Per-process handle table for storing created object handles in. - KHandleTable m_handle_table; + Result SignalAddressArbiter(uintptr_t address, Svc::SignalType signal_type, s32 value, + s32 count) { + R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count)); + } - /// Per-process address arbiter. - KAddressArbiter m_address_arbiter; + Result WaitAddressArbiter(uintptr_t address, Svc::ArbitrationType arb_type, s32 value, + s64 timeout) { + R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout)); + } - /// The per-process mutex lock instance used for handling various - /// forms of services, such as lock arbitration, and condition - /// variable related facilities. - KConditionVariable m_condition_var; + Result GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, s32 max_out_count); - /// Address indicating the location of the process' dedicated TLS region. - KProcessAddress m_plr_address = 0; + static void Switch(KProcess* cur_process, KProcess* next_process); - /// Address indicating the location of the process's entry point. - KProcessAddress m_code_address = 0; +public: + // Attempts to insert a watchpoint into a free slot. Returns false if none are available. + bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); - /// Random values for svcGetInfo RandomEntropy - std::array m_random_entropy{}; + // Attempts to remove the watchpoint specified by the given parameters. + bool RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); - /// List of threads that are running with this process as their owner. - std::list m_thread_list; + const std::array& GetWatchpoints() const { + return m_watchpoints; + } - /// List of shared memory that are running with this process as their owner. - std::list m_shared_memory_list; +public: + Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, + bool is_hbl); - /// Address of the top of the main thread's stack - KProcessAddress m_main_thread_stack_top{}; + void LoadModule(CodeSet code_set, KProcessAddress base_addr); - /// Size of the main thread's stack - std::size_t m_main_thread_stack_size{}; + Core::Memory::Memory& GetMemory() const; - /// Memory usage capacity for the process - std::size_t m_memory_usage_capacity{}; +public: + // Overridden parent functions. + bool IsInitialized() const override { + return m_is_initialized; + } - /// Process total image size - std::size_t m_image_size{}; + static void PostDestroy(uintptr_t arg) {} - /// Schedule count of this process - s64 m_schedule_count{}; + void Finalize() override; - size_t m_memory_release_hint{}; + u64 GetIdImpl() const { + return this->GetProcessId(); + } + u64 GetId() const override { + return this->GetIdImpl(); + } - std::string name{}; + virtual bool IsSignaled() const override { + ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); + return m_is_signaled; + } - bool m_is_signaled{}; - bool m_is_suspended{}; - bool m_is_immortal{}; - bool m_is_handle_table_initialized{}; - bool m_is_initialized{}; - bool m_is_hbl{}; + void DoWorkerTaskImpl(); - std::atomic m_num_running_threads{}; +private: + void ChangeState(State new_state) { + if (m_state != new_state) { + m_state = new_state; + m_is_signaled = true; + this->NotifyAvailable(); + } + } - std::array m_running_threads{}; - std::array m_running_thread_idle_counts{}; - std::array m_pinned_threads{}; - std::array m_watchpoints{}; - std::map m_debug_page_refcounts; + Result InitializeHandleTable(s32 size) { + // Try to initialize the handle table. + R_TRY(m_handle_table.Initialize(size)); - KThread* m_exception_thread{}; + // We succeeded, so note that we did. + m_is_handle_table_initialized = true; + R_SUCCEED(); + } - KLightLock m_state_lock; - KLightLock m_list_lock; + void FinalizeHandleTable() { + // Finalize the table. + m_handle_table.Finalize(); - using TLPTree = - Common::IntrusiveRedBlackTreeBaseTraits::TreeType; - using TLPIterator = TLPTree::iterator; - TLPTree m_fully_used_tlp_tree; - TLPTree m_partially_used_tlp_tree; + // Note that the table is finalized. + m_is_handle_table_initialized = false; + } }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index d8143c650..1bce63a56 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -190,7 +190,7 @@ u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) { if (m_state.should_count_idle) { if (highest_thread != nullptr) [[likely]] { if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) { - process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count); + process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count, 0); } } else { m_state.idle_count++; @@ -356,7 +356,7 @@ void KScheduler::SwitchThread(KThread* next_thread) { const s64 tick_diff = cur_tick - prev_tick; cur_thread->AddCpuTime(m_core_id, tick_diff); if (cur_process != nullptr) { - cur_process->UpdateCPUTimeTicks(tick_diff); + cur_process->AddCpuTime(tick_diff); } m_last_context_switch_time = cur_tick; diff --git a/src/core/hle/kernel/k_system_resource.cpp b/src/core/hle/kernel/k_system_resource.cpp index e6c8d589a..07e92aa80 100644 --- a/src/core/hle/kernel/k_system_resource.cpp +++ b/src/core/hle/kernel/k_system_resource.cpp @@ -1,25 +1,100 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/core.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_system_resource.h" namespace Kernel { Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_limit, KMemoryManager::Pool pool) { - // Unimplemented - UNREACHABLE(); + // Set members. + m_resource_limit = resource_limit; + m_resource_size = size; + m_resource_pool = pool; + + // Determine required size for our secure resource. + const size_t secure_size = this->CalculateRequiredSecureMemorySize(); + + // Reserve memory for our secure resource. + KScopedResourceReservation memory_reservation( + m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, secure_size); + R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); + + // Allocate secure memory. + R_TRY(KSystemControl::AllocateSecureMemory(m_kernel, std::addressof(m_resource_address), + m_resource_size, static_cast(m_resource_pool))); + ASSERT(m_resource_address != 0); + + // Ensure we clean up the secure memory, if we fail past this point. + ON_RESULT_FAILURE { + KSystemControl::FreeSecureMemory(m_kernel, m_resource_address, m_resource_size, + static_cast(m_resource_pool)); + }; + + // Check that our allocation is bigger than the reference counts needed for it. + const size_t rc_size = + Common::AlignUp(KPageTableSlabHeap::CalculateReferenceCountSize(m_resource_size), PageSize); + R_UNLESS(m_resource_size > rc_size, ResultOutOfMemory); + + // Get resource pointer. + KPhysicalAddress resource_paddr = + KPageTable::GetHeapPhysicalAddress(m_kernel.MemoryLayout(), m_resource_address); + auto* resource = + m_kernel.System().DeviceMemory().GetPointer(resource_paddr); + + // Initialize slab heaps. + m_dynamic_page_manager.Initialize(m_resource_address + rc_size, m_resource_size - rc_size, + PageSize); + m_page_table_heap.Initialize(std::addressof(m_dynamic_page_manager), 0, resource); + m_memory_block_heap.Initialize(std::addressof(m_dynamic_page_manager), 0); + m_block_info_heap.Initialize(std::addressof(m_dynamic_page_manager), 0); + + // Initialize managers. + m_page_table_manager.Initialize(std::addressof(m_dynamic_page_manager), + std::addressof(m_page_table_heap)); + m_memory_block_slab_manager.Initialize(std::addressof(m_dynamic_page_manager), + std::addressof(m_memory_block_heap)); + m_block_info_manager.Initialize(std::addressof(m_dynamic_page_manager), + std::addressof(m_block_info_heap)); + + // Set our managers. + this->SetManagers(m_memory_block_slab_manager, m_block_info_manager, m_page_table_manager); + + // Commit the memory reservation. + memory_reservation.Commit(); + + // Open reference to our resource limit. + m_resource_limit->Open(); + + // Set ourselves as initialized. + m_is_initialized = true; + + R_SUCCEED(); } void KSecureSystemResource::Finalize() { - // Unimplemented - UNREACHABLE(); + // Check that we have no outstanding allocations. + ASSERT(m_memory_block_slab_manager.GetUsed() == 0); + ASSERT(m_block_info_manager.GetUsed() == 0); + ASSERT(m_page_table_manager.GetUsed() == 0); + + // Free our secure memory. + KSystemControl::FreeSecureMemory(m_kernel, m_resource_address, m_resource_size, + static_cast(m_resource_pool)); + + // Release the memory reservation. + m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, + this->CalculateRequiredSecureMemorySize()); + + // Close reference to our resource limit. + m_resource_limit->Close(); } size_t KSecureSystemResource::CalculateRequiredSecureMemorySize(size_t size, KMemoryManager::Pool pool) { - // Unimplemented - UNREACHABLE(); + return KSystemControl::CalculateRequiredSecureMemorySize(size, static_cast(pool)); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 7df8fd7f7..a882be403 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -122,16 +122,15 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress case ThreadType::Main: ASSERT(arg == 0); [[fallthrough]]; - case ThreadType::HighPriority: - [[fallthrough]]; - case ThreadType::Dummy: - [[fallthrough]]; case ThreadType::User: ASSERT(((owner == nullptr) || (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); ASSERT(((owner == nullptr) || (prio > Svc::LowestThreadPriority) || (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask())); break; + case ThreadType::HighPriority: + case ThreadType::Dummy: + break; case ThreadType::Kernel: UNIMPLEMENTED(); break; @@ -403,7 +402,7 @@ void KThread::StartTermination() { if (m_parent != nullptr) { m_parent->ReleaseUserException(this); if (m_parent->GetPinnedThread(GetCurrentCoreId(m_kernel)) == this) { - m_parent->UnpinCurrentThread(m_core_id); + m_parent->UnpinCurrentThread(); } } @@ -820,7 +819,7 @@ void KThread::CloneFpuStatus() { ASSERT(this->GetOwnerProcess() != nullptr); ASSERT(this->GetOwnerProcess() == GetCurrentProcessPointer(m_kernel)); - if (this->GetOwnerProcess()->Is64BitProcess()) { + if (this->GetOwnerProcess()->Is64Bit()) { // Clone FPSR and FPCR. ThreadContext64 cur_ctx{}; m_kernel.System().CurrentArmInterface().SaveContext(cur_ctx); @@ -923,7 +922,7 @@ Result KThread::GetThreadContext3(Common::ScratchBuffer& out) { // If we're not terminating, get the thread's user context. if (!this->IsTerminationRequested()) { - if (m_parent->Is64BitProcess()) { + if (m_parent->Is64Bit()) { // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. auto context = GetContext64(); context.pstate &= 0xFF0FFE20; @@ -1174,6 +1173,9 @@ Result KThread::Run() { owner->IncrementRunningThreadCount(); } + // Open a reference, now that we're running. + this->Open(); + // Set our state and finish. this->SetState(ThreadState::Runnable); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index d178c2453..e1f80b04f 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -721,6 +721,7 @@ private: // For core KThread implementation ThreadContext32 m_thread_context_32{}; ThreadContext64 m_thread_context_64{}; + Common::IntrusiveListNode m_process_list_node; Common::IntrusiveRedBlackTreeNode m_condvar_arbiter_tree_node{}; s32 m_priority{}; using ConditionVariableThreadTreeTraits = diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 24433d32b..ac76c71a8 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -101,35 +101,31 @@ struct KernelCore::Impl { void InitializeCores() { for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - cores[core_id]->Initialize((*application_process).Is64BitProcess()); + cores[core_id]->Initialize((*application_process).Is64Bit()); system.ApplicationMemory().SetCurrentPageTable(*application_process, core_id); } } - void CloseApplicationProcess() { - KProcess* old_process = application_process.exchange(nullptr); - if (old_process == nullptr) { - return; - } - - // old_process->Close(); - // TODO: The process should be destroyed based on accurate ref counting after - // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. - old_process->Finalize(); - old_process->Destroy(); + void TerminateApplicationProcess() { + application_process.load()->Terminate(); } void Shutdown() { is_shutting_down.store(true, std::memory_order_relaxed); SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); }); - process_list.clear(); - CloseServices(); + auto* old_process = application_process.exchange(nullptr); + if (old_process) { + old_process->Close(); + } + + process_list.clear(); + next_object_id = 0; - next_kernel_process_id = KProcess::InitialKIPIDMin; - next_user_process_id = KProcess::ProcessIDMin; + next_kernel_process_id = KProcess::InitialProcessIdMin; + next_user_process_id = KProcess::ProcessIdMin; next_thread_id = 1; global_handle_table->Finalize(); @@ -176,8 +172,6 @@ struct KernelCore::Impl { } } - CloseApplicationProcess(); - // Track kernel objects that were not freed on shutdown { std::scoped_lock lk{registered_objects_lock}; @@ -344,6 +338,8 @@ struct KernelCore::Impl { // Create the system page table managers. app_system_resource = std::make_unique(kernel); sys_system_resource = std::make_unique(kernel); + KAutoObject::Create(std::addressof(*app_system_resource)); + KAutoObject::Create(std::addressof(*sys_system_resource)); // Set the managers for the system resources. app_system_resource->SetManagers(*app_memory_block_manager, *app_block_info_manager, @@ -368,6 +364,7 @@ struct KernelCore::Impl { void MakeApplicationProcess(KProcess* process) { application_process = process; + application_process.load()->Open(); } static inline thread_local u8 host_thread_id = UINT8_MAX; @@ -792,8 +789,8 @@ struct KernelCore::Impl { std::mutex registered_in_use_objects_lock; std::atomic next_object_id{0}; - std::atomic next_kernel_process_id{KProcess::InitialKIPIDMin}; - std::atomic next_user_process_id{KProcess::ProcessIDMin}; + std::atomic next_kernel_process_id{KProcess::InitialProcessIdMin}; + std::atomic next_user_process_id{KProcess::ProcessIdMin}; std::atomic next_thread_id{1}; // Lists all processes that exist in the current session. @@ -924,10 +921,6 @@ const KProcess* KernelCore::ApplicationProcess() const { return impl->application_process; } -void KernelCore::CloseApplicationProcess() { - impl->CloseApplicationProcess(); -} - const std::vector& KernelCore::GetProcessList() const { return impl->process_list; } @@ -1128,8 +1121,8 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name, std::function func) { // Make a new process. KProcess* process = KProcess::Create(*this); - ASSERT(R_SUCCEEDED(KProcess::Initialize(process, System(), "", KProcess::ProcessType::Userland, - GetSystemResourceLimit()))); + ASSERT(R_SUCCEEDED( + process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); // Ensure that we don't hold onto any extra references. SCOPE_EXIT({ process->Close(); }); @@ -1156,8 +1149,8 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function // Make a new process. KProcess* process = KProcess::Create(*this); - ASSERT(R_SUCCEEDED(KProcess::Initialize(process, System(), "", KProcess::ProcessType::Userland, - GetSystemResourceLimit()))); + ASSERT(R_SUCCEEDED( + process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); // Ensure that we don't hold onto any extra references. SCOPE_EXIT({ process->Close(); }); @@ -1266,7 +1259,8 @@ const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const { void KernelCore::SuspendApplication(bool suspended) { const bool should_suspend{exception_exited || suspended}; - const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; + const auto activity = + should_suspend ? Svc::ProcessActivity::Paused : Svc::ProcessActivity::Runnable; // Get the application process. KScopedAutoObject process = ApplicationProcess(); @@ -1300,6 +1294,8 @@ void KernelCore::SuspendApplication(bool suspended) { } void KernelCore::ShutdownCores() { + impl->TerminateApplicationProcess(); + KScopedSchedulerLock lk{*this}; for (auto* thread : impl->shutdown_threads) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index d5b08eeb5..d8086c0ea 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -134,9 +134,6 @@ public: /// Retrieves a const pointer to the application process. const KProcess* ApplicationProcess() const; - /// Closes the application process. - void CloseApplicationProcess(); - /// Retrieves the list of processes. const std::vector& GetProcessList() const; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 871d541d4..b76683969 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -4426,7 +4426,7 @@ void Call(Core::System& system, u32 imm) { auto& kernel = system.Kernel(); kernel.EnterSVCProfile(); - if (GetCurrentProcess(system.Kernel()).Is64BitProcess()) { + if (GetCurrentProcess(system.Kernel()).Is64Bit()) { Call64(system, imm); } else { Call32(system, imm); diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp index f99964028..ada998772 100644 --- a/src/core/hle/kernel/svc/svc_info.cpp +++ b/src/core/hle/kernel/svc/svc_info.cpp @@ -86,20 +86,19 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle R_SUCCEED(); case InfoType::TotalMemorySize: - *result = process->GetTotalPhysicalMemoryAvailable(); + *result = process->GetTotalUserPhysicalMemorySize(); R_SUCCEED(); case InfoType::UsedMemorySize: - *result = process->GetTotalPhysicalMemoryUsed(); + *result = process->GetUsedUserPhysicalMemorySize(); R_SUCCEED(); case InfoType::SystemResourceSizeTotal: - *result = process->GetSystemResourceSize(); + *result = process->GetTotalSystemResourceSize(); R_SUCCEED(); case InfoType::SystemResourceSizeUsed: - LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); - *result = process->GetSystemResourceUsage(); + *result = process->GetUsedSystemResourceSize(); R_SUCCEED(); case InfoType::ProgramId: @@ -111,20 +110,29 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle R_SUCCEED(); case InfoType::TotalNonSystemMemorySize: - *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); + *result = process->GetTotalNonSystemUserPhysicalMemorySize(); R_SUCCEED(); case InfoType::UsedNonSystemMemorySize: - *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); + *result = process->GetUsedNonSystemUserPhysicalMemorySize(); R_SUCCEED(); case InfoType::IsApplication: LOG_WARNING(Kernel_SVC, "(STUBBED) Assuming process is application"); - *result = true; + *result = process->IsApplication(); R_SUCCEED(); case InfoType::FreeThreadCount: - *result = process->GetFreeThreadCount(); + if (KResourceLimit* resource_limit = process->GetResourceLimit(); + resource_limit != nullptr) { + const auto current_value = + resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax); + const auto limit_value = + resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax); + *result = limit_value - current_value; + } else { + *result = 0; + } R_SUCCEED(); default: @@ -161,7 +169,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle case InfoType::RandomEntropy: R_UNLESS(handle == 0, ResultInvalidHandle); - R_UNLESS(info_sub_id < KProcess::RANDOM_ENTROPY_SIZE, ResultInvalidCombination); + R_UNLESS(info_sub_id < 4, ResultInvalidCombination); *result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id); R_SUCCEED(); diff --git a/src/core/hle/kernel/svc/svc_lock.cpp b/src/core/hle/kernel/svc/svc_lock.cpp index 1d7bc4246..5f0833fcb 100644 --- a/src/core/hle/kernel/svc/svc_lock.cpp +++ b/src/core/hle/kernel/svc/svc_lock.cpp @@ -17,7 +17,7 @@ Result ArbitrateLock(Core::System& system, Handle thread_handle, u64 address, u3 R_UNLESS(!IsKernelAddress(address), ResultInvalidCurrentMemory); R_UNLESS(Common::IsAligned(address, sizeof(u32)), ResultInvalidAddress); - R_RETURN(GetCurrentProcess(system.Kernel()).WaitForAddress(thread_handle, address, tag)); + R_RETURN(KConditionVariable::WaitForAddress(system.Kernel(), thread_handle, address, tag)); } /// Unlock a mutex @@ -28,7 +28,7 @@ Result ArbitrateUnlock(Core::System& system, u64 address) { R_UNLESS(!IsKernelAddress(address), ResultInvalidCurrentMemory); R_UNLESS(Common::IsAligned(address, sizeof(u32)), ResultInvalidAddress); - R_RETURN(GetCurrentProcess(system.Kernel()).SignalToAddress(address)); + R_RETURN(KConditionVariable::SignalToAddress(system.Kernel(), address)); } Result ArbitrateLock64(Core::System& system, Handle thread_handle, uint64_t address, uint32_t tag) { diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp index d3545f232..99330d02a 100644 --- a/src/core/hle/kernel/svc/svc_physical_memory.cpp +++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp @@ -46,7 +46,7 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) { KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())}; auto& page_table{current_process->GetPageTable()}; - if (current_process->GetSystemResourceSize() == 0) { + if (current_process->GetTotalSystemResourceSize() == 0) { LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); R_THROW(ResultInvalidState); } @@ -95,7 +95,7 @@ Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) { KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())}; auto& page_table{current_process->GetPageTable()}; - if (current_process->GetSystemResourceSize() == 0) { + if (current_process->GetTotalSystemResourceSize() == 0) { LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); R_THROW(ResultInvalidState); } diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp index 8ebc1bd1c..6c79cfd8d 100644 --- a/src/core/hle/kernel/svc/svc_synchronization.cpp +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp @@ -132,7 +132,7 @@ void SynchronizePreemptionState(Core::System& system) { GetCurrentThread(kernel).ClearInterruptFlag(); // Unpin the current thread. - cur_process->UnpinCurrentThread(core_id); + cur_process->UnpinCurrentThread(); } } diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp index 933b82e30..755fd62b5 100644 --- a/src/core/hle/kernel/svc/svc_thread.cpp +++ b/src/core/hle/kernel/svc/svc_thread.cpp @@ -85,10 +85,6 @@ Result StartThread(Core::System& system, Handle thread_handle) { // Try to start the thread. R_TRY(thread->Run()); - // If we succeeded, persist a reference to the thread. - thread->Open(); - system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe()); - R_SUCCEED(); } @@ -99,7 +95,6 @@ void ExitThread(Core::System& system) { auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); system.GlobalSchedulerContext().RemoveThread(current_thread); current_thread->Exit(); - system.Kernel().UnregisterInUseObject(current_thread); } /// Sleep the current thread @@ -260,7 +255,7 @@ Result GetThreadList(Core::System& system, s32* out_num_threads, u64 out_thread_ auto list_iter = thread_list.cbegin(); for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { - memory.Write64(out_thread_ids, (*list_iter)->GetThreadId()); + memory.Write64(out_thread_ids, list_iter->GetThreadId()); out_thread_ids += sizeof(u64); } diff --git a/src/core/hle/kernel/svc_generator.py b/src/core/hle/kernel/svc_generator.py index 7fcbb1ba1..5531faac6 100644 --- a/src/core/hle/kernel/svc_generator.py +++ b/src/core/hle/kernel/svc_generator.py @@ -592,7 +592,7 @@ void Call(Core::System& system, u32 imm) { auto& kernel = system.Kernel(); kernel.EnterSVCProfile(); - if (GetCurrentProcess(system.Kernel()).Is64BitProcess()) { + if (GetCurrentProcess(system.Kernel()).Is64Bit()) { Call64(system, imm); } else { Call32(system, imm); diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 251e6013c..50de02e36 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -604,13 +604,57 @@ enum class ProcessActivity : u32 { Paused, }; +enum class CreateProcessFlag : u32 { + // Is 64 bit? + Is64Bit = (1 << 0), + + // What kind of address space? + AddressSpaceShift = 1, + AddressSpaceMask = (7 << AddressSpaceShift), + AddressSpace32Bit = (0 << AddressSpaceShift), + AddressSpace64BitDeprecated = (1 << AddressSpaceShift), + AddressSpace32BitWithoutAlias = (2 << AddressSpaceShift), + AddressSpace64Bit = (3 << AddressSpaceShift), + + // Should JIT debug be done on crash? + EnableDebug = (1 << 4), + + // Should ASLR be enabled for the process? + EnableAslr = (1 << 5), + + // Is the process an application? + IsApplication = (1 << 6), + + // 4.x deprecated: Should use secure memory? + DeprecatedUseSecureMemory = (1 << 7), + + // 5.x+ Pool partition type. + PoolPartitionShift = 7, + PoolPartitionMask = (0xF << PoolPartitionShift), + PoolPartitionApplication = (0 << PoolPartitionShift), + PoolPartitionApplet = (1 << PoolPartitionShift), + PoolPartitionSystem = (2 << PoolPartitionShift), + PoolPartitionSystemNonSecure = (3 << PoolPartitionShift), + + // 7.x+ Should memory allocation be optimized? This requires IsApplication. + OptimizeMemoryAllocation = (1 << 11), + + // 11.x+ DisableDeviceAddressSpaceMerge. + DisableDeviceAddressSpaceMerge = (1 << 12), + + // Mask of all flags. + All = Is64Bit | AddressSpaceMask | EnableDebug | EnableAslr | IsApplication | + PoolPartitionMask | OptimizeMemoryAllocation | DisableDeviceAddressSpaceMerge, +}; +DECLARE_ENUM_FLAG_OPERATORS(CreateProcessFlag); + struct CreateProcessParameter { std::array name; u32 version; u64 program_id; u64 code_address; s32 code_num_pages; - u32 flags; + CreateProcessFlag flags; Handle reslimit; s32 system_resource_num_pages; }; diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index 6a313a03b..f51e63564 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -21,10 +21,8 @@ ServiceContext::ServiceContext(Core::System& system_, std::string name_) // Create the process. process = Kernel::KProcess::Create(kernel); - ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), - Kernel::KProcess::ProcessType::KernelInternal, - kernel.GetSystemResourceLimit()) - .IsSuccess()); + ASSERT(R_SUCCEEDED(process->Initialize(Kernel::Svc::CreateProcessParameter{}, + kernel.GetSystemResourceLimit(), false))); // Register the process. Kernel::KProcess::Register(kernel, process); diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index a07c621d9..bebb45eae 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -66,7 +66,6 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_ "ScreenComposition", [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) -> std::optional { - { const auto lock_guard = Lock(); } vsync_signal.Set(); return std::chrono::nanoseconds(GetNextTicks()); }); @@ -99,6 +98,7 @@ Nvnflinger::~Nvnflinger() { } ShutdownLayers(); + vsync_thread = {}; if (nvdrv) { nvdrv->Close(disp_fd); @@ -106,6 +106,7 @@ Nvnflinger::~Nvnflinger() { } void Nvnflinger::ShutdownLayers() { + const auto lock_guard = Lock(); for (auto& display : displays) { for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { display.GetLayer(layer).Core().NotifyShutdown(); @@ -229,16 +230,6 @@ VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) { return display->FindLayer(layer_id); } -const VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) const { - const auto* const display = FindDisplay(display_id); - - if (display == nullptr) { - return nullptr; - } - - return display->FindLayer(layer_id); -} - VI::Layer* Nvnflinger::FindOrCreateLayer(u64 display_id, u64 layer_id) { auto* const display = FindDisplay(display_id); @@ -288,7 +279,6 @@ void Nvnflinger::Compose() { auto nvdisp = nvdrv->GetDevice(disp_fd); ASSERT(nvdisp); - guard->unlock(); Common::Rectangle crop_rect{ static_cast(buffer.crop.Left()), static_cast(buffer.crop.Top()), static_cast(buffer.crop.Right()), static_cast(buffer.crop.Bottom())}; @@ -299,7 +289,6 @@ void Nvnflinger::Compose() { buffer.fence.fences, buffer.fence.num_fences); MicroProfileFlip(); - guard->lock(); swap_interval = buffer.swap_interval; diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index 14c783582..959d8b46b 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -117,9 +117,6 @@ private: /// Finds the layer identified by the specified ID in the desired display. [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); - /// Finds the layer identified by the specified ID in the desired display. - [[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; - /// Finds the layer identified by the specified ID in the desired display, /// or creates the layer if it is not found. /// To be used when the system expects the specified ID to already exist. diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index f9cf2dda3..d92499f05 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -37,7 +37,7 @@ std::optional SearchProcessList( void GetApplicationPidGeneric(HLERequestContext& ctx, const std::vector& process_list) { const auto process = SearchProcessList(process_list, [](const auto& proc) { - return proc->GetProcessId() == Kernel::KProcess::ProcessIDMin; + return proc->GetProcessId() == Kernel::KProcess::ProcessIdMin; }); IPC::ResponseBuilder rb{ctx, 4}; diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index ed875d444..5d168cbc1 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -116,7 +116,7 @@ json GetProcessorStateDataAuto(Core::System& system) { Core::ARM_Interface::ThreadContext64 context{}; arm.SaveContext(context); - return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32", + return GetProcessorStateData(process->Is64Bit() ? "AArch64" : "AArch32", GetInteger(process->GetEntryPoint()), context.sp, context.pc, context.pstate, context.cpu_registers); } diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 0783a2430..7049c57b6 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -127,7 +127,7 @@ std::vector> WaitTreeCallstack::GetChildren() cons return list; } - if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64BitProcess()) { + if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64Bit()) { return list; } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 1431cf2fe..816d804c4 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2019,7 +2019,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())} .filename()); } - const bool is_64bit = system->Kernel().ApplicationProcess()->Is64BitProcess(); + const bool is_64bit = system->Kernel().ApplicationProcess()->Is64Bit(); const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)"); title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit") .arg(QString::fromStdString(title_name), instruction_set_suffix) -- cgit v1.2.3 From bb195c2c2bebdb62d349cf181479489a1a15b108 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 21 Oct 2023 18:46:19 -0400 Subject: kernel: add missing TLR clear --- src/core/hle/kernel/k_thread.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index a882be403..ac0f215d7 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -215,6 +215,7 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress // Setup the TLS, if needed. if (type == ThreadType::User) { R_TRY(owner->CreateThreadLocalRegion(std::addressof(m_tls_address))); + owner->GetMemory().ZeroBlock(m_tls_address, Svc::ThreadLocalRegionSize); } m_parent = owner; -- cgit v1.2.3 From dcfe674ed4eef3879f0ed8706507ea21d13aab24 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 21 Oct 2023 19:18:38 -0400 Subject: kernel: signal thread on termination completed --- src/core/hle/kernel/k_thread.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index ac0f215d7..a6deb50ec 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -415,10 +415,6 @@ void KThread::StartTermination() { m_parent->ClearRunningThread(this); } - // Signal. - m_signaled = true; - KSynchronizationObject::NotifyAvailable(); - // Clear previous thread in KScheduler. KScheduler::ClearPreviousThread(m_kernel, this); @@ -437,6 +433,13 @@ void KThread::FinishTermination() { } } + // Acquire the scheduler lock. + KScopedSchedulerLock sl{m_kernel}; + + // Signal. + m_signaled = true; + KSynchronizationObject::NotifyAvailable(); + // Close the thread. this->Close(); } -- cgit v1.2.3 From 5f8f09d750a7edb4f97aa037ee202fda353fd078 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 21 Oct 2023 20:35:18 -0400 Subject: kernel: shutdown app before gpu --- src/core/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/core.cpp b/src/core/core.cpp index 296727ed7..934177b85 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -410,6 +410,7 @@ struct System::Impl { services->KillNVNFlinger(); } kernel.CloseServices(); + kernel.ShutdownCores(); services.reset(); service_manager.reset(); cheat_engine.reset(); @@ -421,7 +422,6 @@ struct System::Impl { gpu_core.reset(); host1x_core.reset(); perf_stats.reset(); - kernel.ShutdownCores(); cpu_manager.Shutdown(); debugger.reset(); kernel.Shutdown(); -- cgit v1.2.3 From 31bffc729997a5ad8176d62805e342603331742e Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 21 Oct 2023 22:16:41 -0400 Subject: kernel: fix extraneous ref --- src/core/hle/kernel/kernel.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index ac76c71a8..4a1559291 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -364,7 +364,6 @@ struct KernelCore::Impl { void MakeApplicationProcess(KProcess* process) { application_process = process; - application_process.load()->Open(); } static inline thread_local u8 host_thread_id = UINT8_MAX; -- cgit v1.2.3 From e4dfd513378432c4343159b09b3129f608daa597 Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 22 Oct 2023 10:14:08 -0600 Subject: input_common: joycon: Move vibrations to a queue --- src/input_common/helpers/joycon_driver.cpp | 16 ++++++++++++++-- src/input_common/helpers/joycon_driver.h | 5 +++++ 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index cf51f3481..c9f903213 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -139,7 +139,7 @@ void JoyconDriver::InputThread(std::stop_token stop_token) { input_thread_running = true; // Max update rate is 5ms, ensure we are always able to read a bit faster - constexpr int ThreadDelay = 2; + constexpr int ThreadDelay = 3; std::vector buffer(MaxBufferSize); while (!stop_token.stop_requested()) { @@ -163,6 +163,17 @@ void JoyconDriver::InputThread(std::stop_token stop_token) { OnNewData(buffer); } + if (!vibration_queue.Empty()) { + VibrationValue vibration_value; + vibration_queue.Pop(vibration_value); + last_vibration_result = rumble_protocol->SendVibration(vibration_value); + } + + // We can't keep up with vibrations. Start skipping. + while (vibration_queue.Size() > 6) { + vibration_queue.Pop(); + } + std::this_thread::yield(); } @@ -402,7 +413,8 @@ Common::Input::DriverResult JoyconDriver::SetVibration(const VibrationValue& vib if (disable_input_thread) { return Common::Input::DriverResult::HandleInUse; } - return rumble_protocol->SendVibration(vibration); + vibration_queue.Push(vibration); + return last_vibration_result; } Common::Input::DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index 335e12cc3..5355780fb 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -9,6 +9,7 @@ #include #include +#include "common/threadsafe_queue.h" #include "input_common/helpers/joycon_protocol/joycon_types.h" namespace Common::Input { @@ -152,6 +153,10 @@ private: SerialNumber handle_serial_number{}; // Serial number type reported by hidapi SupportedFeatures supported_features{}; + /// Queue of vibration request to controllers + Common::Input::DriverResult last_vibration_result{Common::Input::DriverResult::Success}; + Common::SPSCQueue vibration_queue; + // Thread related mutable std::mutex mutex; std::jthread input_thread; -- cgit v1.2.3 From 0604b142637e9308b6d85872fbd2d45fda978553 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 23 Oct 2023 09:08:57 -0400 Subject: Manually robust on Pascal and earlier --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 804b95989..22bf8cc77 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -358,7 +358,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device .has_broken_spirv_subgroup_mask_vector_extract_dynamic = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, .has_broken_robust = - device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Maxwell, + device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, }; host_info = Shader::HostTranslateInfo{ -- cgit v1.2.3 From 68f25217b879dc53721c8ae8686505f58fd1c630 Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Mon, 23 Oct 2023 15:08:56 +0100 Subject: Add missing dowhile loops around FindBuffer calls --- src/video_core/buffer_cache/buffer_cache.h | 13 +++++++------ src/video_core/renderer_vulkan/vk_rasterizer.cpp | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 9b2698fad..081a574e8 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1067,8 +1067,7 @@ void BufferCache

::BindHostComputeTextureBuffers() { template void BufferCache

::DoUpdateGraphicsBuffers(bool is_indexed) { - do { - channel_state->has_deleted_buffers = false; + BufferOperations([&]() { if (is_indexed) { UpdateIndexBuffer(); } @@ -1082,14 +1081,16 @@ void BufferCache

::DoUpdateGraphicsBuffers(bool is_indexed) { if (current_draw_indirect) { UpdateDrawIndirect(); } - } while (channel_state->has_deleted_buffers); + }); } template void BufferCache

::DoUpdateComputeBuffers() { - UpdateComputeUniformBuffers(); - UpdateComputeStorageBuffers(); - UpdateComputeTextureBuffers(); + BufferOperations([&]() { + UpdateComputeUniformBuffers(); + UpdateComputeStorageBuffers(); + UpdateComputeTextureBuffers(); + }); } template diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 465eac37e..059b7cb40 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -13,6 +13,7 @@ #include "common/microprofile.h" #include "common/scope_exit.h" #include "common/settings.h" +#include "video_core/buffer_cache/buffer_cache.h" #include "video_core/control/channel_state.h" #include "video_core/engines/draw_manager.h" #include "video_core/engines/kepler_compute.h" @@ -285,6 +286,7 @@ void RasterizerVulkan::DrawTexture() { query_cache.NotifySegment(true); + std::scoped_lock l{texture_cache.mutex}; texture_cache.SynchronizeGraphicsDescriptors(); texture_cache.UpdateRenderTargets(false); -- cgit v1.2.3 From 79894152a8b2270f928644c37ef26f33eb44272e Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 23 Oct 2023 22:09:29 -0400 Subject: qt: fix game list shutdown crash --- src/yuzu/game_list.cpp | 26 +++++------ src/yuzu/game_list.h | 10 +++-- src/yuzu/game_list_worker.cpp | 102 +++++++++++++++++++++++++++++++----------- src/yuzu/game_list_worker.h | 35 ++++++++------- 4 files changed, 112 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 2bb1a0239..7e7d8e252 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -380,7 +380,6 @@ void GameList::UnloadController() { GameList::~GameList() { UnloadController(); - emit ShouldCancelWorker(); } void GameList::SetFilterFocus() { @@ -397,6 +396,10 @@ void GameList::ClearFilter() { search_field->clear(); } +void GameList::WorkerEvent() { + current_worker->ProcessEvents(this); +} + void GameList::AddDirEntry(GameListDir* entry_items) { item_model->invisibleRootItem()->appendRow(entry_items); tree_view->setExpanded( @@ -826,28 +829,21 @@ void GameList::PopulateAsync(QVector& game_dirs) { tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size); tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time); - // Before deleting rows, cancel the worker so that it is not using them - emit ShouldCancelWorker(); + // Cancel any existing worker. + current_worker.reset(); // Delete any rows that might already exist if we're repopulating item_model->removeRows(0, item_model->rowCount()); search_field->clear(); - GameListWorker* worker = - new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system); + current_worker = std::make_unique(vfs, provider, game_dirs, compatibility_list, + play_time_manager, system); - connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); - connect(worker, &GameListWorker::DirEntryReady, this, &GameList::AddDirEntry, - Qt::QueuedConnection); - connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, + // Get events from the worker as data becomes available + connect(current_worker.get(), &GameListWorker::DataAvailable, this, &GameList::WorkerEvent, Qt::QueuedConnection); - // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to - // cancel without delay. - connect(this, &GameList::ShouldCancelWorker, worker, &GameListWorker::Cancel, - Qt::DirectConnection); - QThreadPool::globalInstance()->start(worker); - current_worker = std::move(worker); + QThreadPool::globalInstance()->start(current_worker.get()); } void GameList::SaveInterfaceLayout() { diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 712570cea..563a3a35b 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -109,7 +109,6 @@ signals: void BootGame(const QString& game_path, u64 program_id, std::size_t program_index, StartGameType type, AmLaunchType launch_type); void GameChosen(const QString& game_path, const u64 title_id = 0); - void ShouldCancelWorker(); void OpenFolderRequested(u64 program_id, GameListOpenTarget target, const std::string& game_path); void OpenTransferableShaderCacheRequested(u64 program_id); @@ -138,11 +137,16 @@ private slots: void OnUpdateThemedIcons(); private: + friend class GameListWorker; + void WorkerEvent(); + void AddDirEntry(GameListDir* entry_items); void AddEntry(const QList& entry_items, GameListDir* parent); - void ValidateEntry(const QModelIndex& item); void DonePopulating(const QStringList& watch_list); +private: + void ValidateEntry(const QModelIndex& item); + void RefreshGameDirectory(); void ToggleFavorite(u64 program_id); @@ -165,7 +169,7 @@ private: QVBoxLayout* layout = nullptr; QTreeView* tree_view = nullptr; QStandardItemModel* item_model = nullptr; - GameListWorker* current_worker = nullptr; + std::unique_ptr current_worker; QFileSystemWatcher* watcher = nullptr; ControllerNavigation* controller_navigation = nullptr; CompatibilityList compatibility_list; diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 077ced12b..69be21027 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -233,10 +233,53 @@ GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs_, const PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_) : vfs{std::move(vfs_)}, provider{provider_}, game_dirs{game_dirs_}, - compatibility_list{compatibility_list_}, - play_time_manager{play_time_manager_}, system{system_} {} + compatibility_list{compatibility_list_}, play_time_manager{play_time_manager_}, system{ + system_} { + // We want the game list to manage our lifetime. + setAutoDelete(false); +} + +GameListWorker::~GameListWorker() { + this->disconnect(); + stop_requested.store(true); + processing_completed.Wait(); +} + +void GameListWorker::ProcessEvents(GameList* game_list) { + while (true) { + std::function func; + { + // Lock queue to protect concurrent modification. + std::scoped_lock lk(lock); + + // If we can't pop a function, return. + if (queued_events.empty()) { + return; + } + + // Pop a function. + func = std::move(queued_events.back()); + queued_events.pop_back(); + } + + // Run the function. + func(game_list); + } +} + +template +void GameListWorker::RecordEvent(F&& func) { + { + // Lock queue to protect concurrent modification. + std::scoped_lock lk(lock); -GameListWorker::~GameListWorker() = default; + // Add the function into the front of the queue. + queued_events.emplace_front(std::move(func)); + } + + // Data now available. + emit DataAvailable(); +} void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { using namespace FileSys; @@ -284,9 +327,9 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { GetMetadataFromControlNCA(patch, *control, icon, name); } - emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader, - program_id, compatibility_list, play_time_manager, patch), - parent_dir); + auto entry = MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader, + program_id, compatibility_list, play_time_manager, patch); + RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); } } @@ -360,11 +403,12 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa const FileSys::PatchManager patch{id, system.GetFileSystemController(), system.GetContentProvider()}; - emit EntryReady(MakeGameListEntry(physical_name, name, - Common::FS::GetSize(physical_name), icon, - *loader, id, compatibility_list, - play_time_manager, patch), - parent_dir); + auto entry = MakeGameListEntry( + physical_name, name, Common::FS::GetSize(physical_name), icon, *loader, + id, compatibility_list, play_time_manager, patch); + + RecordEvent( + [=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); } } else { std::vector icon; @@ -376,11 +420,12 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), system.GetContentProvider()}; - emit EntryReady(MakeGameListEntry(physical_name, name, - Common::FS::GetSize(physical_name), icon, - *loader, program_id, compatibility_list, - play_time_manager, patch), - parent_dir); + auto entry = MakeGameListEntry( + physical_name, name, Common::FS::GetSize(physical_name), icon, *loader, + program_id, compatibility_list, play_time_manager, patch); + + RecordEvent( + [=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); } } } else if (is_dir) { @@ -399,25 +444,34 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa } void GameListWorker::run() { + watch_list.clear(); provider->ClearAllEntries(); + const auto DirEntryReady = [&](GameListDir* game_list_dir) { + RecordEvent([=](GameList* game_list) { game_list->AddDirEntry(game_list_dir); }); + }; + for (UISettings::GameDir& game_dir : game_dirs) { + if (stop_requested) { + break; + } + if (game_dir.path == QStringLiteral("SDMC")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); - emit DirEntryReady(game_list_dir); + DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else if (game_dir.path == QStringLiteral("UserNAND")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); - emit DirEntryReady(game_list_dir); + DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else if (game_dir.path == QStringLiteral("SysNAND")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); - emit DirEntryReady(game_list_dir); + DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else { watch_list.append(game_dir.path); auto* const game_list_dir = new GameListDir(game_dir); - emit DirEntryReady(game_list_dir); + DirEntryReady(game_list_dir); ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), game_dir.deep_scan, game_list_dir); ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), @@ -425,12 +479,6 @@ void GameListWorker::run() { } } - emit Finished(watch_list); + RecordEvent([=](GameList* game_list) { game_list->DonePopulating(watch_list); }); processing_completed.Set(); } - -void GameListWorker::Cancel() { - this->disconnect(); - stop_requested.store(true); - processing_completed.Wait(); -} diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 54dc05e30..d5990fcde 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include @@ -20,6 +21,7 @@ namespace Core { class System; } +class GameList; class QStandardItem; namespace FileSys { @@ -46,24 +48,22 @@ public: /// Starts the processing of directory tree information. void run() override; - /// Tells the worker that it should no longer continue processing. Thread-safe. - void Cancel(); - -signals: +public: /** - * The `EntryReady` signal is emitted once an entry has been prepared and is ready - * to be added to the game list. - * @param entry_items a list with `QStandardItem`s that make up the columns of the new - * entry. + * Synchronously processes any events queued by the worker. + * + * AddDirEntry is called on the game list for every discovered directory. + * AddEntry is called on the game list for every discovered program. + * DonePopulating is called on the game list when processing completes. */ - void DirEntryReady(GameListDir* entry_items); - void EntryReady(QList entry_items, GameListDir* parent_dir); + void ProcessEvents(GameList* game_list); - /** - * After the worker has traversed the game directory looking for entries, this signal is - * emitted with a list of folders that should be watched for changes as well. - */ - void Finished(QStringList watch_list); +signals: + void DataAvailable(); + +private: + template + void RecordEvent(F&& func); private: void AddTitlesToGameList(GameListDir* parent_dir); @@ -84,8 +84,11 @@ private: QStringList watch_list; - Common::Event processing_completed; + std::mutex lock; + std::condition_variable cv; + std::deque> queued_events; std::atomic_bool stop_requested = false; + Common::Event processing_completed; Core::System& system; }; -- cgit v1.2.3 From 19e9bde9e069f841387b8d7cbb4b9074d5f30c21 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 25 Oct 2023 10:05:45 -0400 Subject: kernel: make sure new process is in list --- src/core/core.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/core/core.cpp b/src/core/core.cpp index 934177b85..14d6c8c27 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -312,6 +312,7 @@ struct System::Impl { // Create the process. auto main_process = Kernel::KProcess::Create(system.Kernel()); Kernel::KProcess::Register(system.Kernel(), main_process); + kernel.AppendNewProcess(main_process); kernel.MakeApplicationProcess(main_process); const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); if (load_result != Loader::ResultStatus::Success) { -- cgit v1.2.3 From ca75c58f4391fcd20d325333c802f9e225ff9c47 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 25 Oct 2023 12:59:11 -0400 Subject: sockets: use safe access helpers --- src/core/hle/service/sockets/bsd.cpp | 77 +++++++++++++++++------------------- src/core/hle/service/sockets/bsd.h | 2 +- 2 files changed, 38 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 85849d5f3..dd652ca42 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -39,6 +39,18 @@ bool IsConnectionBased(Type type) { } } +template +T GetValue(std::span buffer) { + T t{}; + std::memcpy(&t, buffer.data(), std::min(sizeof(T), buffer.size())); + return t; +} + +template +void PutValue(std::span buffer, const T& t) { + std::memcpy(buffer.data(), &t, std::min(sizeof(T), buffer.size())); +} + } // Anonymous namespace void BSD::PollWork::Execute(BSD* bsd) { @@ -316,22 +328,12 @@ void BSD::SetSockOpt(HLERequestContext& ctx) { const s32 fd = rp.Pop(); const u32 level = rp.Pop(); const OptName optname = static_cast(rp.Pop()); - - const auto buffer = ctx.ReadBuffer(); - const u8* optval = buffer.empty() ? nullptr : buffer.data(); - size_t optlen = buffer.size(); - - std::array values; - if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) { - std::memcpy(values.data(), buffer.data(), sizeof(values)); - optlen = sizeof(values); - optval = reinterpret_cast(values.data()); - } + const auto optval = ctx.ReadBuffer(); LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level, - static_cast(optname), optlen); + static_cast(optname), optval.size()); - BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval)); + BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval)); } void BSD::Shutdown(HLERequestContext& ctx) { @@ -521,18 +523,19 @@ std::pair BSD::SocketImpl(Domain domain, Type type, Protocol protoco std::pair BSD::PollImpl(std::vector& write_buffer, std::span read_buffer, s32 nfds, s32 timeout) { - if (write_buffer.size() < nfds * sizeof(PollFD)) { - return {-1, Errno::INVAL}; - } - - if (nfds == 0) { + if (nfds <= 0) { // When no entries are provided, -1 is returned with errno zero return {-1, Errno::SUCCESS}; } + if (read_buffer.size() < nfds * sizeof(PollFD)) { + return {-1, Errno::INVAL}; + } + if (write_buffer.size() < nfds * sizeof(PollFD)) { + return {-1, Errno::INVAL}; + } - const size_t length = std::min(read_buffer.size(), write_buffer.size()); std::vector fds(nfds); - std::memcpy(fds.data(), read_buffer.data(), length); + std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD)); if (timeout >= 0) { const s64 seconds = timeout / 1000; @@ -580,7 +583,7 @@ std::pair BSD::PollImpl(std::vector& write_buffer, std::span BSD::AcceptImpl(s32 fd, std::vector& write_buffer) { new_descriptor.is_connection_based = descriptor.is_connection_based; const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); - const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size()); - std::memcpy(write_buffer.data(), &guest_addr_in, length); + PutValue(write_buffer, guest_addr_in); return {new_fd, Errno::SUCCESS}; } @@ -619,8 +621,7 @@ Errno BSD::BindImpl(s32 fd, std::span addr) { return Errno::BADF; } ASSERT(addr.size() == sizeof(SockAddrIn)); - SockAddrIn addr_in; - std::memcpy(&addr_in, addr.data(), sizeof(addr_in)); + auto addr_in = GetValue(addr); return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); } @@ -631,8 +632,7 @@ Errno BSD::ConnectImpl(s32 fd, std::span addr) { } UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); - SockAddrIn addr_in; - std::memcpy(&addr_in, addr.data(), sizeof(addr_in)); + auto addr_in = GetValue(addr); return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); } @@ -650,7 +650,7 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector& write_buffer) { ASSERT(write_buffer.size() >= sizeof(guest_addrin)); write_buffer.resize(sizeof(guest_addrin)); - std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); + PutValue(write_buffer, guest_addrin); return Translate(bsd_errno); } @@ -667,7 +667,7 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector& write_buffer) { ASSERT(write_buffer.size() >= sizeof(guest_addrin)); write_buffer.resize(sizeof(guest_addrin)); - std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); + PutValue(write_buffer, guest_addrin); return Translate(bsd_errno); } @@ -725,7 +725,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector& o optval.size() == sizeof(Errno), { return Errno::INVAL; }, "Incorrect getsockopt option size"); optval.resize(sizeof(Errno)); - memcpy(optval.data(), &translated_pending_err, sizeof(Errno)); + PutValue(optval, translated_pending_err); } return Translate(getsockopt_err); } @@ -735,7 +735,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector& o } } -Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { +Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span optval) { if (!IsFileDescriptorValid(fd)) { return Errno::BADF; } @@ -748,17 +748,15 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); if (optname == OptName::LINGER) { - ASSERT(optlen == sizeof(Linger)); - Linger linger; - std::memcpy(&linger, optval, sizeof(linger)); + ASSERT(optval.size() == sizeof(Linger)); + auto linger = GetValue(optval); ASSERT(linger.onoff == 0 || linger.onoff == 1); return Translate(socket->SetLinger(linger.onoff != 0, linger.linger)); } - ASSERT(optlen == sizeof(u32)); - u32 value; - std::memcpy(&value, optval, sizeof(value)); + ASSERT(optval.size() == sizeof(u32)); + auto value = GetValue(optval); switch (optname) { case OptName::REUSEADDR: @@ -862,7 +860,7 @@ std::pair BSD::RecvFromImpl(s32 fd, u32 flags, std::vector& mess } else { ASSERT(addr.size() == sizeof(SockAddrIn)); const SockAddrIn result = Translate(addr_in); - std::memcpy(addr.data(), &result, sizeof(result)); + PutValue(addr, result); } } @@ -886,8 +884,7 @@ std::pair BSD::SendToImpl(s32 fd, u32 flags, std::span mes Network::SockAddrIn* p_addr_in = nullptr; if (!addr.empty()) { ASSERT(addr.size() == sizeof(SockAddrIn)); - SockAddrIn guest_addr_in; - std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in)); + auto guest_addr_in = GetValue(addr); addr_in = Translate(guest_addr_in); p_addr_in = &addr_in; } diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 161f22b9b..4f69d382c 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -163,7 +163,7 @@ private: Errno ListenImpl(s32 fd, s32 backlog); std::pair FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector& optval); - Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); + Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span optval); Errno ShutdownImpl(s32 fd, s32 how); std::pair RecvImpl(s32 fd, u32 flags, std::vector& message); std::pair RecvFromImpl(s32 fd, u32 flags, std::vector& message, -- cgit v1.2.3 From f26dddf3b59ace84626dc05e2699514579d4a5bc Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Thu, 26 Oct 2023 16:27:44 -0600 Subject: service: am: Implement ISelfController::SaveCurrentScreenshot --- src/core/hle/service/am/am.cpp | 13 +++++++-- src/core/hle/service/caps/caps_manager.cpp | 16 ++++++++---- src/core/hle/service/caps/caps_manager.h | 9 ++++--- src/core/hle/service/caps/caps_ss.cpp | 10 ++++--- src/core/hle/service/caps/caps_su.cpp | 42 +++++++++++++++++++++++++++--- src/core/hle/service/caps/caps_su.h | 9 +++++++ 6 files changed, 82 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 98765b81a..0886531b2 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -31,6 +31,7 @@ #include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/apm/apm_interface.h" #include "core/hle/service/bcat/backend/backend.h" +#include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_types.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ipc_helpers.h" @@ -702,9 +703,17 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& c void ISelfController::SaveCurrentScreenshot(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto album_report_option = rp.PopEnum(); + const auto report_option = rp.PopEnum(); - LOG_WARNING(Service_AM, "(STUBBED) called. album_report_option={}", album_report_option); + LOG_INFO(Service_AM, "called, report_option={}", report_option); + + const auto screenshot_service = + system.ServiceManager().GetService( + "caps:su"); + + if (screenshot_service) { + screenshot_service->CaptureAndSaveScreenshot(report_option); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp index 7d733eb54..96b225d5f 100644 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -228,12 +228,14 @@ Result AlbumManager::LoadAlbumScreenShotThumbnail( Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, - std::span image_data, u64 aruid) { - return SaveScreenShot(out_entry, attribute, {}, image_data, aruid); + AlbumReportOption report_option, std::span image_data, + u64 aruid) { + return SaveScreenShot(out_entry, attribute, report_option, {}, image_data, aruid); } Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, + AlbumReportOption report_option, const ApplicationData& app_data, std::span image_data, u64 aruid) { const u64 title_id = system.GetApplicationProcessProgramID(); @@ -407,10 +409,14 @@ Result AlbumManager::LoadImage(std::span out_image, const std::filesystem::p return ResultSuccess; } -static void PNGToMemory(void* context, void* png, int len) { +void AlbumManager::FlipVerticallyOnWrite(bool flip) { + stbi_flip_vertically_on_write(flip); +} + +static void PNGToMemory(void* context, void* data, int len) { std::vector* png_image = static_cast*>(context); - png_image->reserve(len); - std::memcpy(png_image->data(), png, len); + unsigned char* png = static_cast(data); + png_image->insert(png_image->end(), png, png + len); } Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span image, diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h index 44d85117f..e20c70c7b 100644 --- a/src/core/hle/service/caps/caps_manager.h +++ b/src/core/hle/service/caps/caps_manager.h @@ -59,14 +59,17 @@ public: const ScreenShotDecodeOption& decoder_options) const; Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, - std::span image_data, u64 aruid); - Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, - const ApplicationData& app_data, std::span image_data, + AlbumReportOption report_option, std::span image_data, u64 aruid); + Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, + AlbumReportOption report_option, const ApplicationData& app_data, + std::span image_data, u64 aruid); Result SaveEditedScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, const AlbumFileId& file_id, std::span image_data); + void FlipVerticallyOnWrite(bool flip); + private: static constexpr std::size_t NandAlbumFileLimit = 1000; static constexpr std::size_t SdAlbumFileLimit = 10000; diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index 1ba2b7972..eab023568 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp @@ -34,7 +34,7 @@ void IScreenShotService::SaveScreenShotEx0(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { ScreenShotAttribute attribute{}; - u32 report_option{}; + AlbumReportOption report_option{}; INSERT_PADDING_BYTES(0x4); u64 applet_resource_user_id{}; }; @@ -49,13 +49,16 @@ void IScreenShotService::SaveScreenShotEx0(HLERequestContext& ctx) { parameters.applet_resource_user_id); ApplicationAlbumEntry entry{}; - const auto result = manager->SaveScreenShot(entry, parameters.attribute, image_data_buffer, - parameters.applet_resource_user_id); + manager->FlipVerticallyOnWrite(false); + const auto result = + manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, + image_data_buffer, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 10}; rb.Push(result); rb.PushRaw(entry); } + void IScreenShotService::SaveEditedScreenShotEx1(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { @@ -83,6 +86,7 @@ void IScreenShotService::SaveEditedScreenShotEx1(HLERequestContext& ctx) { image_data_buffer.size(), thumbnail_image_data_buffer.size()); ApplicationAlbumEntry entry{}; + manager->FlipVerticallyOnWrite(false); const auto result = manager->SaveEditedScreenShot(entry, parameters.attribute, parameters.file_id, image_data_buffer); diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index e85625ee4..296b07b00 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp @@ -2,10 +2,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_types.h" #include "core/hle/service/ipc_helpers.h" +#include "video_core/renderer_base.h" namespace Service::Capture { @@ -58,8 +60,10 @@ void IScreenShotApplicationService::SaveScreenShotEx0(HLERequestContext& ctx) { parameters.applet_resource_user_id); ApplicationAlbumEntry entry{}; - const auto result = manager->SaveScreenShot(entry, parameters.attribute, image_data_buffer, - parameters.applet_resource_user_id); + manager->FlipVerticallyOnWrite(false); + const auto result = + manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, + image_data_buffer, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 10}; rb.Push(result); @@ -88,13 +92,43 @@ void IScreenShotApplicationService::SaveScreenShotEx1(HLERequestContext& ctx) { ApplicationAlbumEntry entry{}; ApplicationData app_data{}; std::memcpy(&app_data, app_data_buffer.data(), sizeof(ApplicationData)); + manager->FlipVerticallyOnWrite(false); const auto result = - manager->SaveScreenShot(entry, parameters.attribute, app_data, image_data_buffer, - parameters.applet_resource_user_id); + manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, app_data, + image_data_buffer, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 10}; rb.Push(result); rb.PushRaw(entry); } +void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption report_option) { + auto& renderer = system.Renderer(); + Layout::FramebufferLayout layout = + Layout::DefaultFrameLayout(screenshot_width, screenshot_height); + + const Capture::ScreenShotAttribute attribute{ + .unknown_0{}, + .orientation = Capture::AlbumImageOrientation::None, + .unknown_1{}, + .unknown_2{}, + }; + + renderer.RequestScreenshot( + image_data.data(), + [attribute, report_option, this](bool invert_y) { + // Convert from BGRA to RGBA + for (std::size_t i = 0; i < image_data.size(); i += bytes_per_pixel) { + const u8 temp = image_data[i]; + image_data[i] = image_data[i + 2]; + image_data[i + 2] = temp; + } + + Capture::ApplicationAlbumEntry entry{}; + manager->FlipVerticallyOnWrite(invert_y); + manager->SaveScreenShot(entry, attribute, report_option, image_data, {}); + }, + layout); +} + } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index 89e71f506..21912e95f 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h @@ -10,6 +10,7 @@ class System; } namespace Service::Capture { +enum class AlbumReportOption : s32; class AlbumManager; class IScreenShotApplicationService final : public ServiceFramework { @@ -18,11 +19,19 @@ public: std::shared_ptr album_manager); ~IScreenShotApplicationService() override; + void CaptureAndSaveScreenshot(AlbumReportOption report_option); + private: + static constexpr std::size_t screenshot_width = 1280; + static constexpr std::size_t screenshot_height = 720; + static constexpr std::size_t bytes_per_pixel = 4; + void SetShimLibraryVersion(HLERequestContext& ctx); void SaveScreenShotEx0(HLERequestContext& ctx); void SaveScreenShotEx1(HLERequestContext& ctx); + std::array image_data; + std::shared_ptr manager; }; -- cgit v1.2.3 From 21c631b33bd05fab0bb96dfb774b63048ff22e83 Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 26 Oct 2023 19:24:00 -0400 Subject: renderer_vulkan: fix viewport swizzle dirty state tracking --- src/video_core/renderer_vulkan/vk_state_tracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index d56558a83..daaea2979 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -190,7 +190,7 @@ void SetupDirtySpecialOps(Tables& tables) { void SetupDirtyViewportSwizzles(Tables& tables) { static constexpr size_t swizzle_offset = 6; for (size_t index = 0; index < Regs::NumViewports; ++index) { - tables[0][OFF(viewport_transform) + index * NUM(viewport_transform[0]) + swizzle_offset] = + tables[1][OFF(viewport_transform) + index * NUM(viewport_transform[0]) + swizzle_offset] = ViewportSwizzles; } } -- cgit v1.2.3 From 9e4d606c4c6352fae244128c10403c18eea956f1 Mon Sep 17 00:00:00 2001 From: Ameer J <52414509+ameerj@users.noreply.github.com> Date: Sat, 28 Oct 2023 21:26:22 -0400 Subject: nvidia_flags: Enable GL Threaded optimizations --- src/common/nvidia_flags.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/common/nvidia_flags.cpp b/src/common/nvidia_flags.cpp index 7ed7690ee..fa3747782 100644 --- a/src/common/nvidia_flags.cpp +++ b/src/common/nvidia_flags.cpp @@ -25,6 +25,7 @@ void ConfigureNvidiaEnvironmentFlags() { void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str())); void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1")); + void(_putenv("__GL_THREADED_OPTIMIZATIONS=1")); #endif } -- cgit v1.2.3 From a5aa5876b4c92ee6da74ddc539b17ffa527c1e0b Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sun, 29 Oct 2023 13:47:41 -0400 Subject: android: Break home settings into grid with large screens --- .../main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt | 5 +++-- src/android/app/src/main/res/layout/card_home_option.xml | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index fd9785075..f273c880a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt @@ -26,7 +26,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.findNavController import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialSharedAxis import org.yuzu.yuzu_emu.BuildConfig import org.yuzu.yuzu_emu.HomeNavigationDirections @@ -186,7 +186,8 @@ class HomeSettingsFragment : Fragment() { } binding.homeSettingsList.apply { - layoutManager = LinearLayoutManager(requireContext()) + layoutManager = + GridLayoutManager(requireContext(), resources.getInteger(R.integer.grid_columns)) adapter = HomeSettingAdapter( requireActivity() as AppCompatActivity, viewLifecycleOwner, diff --git a/src/android/app/src/main/res/layout/card_home_option.xml b/src/android/app/src/main/res/layout/card_home_option.xml index f9f1d89fb..6e8a232f9 100644 --- a/src/android/app/src/main/res/layout/card_home_option.xml +++ b/src/android/app/src/main/res/layout/card_home_option.xml @@ -16,7 +16,8 @@ + android:layout_height="wrap_content" + android:layout_gravity="center_vertical"> Date: Sun, 29 Oct 2023 02:46:25 +0200 Subject: Implemented wheel event for volume control in VolumeButton --- src/yuzu/main.cpp | 30 +++++++++++++++++++++++++++++- src/yuzu/main.h | 25 ++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 1431cf2fe..5e687891f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1072,7 +1072,7 @@ void GMainWindow::InitializeWidgets() { }); volume_popup->layout()->addWidget(volume_slider); - volume_button = new QPushButton(); + volume_button = new VolumeButton(); volume_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); volume_button->setFocusPolicy(Qt::NoFocus); volume_button->setCheckable(true); @@ -1103,6 +1103,8 @@ void GMainWindow::InitializeWidgets() { context_menu.exec(volume_button->mapToGlobal(menu_location)); volume_button->repaint(); }); + connect(volume_button, &VolumeButton::VolumeChanged, this, &GMainWindow::UpdateVolumeUI); + statusBar()->insertPermanentWidget(0, volume_button); // setup AA button @@ -5126,6 +5128,32 @@ void GMainWindow::changeEvent(QEvent* event) { QWidget::changeEvent(event); } +void VolumeButton::wheelEvent(QWheelEvent* event) { + + int num_degrees = event->angleDelta().y() / 8; + int num_steps = (num_degrees / 15) * scroll_multiplier; + // Stated in QT docs: Most mouse types work in steps of 15 degrees, in which case the delta + // value is a multiple of 120; i.e., 120 units * 1/8 = 15 degrees. + + if (num_steps > 0) { + Settings::values.volume.SetValue( + std::min(200, Settings::values.volume.GetValue() + num_steps)); + } else { + Settings::values.volume.SetValue( + std::max(0, Settings::values.volume.GetValue() + num_steps)); + } + + scroll_multiplier = std::min(MaxMultiplier, scroll_multiplier * 2); + scroll_timer.start(100); // reset the multiplier if no scroll event occurs within 100 ms + + emit VolumeChanged(); + event->accept(); +} + +void VolumeButton::ResetMultiplier() { + scroll_multiplier = 1; +} + #ifdef main #undef main #endif diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 270a40c5f..f9c6efe4f 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -137,6 +138,28 @@ namespace VkDeviceInfo { class Record; } +class VolumeButton : public QPushButton { + Q_OBJECT +public: + explicit VolumeButton(QWidget* parent = nullptr) : QPushButton(parent), scroll_multiplier(1) { + connect(&scroll_timer, &QTimer::timeout, this, &VolumeButton::ResetMultiplier); + } + +signals: + void VolumeChanged(); + +protected: + void wheelEvent(QWheelEvent* event) override; + +private slots: + void ResetMultiplier(); + +private: + int scroll_multiplier; + QTimer scroll_timer; + constexpr static int MaxMultiplier = 8; +}; + class GMainWindow : public QMainWindow { Q_OBJECT @@ -481,7 +504,7 @@ private: QPushButton* dock_status_button = nullptr; QPushButton* filter_status_button = nullptr; QPushButton* aa_status_button = nullptr; - QPushButton* volume_button = nullptr; + VolumeButton* volume_button = nullptr; QWidget* volume_popup = nullptr; QSlider* volume_slider = nullptr; QTimer status_bar_update_timer; -- cgit v1.2.3 From 8427b9d49d2332c5a2b6a8d68c531a43f9cc3ad1 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 29 Oct 2023 15:31:05 -0400 Subject: renderer_vulkan: ensure exception on surface loss --- src/video_core/renderer_vulkan/vk_swapchain.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 81ef98f61..821f44f1a 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -147,6 +147,9 @@ bool Swapchain::AcquireNextImage() { case VK_ERROR_OUT_OF_DATE_KHR: is_outdated = true; break; + case VK_ERROR_SURFACE_LOST_KHR: + vk::Check(result); + break; default: LOG_ERROR(Render_Vulkan, "vkAcquireNextImageKHR returned {}", vk::ToString(result)); break; @@ -180,6 +183,9 @@ void Swapchain::Present(VkSemaphore render_semaphore) { case VK_ERROR_OUT_OF_DATE_KHR: is_outdated = true; break; + case VK_ERROR_SURFACE_LOST_KHR: + vk::Check(result); + break; default: LOG_CRITICAL(Render_Vulkan, "Failed to present with error {}", vk::ToString(result)); break; -- cgit v1.2.3 From 6e883a26da52b84086340ca5b5d38f55ef04bf8d Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 29 Oct 2023 13:45:53 -0600 Subject: core: Close all KEvents --- src/core/hle/service/am/am.cpp | 8 ++++++-- src/core/hle/service/am/applets/applet_cabinet.cpp | 4 +++- src/core/hle/service/hid/controllers/palma.cpp | 4 +++- src/core/hle/service/hid/hid.cpp | 4 ++++ src/core/hle/service/hid/hidbus/hidbus_base.cpp | 5 ++++- src/core/hle/service/pctl/pctl_module.cpp | 6 ++++++ 6 files changed, 26 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 98765b81a..ff067c8d9 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -796,7 +796,9 @@ ILockAccessor::ILockAccessor(Core::System& system_) lock_event = service_context.CreateEvent("ILockAccessor::LockEvent"); } -ILockAccessor::~ILockAccessor() = default; +ILockAccessor::~ILockAccessor() { + service_context.CloseEvent(lock_event); +}; void ILockAccessor::TryLock(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -909,7 +911,9 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); } -ICommonStateGetter::~ICommonStateGetter() = default; +ICommonStateGetter::~ICommonStateGetter() { + service_context.CloseEvent(sleep_lock_event); +}; void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index 19ed184e8..b379dadeb 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -25,7 +25,9 @@ Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_, service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent"); } -Cabinet::~Cabinet() = default; +Cabinet::~Cabinet() { + service_context.CloseEvent(availability_change_event); +}; void Cabinet::Initialize() { Applet::Initialize(); diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp index 14c67e454..73a2a2b91 100644 --- a/src/core/hle/service/hid/controllers/palma.cpp +++ b/src/core/hle/service/hid/controllers/palma.cpp @@ -19,7 +19,9 @@ Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); } -Controller_Palma::~Controller_Palma() = default; +Controller_Palma::~Controller_Palma() { + service_context.CloseEvent(operation_complete_event); +}; void Controller_Palma::OnInit() {} diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 4d70006c1..929dd5f03 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -2757,6 +2757,10 @@ public: joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent"); } + ~HidSys() { + service_context.CloseEvent(joy_detach_event); + }; + private: void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { LOG_WARNING(Service_HID, "called"); diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp index ee522c36e..8c44f93e8 100644 --- a/src/core/hle/service/hid/hidbus/hidbus_base.cpp +++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp @@ -13,7 +13,10 @@ HidbusBase::HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& ser : system(system_), service_context(service_context_) { send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent"); } -HidbusBase::~HidbusBase() = default; + +HidbusBase::~HidbusBase() { + service_context.CloseEvent(send_command_async_event); +}; void HidbusBase::ActivateDevice() { if (is_activated) { diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp index 938330dd0..6a7fd72bc 100644 --- a/src/core/hle/service/pctl/pctl_module.cpp +++ b/src/core/hle/service/pctl/pctl_module.cpp @@ -141,6 +141,12 @@ public: service_context.CreateEvent("IParentalControlService::RequestSuspensionEvent"); } + ~IParentalControlService() { + service_context.CloseEvent(synchronization_event); + service_context.CloseEvent(unlinked_event); + service_context.CloseEvent(request_suspension_event); + }; + private: bool CheckFreeCommunicationPermissionImpl() const { if (states.temporary_unlocked) { -- cgit v1.2.3 From 25815900236c45a340feb7654b6d49792c10c4f4 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sun, 29 Oct 2023 14:15:37 -0400 Subject: android: Move game deserialization to another thread Deserializing games from the cache in shared preferences was done on the main thread and could cause a stutter on startup. --- .../java/org/yuzu/yuzu_emu/model/GamesViewModel.kt | 39 +++++++++++++--------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt index 6e09fa81d..004b25b04 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt @@ -49,26 +49,33 @@ class GamesViewModel : ViewModel() { // Retrieve list of cached games val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) .getStringSet(GameHelper.KEY_GAMES, emptySet()) - if (storedGames!!.isNotEmpty()) { - val deserializedGames = mutableSetOf() - storedGames.forEach { - val game: Game - try { - game = Json.decodeFromString(it) - } catch (e: MissingFieldException) { - return@forEach - } - val gameExists = - DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path)) - ?.exists() - if (gameExists == true) { - deserializedGames.add(game) + viewModelScope.launch { + withContext(Dispatchers.IO) { + if (storedGames!!.isNotEmpty()) { + val deserializedGames = mutableSetOf() + storedGames.forEach { + val game: Game + try { + game = Json.decodeFromString(it) + } catch (e: MissingFieldException) { + return@forEach + } + + val gameExists = + DocumentFile.fromSingleUri( + YuzuApplication.appContext, + Uri.parse(game.path) + )?.exists() + if (gameExists == true) { + deserializedGames.add(game) + } + } + setGames(deserializedGames.toList()) } + reloadGames(false) } - setGames(deserializedGames.toList()) } - reloadGames(false) } fun setGames(games: List) { -- cgit v1.2.3 From 2c1d850b462f39d2a7eb3d65ad7218ff5223db71 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sun, 29 Oct 2023 21:42:47 -0400 Subject: android: Release touch on input overlay when opening in-game menu --- .../yuzu/yuzu_emu/fragments/EmulationFragment.kt | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 598a9d42b..07bd78bf7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -15,6 +15,7 @@ import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Looper +import android.os.SystemClock import android.view.* import android.widget.TextView import android.widget.Toast @@ -25,6 +26,7 @@ import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.drawerlayout.widget.DrawerLayout +import androidx.drawerlayout.widget.DrawerLayout.DrawerListener import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle @@ -156,6 +158,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { binding.showFpsText.setTextColor(Color.YELLOW) binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } + binding.drawerLayout.addDrawerListener(object : DrawerListener { + override fun onDrawerSlide(drawerView: View, slideOffset: Float) { + binding.surfaceInputOverlay.dispatchTouchEvent( + MotionEvent.obtain( + SystemClock.uptimeMillis(), + SystemClock.uptimeMillis() + 100, + MotionEvent.ACTION_UP, + 0f, + 0f, + 0 + ) + ) + } + + override fun onDrawerOpened(drawerView: View) { + // No op + } + + override fun onDrawerClosed(drawerView: View) { + // No op + } + + override fun onDrawerStateChanged(newState: Int) { + // No op + } + }) binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) binding.inGameMenu.getHeaderView(0).findViewById(R.id.text_game_title).text = game.title -- cgit v1.2.3 From 9b3c64f4a40872208bfd0e8c90174d724ee5e9e6 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Mon, 30 Oct 2023 00:32:43 -0400 Subject: android: Removed unused ControllerMappingHelper --- .../yuzu/yuzu_emu/activities/EmulationActivity.kt | 5 -- .../yuzu/yuzu_emu/utils/ControllerMappingHelper.kt | 70 ---------------------- 2 files changed, 75 deletions(-) delete mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index e96a2059b..7464647c4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -45,7 +45,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.Game -import org.yuzu.yuzu_emu.utils.ControllerMappingHelper import org.yuzu.yuzu_emu.utils.ForegroundService import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.MemoryUtil @@ -57,8 +56,6 @@ import kotlin.math.roundToInt class EmulationActivity : AppCompatActivity(), SensorEventListener { private lateinit var binding: ActivityEmulationBinding - private var controllerMappingHelper: ControllerMappingHelper? = null - var isActivityRecreated = false private lateinit var nfcReader: NfcReader private lateinit var inputHandler: InputHandler @@ -95,8 +92,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { isActivityRecreated = savedInstanceState != null - controllerMappingHelper = ControllerMappingHelper() - // Set these options now so that the SurfaceView the game renders into is the right size. enableFullscreenImmersive() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt deleted file mode 100644 index eeefcdf20..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.yuzu.yuzu_emu.utils - -import android.view.InputDevice -import android.view.KeyEvent -import android.view.MotionEvent - -/** - * Some controllers have incorrect mappings. This class has special-case fixes for them. - */ -class ControllerMappingHelper { - /** - * Some controllers report extra button presses that can be ignored. - */ - fun shouldKeyBeIgnored(inputDevice: InputDevice, keyCode: Int): Boolean { - return if (isDualShock4(inputDevice)) { - // The two analog triggers generate analog motion events as well as a keycode. - // We always prefer to use the analog values, so throw away the button press - keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2 - } else { - false - } - } - - /** - * Scale an axis to be zero-centered with a proper range. - */ - fun scaleAxis(inputDevice: InputDevice, axis: Int, value: Float): Float { - if (isDualShock4(inputDevice)) { - // Android doesn't have correct mappings for this controller's triggers. It reports them - // as RX & RY, centered at -1.0, and with a range of [-1.0, 1.0] - // Scale them to properly zero-centered with a range of [0.0, 1.0]. - if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY) { - return (value + 1) / 2.0f - } - } else if (isXboxOneWireless(inputDevice)) { - // Same as the DualShock 4, the mappings are missing. - if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ) { - return (value + 1) / 2.0f - } - if (axis == MotionEvent.AXIS_GENERIC_1) { - // This axis is stuck at ~.5. Ignore it. - return 0.0f - } - } else if (isMogaPro2Hid(inputDevice)) { - // This controller has a broken axis that reports a constant value. Ignore it. - if (axis == MotionEvent.AXIS_GENERIC_1) { - return 0.0f - } - } - return value - } - - // Sony DualShock 4 controller - private fun isDualShock4(inputDevice: InputDevice): Boolean { - return inputDevice.vendorId == 0x54c && inputDevice.productId == 0x9cc - } - - // Microsoft Xbox One controller - private fun isXboxOneWireless(inputDevice: InputDevice): Boolean { - return inputDevice.vendorId == 0x45e && inputDevice.productId == 0x2e0 - } - - // Moga Pro 2 HID - private fun isMogaPro2Hid(inputDevice: InputDevice): Boolean { - return inputDevice.vendorId == 0x20d6 && inputDevice.productId == 0x6271 - } -} -- cgit v1.2.3 From 70be45c992218469f6884516f9f22e373cd0c3b1 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Mon, 30 Oct 2023 01:09:14 -0400 Subject: android: InputHandler: Convert to object This doesn't need to be an instance of a class because it doesn't hold any data. It's just all helper functions. --- .../main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt | 8 +++----- .../app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 7464647c4..0eda27f7d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -58,7 +58,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { var isActivityRecreated = false private lateinit var nfcReader: NfcReader - private lateinit var inputHandler: InputHandler private val gyro = FloatArray(3) private val accel = FloatArray(3) @@ -100,8 +99,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { nfcReader = NfcReader(this) nfcReader.initialize() - inputHandler = InputHandler() - inputHandler.initialize() + InputHandler.initialize() val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { @@ -190,7 +188,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { return super.dispatchKeyEvent(event) } - return inputHandler.dispatchKeyEvent(event) + return InputHandler.dispatchKeyEvent(event) } override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { @@ -205,7 +203,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { return true } - return inputHandler.dispatchGenericMotionEvent(event) + return InputHandler.dispatchGenericMotionEvent(event) } override fun onSensorChanged(event: SensorEvent) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt index e963dfbc1..fec40e27d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt @@ -8,7 +8,7 @@ import android.view.MotionEvent import kotlin.math.sqrt import org.yuzu.yuzu_emu.NativeLibrary -class InputHandler { +object InputHandler { fun initialize() { // Connect first controller NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device)) -- cgit v1.2.3 From 0bbbe80f75596c090f161ef32a980366e3a436d3 Mon Sep 17 00:00:00 2001 From: Termynat0r Date: Mon, 30 Oct 2023 10:49:39 +0100 Subject: Fix macOS build Added missing preprocessor macros for macOS analog to linux and freebsd --- src/yuzu/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 7cc11ae3b..9eafacea7 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2906,7 +2906,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga const std::string game_file_name = std::filesystem::path(game_path).filename().string(); // Determine full paths for icon and shortcut -#if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) const char* home = std::getenv("HOME"); const std::filesystem::path home_path = (home == nullptr ? "~" : home); const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); @@ -2963,7 +2963,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga QImage icon_data = QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); -#if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) // Convert and write the icon as a PNG if (!icon_data.save(QString::fromStdString(icon_path.string()))) { LOG_ERROR(Frontend, "Could not write icon as PNG to file"); @@ -4002,7 +4002,7 @@ bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::st const std::string& comment, const std::string& icon_path, const std::string& command, const std::string& arguments, const std::string& categories, const std::string& keywords) { -#if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) // This desktop file template was writing referencing // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html std::string shortcut_contents{}; -- cgit v1.2.3 From 1e61c3e1e733ceb49484cb199e5a41a6caf05b9c Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Tue, 24 Oct 2023 17:00:15 -0400 Subject: android: Use header for EmulationSession --- src/android/app/src/main/jni/CMakeLists.txt | 1 + src/android/app/src/main/jni/native.cpp | 713 ++++++++++++---------------- src/android/app/src/main/jni/native.h | 84 ++++ 3 files changed, 392 insertions(+), 406 deletions(-) create mode 100644 src/android/app/src/main/jni/native.h (limited to 'src') diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index e15d1480b..7193903da 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(yuzu-android SHARED id_cache.cpp id_cache.h native.cpp + native.h native_config.cpp uisettings.cpp ) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 598f4e8bf..629be3d81 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -33,7 +33,6 @@ #include "core/crypto/key_manager.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" -#include "core/file_sys/registered_cache.h" #include "core/file_sys/submission_package.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" @@ -48,514 +47,416 @@ #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" #include "core/hid/hid_types.h" -#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" -#include "core/perf_stats.h" #include "jni/android_common/android_common.h" -#include "jni/applets/software_keyboard.h" #include "jni/config.h" -#include "jni/emu_window/emu_window.h" #include "jni/id_cache.h" -#include "video_core/rasterizer_interface.h" +#include "jni/native.h" #include "video_core/renderer_base.h" #define jconst [[maybe_unused]] const auto #define jauto [[maybe_unused]] auto -namespace { +static EmulationSession s_instance; -class EmulationSession final { -public: - EmulationSession() { - m_vfs = std::make_shared(); - } - - ~EmulationSession() = default; - - static EmulationSession& GetInstance() { - return s_instance; - } - - const Core::System& System() const { - return m_system; - } +EmulationSession::EmulationSession() { + m_vfs = std::make_shared(); +} - Core::System& System() { - return m_system; - } +EmulationSession& EmulationSession::GetInstance() { + return s_instance; +} - const EmuWindow_Android& Window() const { - return *m_window; - } +const Core::System& EmulationSession::System() const { + return m_system; +} - EmuWindow_Android& Window() { - return *m_window; - } +Core::System& EmulationSession::System() { + return m_system; +} - ANativeWindow* NativeWindow() const { - return m_native_window; - } +const EmuWindow_Android& EmulationSession::Window() const { + return *m_window; +} - void SetNativeWindow(ANativeWindow* native_window) { - m_native_window = native_window; - } +EmuWindow_Android& EmulationSession::Window() { + return *m_window; +} - int InstallFileToNand(std::string filename, std::string file_extension) { - jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, - std::size_t block_size) { - if (src == nullptr || dest == nullptr) { - return false; - } - if (!dest->Resize(src->GetSize())) { - return false; - } +ANativeWindow* EmulationSession::NativeWindow() const { + return m_native_window; +} - using namespace Common::Literals; - [[maybe_unused]] std::vector buffer(1_MiB); +void EmulationSession::SetNativeWindow(ANativeWindow* native_window) { + m_native_window = native_window; +} - for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { - jconst read = src->Read(buffer.data(), buffer.size(), i); - dest->Write(buffer.data(), read, i); - } - return true; - }; - - enum InstallResult { - Success = 0, - SuccessFileOverwritten = 1, - InstallError = 2, - ErrorBaseGame = 3, - ErrorFilenameExtension = 4, - }; - - m_system.SetContentProvider(std::make_unique()); - m_system.GetFileSystemController().CreateFactories(*m_vfs); - - [[maybe_unused]] std::shared_ptr nsp; - if (file_extension == "nsp") { - nsp = std::make_shared(m_vfs->OpenFile(filename, FileSys::Mode::Read)); - if (nsp->IsExtractedType()) { - return InstallError; - } - } else { - return ErrorFilenameExtension; +int EmulationSession::InstallFileToNand(std::string filename, std::string file_extension) { + jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, + std::size_t block_size) { + if (src == nullptr || dest == nullptr) { + return false; } - - if (!nsp) { - return InstallError; + if (!dest->Resize(src->GetSize())) { + return false; } - if (nsp->GetStatus() != Loader::ResultStatus::Success) { - return InstallError; - } + using namespace Common::Literals; + [[maybe_unused]] std::vector buffer(1_MiB); - jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry( - *nsp, true, copy_func); - - switch (res) { - case FileSys::InstallResult::Success: - return Success; - case FileSys::InstallResult::OverwriteExisting: - return SuccessFileOverwritten; - case FileSys::InstallResult::ErrorBaseInstall: - return ErrorBaseGame; - default: - return InstallError; + for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { + jconst read = src->Read(buffer.data(), buffer.size(), i); + dest->Write(buffer.data(), read, i); } - } + return true; + }; - void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, - const std::string& custom_driver_name, - const std::string& file_redirect_dir) { -#ifdef ARCHITECTURE_arm64 - void* handle{}; - const char* file_redirect_dir_{}; - int featureFlags{}; - - // Enable driver file redirection when renderer debugging is enabled. - if (Settings::values.renderer_debug && file_redirect_dir.size()) { - featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; - file_redirect_dir_ = file_redirect_dir.c_str(); - } + enum InstallResult { + Success = 0, + SuccessFileOverwritten = 1, + InstallError = 2, + ErrorBaseGame = 3, + ErrorFilenameExtension = 4, + }; - // Try to load a custom driver. - if (custom_driver_name.size()) { - handle = adrenotools_open_libvulkan( - RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), - custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); - } + m_system.SetContentProvider(std::make_unique()); + m_system.GetFileSystemController().CreateFactories(*m_vfs); - // Try to load the system driver. - if (!handle) { - handle = - adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), - nullptr, nullptr, file_redirect_dir_, nullptr); + [[maybe_unused]] std::shared_ptr nsp; + if (file_extension == "nsp") { + nsp = std::make_shared(m_vfs->OpenFile(filename, FileSys::Mode::Read)); + if (nsp->IsExtractedType()) { + return InstallError; } - - m_vulkan_library = std::make_shared(handle); -#endif + } else { + return ErrorFilenameExtension; } - bool IsRunning() const { - return m_is_running; + if (!nsp) { + return InstallError; } - bool IsPaused() const { - return m_is_running && m_is_paused; + if (nsp->GetStatus() != Loader::ResultStatus::Success) { + return InstallError; } - const Core::PerfStatsResults& PerfStats() const { - std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); - return m_perf_stats; - } + jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, + copy_func); - void SurfaceChanged() { - if (!IsRunning()) { - return; - } - m_window->OnSurfaceChanged(m_native_window); + switch (res) { + case FileSys::InstallResult::Success: + return Success; + case FileSys::InstallResult::OverwriteExisting: + return SuccessFileOverwritten; + case FileSys::InstallResult::ErrorBaseInstall: + return ErrorBaseGame; + default: + return InstallError; } +} - void ConfigureFilesystemProvider(const std::string& filepath) { - const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read); - if (!file) { - return; - } - - auto loader = Loader::GetLoader(m_system, file); - if (!loader) { - return; - } - - const auto file_type = loader->GetFileType(); - if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { - return; - } +void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir, + const std::string& custom_driver_dir, + const std::string& custom_driver_name, + const std::string& file_redirect_dir) { +#ifdef ARCHITECTURE_arm64 + void* handle{}; + const char* file_redirect_dir_{}; + int featureFlags{}; - u64 program_id = 0; - const auto res2 = loader->ReadProgramId(program_id); - if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { - m_manual_provider->AddEntry(FileSys::TitleType::Application, - FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), - program_id, file); - } else if (res2 == Loader::ResultStatus::Success && - (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) { - const auto nsp = file_type == Loader::FileType::NSP - ? std::make_shared(file) - : FileSys::XCI{file}.GetSecurePartitionNSP(); - for (const auto& title : nsp->GetNCAs()) { - for (const auto& entry : title.second) { - m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first, - entry.second->GetBaseFile()); - } - } - } + // Enable driver file redirection when renderer debugging is enabled. + if (Settings::values.renderer_debug && file_redirect_dir.size()) { + featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT; + file_redirect_dir_ = file_redirect_dir.c_str(); } - Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { - std::scoped_lock lock(m_mutex); - - // Create the render window. - m_window = std::make_unique(&m_input_subsystem, m_native_window, - m_vulkan_library); - - m_system.SetFilesystem(m_vfs); - m_system.GetUserChannel().clear(); - - // Initialize system. - jauto android_keyboard = std::make_unique(); - m_software_keyboard = android_keyboard.get(); - m_system.SetShuttingDown(false); - m_system.ApplySettings(); - Settings::LogSettings(); - m_system.HIDCore().ReloadInputDevices(); - m_system.SetAppletFrontendSet({ - nullptr, // Amiibo Settings - nullptr, // Controller Selector - nullptr, // Error Display - nullptr, // Mii Editor - nullptr, // Parental Controls - nullptr, // Photo Viewer - nullptr, // Profile Selector - std::move(android_keyboard), // Software Keyboard - nullptr, // Web Browser - }); - - // Initialize filesystem. - m_manual_provider = std::make_unique(); - m_system.SetContentProvider(std::make_unique()); - m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, - m_manual_provider.get()); - m_system.GetFileSystemController().CreateFactories(*m_vfs); - ConfigureFilesystemProvider(filepath); - - // Initialize account manager - m_profile_manager = std::make_unique(); - - // Load the ROM. - m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); - if (m_load_result != Core::SystemResultStatus::Success) { - return m_load_result; - } - - // Complete initialization. - m_system.GPU().Start(); - m_system.GetCpuManager().OnGpuReady(); - m_system.RegisterExitCallback([&] { HaltEmulation(); }); + // Try to load a custom driver. + if (custom_driver_name.size()) { + handle = adrenotools_open_libvulkan( + RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(), + custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr); + } - return Core::SystemResultStatus::Success; + // Try to load the system driver. + if (!handle) { + handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), + nullptr, nullptr, file_redirect_dir_, nullptr); } - void ShutdownEmulation() { - std::scoped_lock lock(m_mutex); + m_vulkan_library = std::make_shared(handle); +#endif +} - m_is_running = false; +bool EmulationSession::IsRunning() const { + return m_is_running; +} - // Unload user input. - m_system.HIDCore().UnloadInputDevices(); +bool EmulationSession::IsPaused() const { + return m_is_running && m_is_paused; +} - // Shutdown the main emulated process - if (m_load_result == Core::SystemResultStatus::Success) { - m_system.DetachDebugger(); - m_system.ShutdownMainProcess(); - m_detached_tasks.WaitForAllTasks(); - m_load_result = Core::SystemResultStatus::ErrorNotInitialized; - m_window.reset(); - OnEmulationStopped(Core::SystemResultStatus::Success); - return; - } +const Core::PerfStatsResults& EmulationSession::PerfStats() const { + std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); + return m_perf_stats; +} - // Tear down the render window. - m_window.reset(); +void EmulationSession::SurfaceChanged() { + if (!IsRunning()) { + return; } + m_window->OnSurfaceChanged(m_native_window); +} - void PauseEmulation() { - std::scoped_lock lock(m_mutex); - m_system.Pause(); - m_is_paused = true; +void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) { + const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read); + if (!file) { + return; } - void UnPauseEmulation() { - std::scoped_lock lock(m_mutex); - m_system.Run(); - m_is_paused = false; + auto loader = Loader::GetLoader(m_system, file); + if (!loader) { + return; } - void HaltEmulation() { - std::scoped_lock lock(m_mutex); - m_is_running = false; - m_cv.notify_one(); + const auto file_type = loader->GetFileType(); + if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { + return; } - void RunEmulation() { - { - std::scoped_lock lock(m_mutex); - m_is_running = true; + u64 program_id = 0; + const auto res2 = loader->ReadProgramId(program_id); + if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { + m_manual_provider->AddEntry(FileSys::TitleType::Application, + FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), + program_id, file); + } else if (res2 == Loader::ResultStatus::Success && + (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) { + const auto nsp = file_type == Loader::FileType::NSP + ? std::make_shared(file) + : FileSys::XCI{file}.GetSecurePartitionNSP(); + for (const auto& title : nsp->GetNCAs()) { + for (const auto& entry : title.second) { + m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first, + entry.second->GetBaseFile()); + } } + } +} - // Load the disk shader cache. - if (Settings::values.use_disk_shader_cache.GetValue()) { - LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); - m_system.Renderer().ReadRasterizer()->LoadDiskResources( - m_system.GetApplicationProcessProgramID(), std::stop_token{}, - LoadDiskCacheProgress); - LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); - } +Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) { + std::scoped_lock lock(m_mutex); + + // Create the render window. + m_window = + std::make_unique(&m_input_subsystem, m_native_window, m_vulkan_library); + + m_system.SetFilesystem(m_vfs); + m_system.GetUserChannel().clear(); + + // Initialize system. + jauto android_keyboard = std::make_unique(); + m_software_keyboard = android_keyboard.get(); + m_system.SetShuttingDown(false); + m_system.ApplySettings(); + Settings::LogSettings(); + m_system.HIDCore().ReloadInputDevices(); + m_system.SetAppletFrontendSet({ + nullptr, // Amiibo Settings + nullptr, // Controller Selector + nullptr, // Error Display + nullptr, // Mii Editor + nullptr, // Parental Controls + nullptr, // Photo Viewer + nullptr, // Profile Selector + std::move(android_keyboard), // Software Keyboard + nullptr, // Web Browser + }); + + // Initialize filesystem. + m_manual_provider = std::make_unique(); + m_system.SetContentProvider(std::make_unique()); + m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, + m_manual_provider.get()); + m_system.GetFileSystemController().CreateFactories(*m_vfs); + ConfigureFilesystemProvider(filepath); + + // Initialize account manager + m_profile_manager = std::make_unique(); + + // Load the ROM. + m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); + if (m_load_result != Core::SystemResultStatus::Success) { + return m_load_result; + } + + // Complete initialization. + m_system.GPU().Start(); + m_system.GetCpuManager().OnGpuReady(); + m_system.RegisterExitCallback([&] { HaltEmulation(); }); - void(m_system.Run()); + return Core::SystemResultStatus::Success; +} - if (m_system.DebuggerEnabled()) { - m_system.InitializeDebugger(); - } +void EmulationSession::ShutdownEmulation() { + std::scoped_lock lock(m_mutex); - OnEmulationStarted(); + m_is_running = false; - while (true) { - { - [[maybe_unused]] std::unique_lock lock(m_mutex); - if (m_cv.wait_for(lock, std::chrono::milliseconds(800), - [&]() { return !m_is_running; })) { - // Emulation halted. - break; - } - } - { - // Refresh performance stats. - std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); - m_perf_stats = m_system.GetAndResetPerfStats(); - } - } - } - - std::string GetRomTitle(const std::string& path) { - return GetRomMetadata(path).title; - } - - std::vector GetRomIcon(const std::string& path) { - return GetRomMetadata(path).icon; - } + // Unload user input. + m_system.HIDCore().UnloadInputDevices(); - bool GetIsHomebrew(const std::string& path) { - return GetRomMetadata(path).isHomebrew; + // Shutdown the main emulated process + if (m_load_result == Core::SystemResultStatus::Success) { + m_system.DetachDebugger(); + m_system.ShutdownMainProcess(); + m_detached_tasks.WaitForAllTasks(); + m_load_result = Core::SystemResultStatus::ErrorNotInitialized; + m_window.reset(); + OnEmulationStopped(Core::SystemResultStatus::Success); + return; } - void ResetRomMetadata() { - m_rom_metadata_cache.clear(); - } + // Tear down the render window. + m_window.reset(); +} - bool IsHandheldOnly() { - jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); +void EmulationSession::PauseEmulation() { + std::scoped_lock lock(m_mutex); + m_system.Pause(); + m_is_paused = true; +} - if (npad_style_set.fullkey == 1) { - return false; - } +void EmulationSession::UnPauseEmulation() { + std::scoped_lock lock(m_mutex); + m_system.Run(); + m_is_paused = false; +} - if (npad_style_set.handheld == 0) { - return false; - } +void EmulationSession::HaltEmulation() { + std::scoped_lock lock(m_mutex); + m_is_running = false; + m_cv.notify_one(); +} - return !Settings::IsDockedMode(); +void EmulationSession::RunEmulation() { + { + std::scoped_lock lock(m_mutex); + m_is_running = true; } - void SetDeviceType([[maybe_unused]] int index, int type) { - jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - controller->SetNpadStyleIndex(static_cast(type)); + // Load the disk shader cache. + if (Settings::values.use_disk_shader_cache.GetValue()) { + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); + m_system.Renderer().ReadRasterizer()->LoadDiskResources( + m_system.GetApplicationProcessProgramID(), std::stop_token{}, LoadDiskCacheProgress); + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); } - void OnGamepadConnectEvent([[maybe_unused]] int index) { - jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - - // Ensure that player1 is configured correctly and handheld disconnected - if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { - jauto handheld = - m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); + void(m_system.Run()); - if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { - handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); - handheld->Disconnect(); - } - } + if (m_system.DebuggerEnabled()) { + m_system.InitializeDebugger(); + } - // Ensure that handheld is configured correctly and player 1 disconnected - if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { - jauto player1 = - m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + OnEmulationStarted(); - if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { - player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); - player1->Disconnect(); + while (true) { + { + [[maybe_unused]] std::unique_lock lock(m_mutex); + if (m_cv.wait_for(lock, std::chrono::milliseconds(800), + [&]() { return !m_is_running; })) { + // Emulation halted. + break; } } - - if (!controller->IsConnected()) { - controller->Connect(); + { + // Refresh performance stats. + std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); + m_perf_stats = m_system.GetAndResetPerfStats(); } } +} + +bool EmulationSession::IsHandheldOnly() { + jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); - void OnGamepadDisconnectEvent([[maybe_unused]] int index) { - jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - controller->Disconnect(); + if (npad_style_set.fullkey == 1) { + return false; } - SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() { - return m_software_keyboard; + if (npad_style_set.handheld == 0) { + return false; } -private: - struct RomMetadata { - std::string title; - std::vector icon; - bool isHomebrew; - }; + return !Settings::IsDockedMode(); +} - RomMetadata GetRomMetadata(const std::string& path) { - if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { - return search->second; - } +void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) { + jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + controller->SetNpadStyleIndex(static_cast(type)); +} - return CacheRomMetadata(path); - } +void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) { + jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - RomMetadata CacheRomMetadata(const std::string& path) { - jconst file = Core::GetGameFileFromPath(m_vfs, path); - jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); + // Ensure that player1 is configured correctly and handheld disconnected + if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { + jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); - RomMetadata entry; - loader->ReadTitle(entry.title); - loader->ReadIcon(entry.icon); - if (loader->GetFileType() == Loader::FileType::NRO) { - jauto loader_nro = reinterpret_cast(loader.get()); - entry.isHomebrew = loader_nro->IsHomebrew(); - } else { - entry.isHomebrew = false; + if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { + handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + handheld->Disconnect(); } - - m_rom_metadata_cache[path] = entry; - - return entry; } -private: - static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) { - JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), - IDCache::GetDiskCacheLoadProgress(), static_cast(stage), - static_cast(progress), static_cast(max)); - } + // Ensure that handheld is configured correctly and player 1 disconnected + if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { + jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); - static void OnEmulationStarted() { - JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), - IDCache::GetOnEmulationStarted()); + if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { + player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); + player1->Disconnect(); + } } - static void OnEmulationStopped(Core::SystemResultStatus result) { - JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), - IDCache::GetOnEmulationStopped(), static_cast(result)); + if (!controller->IsConnected()) { + controller->Connect(); } +} -private: - static EmulationSession s_instance; - - // Frontend management - std::unordered_map m_rom_metadata_cache; - - // Window management - std::unique_ptr m_window; - ANativeWindow* m_native_window{}; - - // Core emulation - Core::System m_system; - InputCommon::InputSubsystem m_input_subsystem; - Common::DetachedTasks m_detached_tasks; - Core::PerfStatsResults m_perf_stats{}; - std::shared_ptr m_vfs; - Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; - std::atomic m_is_running = false; - std::atomic m_is_paused = false; - SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; - std::unique_ptr m_profile_manager; - std::unique_ptr m_manual_provider; +void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) { + jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); + controller->Disconnect(); +} - // GPU driver parameters - std::shared_ptr m_vulkan_library; +SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { + return m_software_keyboard; +} - // Synchronization - std::condition_variable_any m_cv; - mutable std::mutex m_perf_stats_mutex; - mutable std::mutex m_mutex; -}; +void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, + int max) { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), + IDCache::GetDiskCacheLoadProgress(), static_cast(stage), + static_cast(progress), static_cast(max)); +} -/*static*/ EmulationSession EmulationSession::s_instance; +void EmulationSession::OnEmulationStarted() { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted()); +} -} // Anonymous namespace +void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { + JNIEnv* env = IDCache::GetEnvForThread(); + env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(), + static_cast(result)); +} static Core::SystemResultStatus RunEmulation(const std::string& filepath) { Common::Log::Initialize(); diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h new file mode 100644 index 000000000..2eb5c4349 --- /dev/null +++ b/src/android/app/src/main/jni/native.h @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "core/core.h" +#include "core/perf_stats.h" +#include "jni/emu_window/emu_window.h" +#include "jni/applets/software_keyboard.h" +#include "video_core/rasterizer_interface.h" +#include "common/detached_tasks.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/file_sys/registered_cache.h" + +#pragma once + +class EmulationSession final { +public: + explicit EmulationSession(); + ~EmulationSession() = default; + + static EmulationSession& GetInstance(); + const Core::System& System() const; + Core::System& System(); + + const EmuWindow_Android& Window() const; + EmuWindow_Android& Window(); + ANativeWindow* NativeWindow() const; + void SetNativeWindow(ANativeWindow* native_window); + void SurfaceChanged(); + + int InstallFileToNand(std::string filename, std::string file_extension); + void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, + const std::string& custom_driver_name, + const std::string& file_redirect_dir); + + bool IsRunning() const; + bool IsPaused() const; + void PauseEmulation(); + void UnPauseEmulation(); + void HaltEmulation(); + void RunEmulation(); + void ShutdownEmulation(); + + const Core::PerfStatsResults& PerfStats() const; + void ConfigureFilesystemProvider(const std::string& filepath); + Core::SystemResultStatus InitializeEmulation(const std::string& filepath); + + bool IsHandheldOnly(); + void SetDeviceType([[maybe_unused]] int index, int type); + void OnGamepadConnectEvent([[maybe_unused]] int index); + void OnGamepadDisconnectEvent([[maybe_unused]] int index); + SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); + +private: + static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); + static void OnEmulationStarted(); + static void OnEmulationStopped(Core::SystemResultStatus result); + +private: + // Window management + std::unique_ptr m_window; + ANativeWindow* m_native_window{}; + + // Core emulation + Core::System m_system; + InputCommon::InputSubsystem m_input_subsystem; + Common::DetachedTasks m_detached_tasks; + Core::PerfStatsResults m_perf_stats{}; + std::shared_ptr m_vfs; + Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; + std::atomic m_is_running = false; + std::atomic m_is_paused = false; + SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; + std::unique_ptr m_profile_manager; + std::unique_ptr m_manual_provider; + + // GPU driver parameters + std::shared_ptr m_vulkan_library; + + // Synchronization + std::condition_variable_any m_cv; + mutable std::mutex m_perf_stats_mutex; + mutable std::mutex m_mutex; +}; -- cgit v1.2.3 From a9e29a3972dc0d74a6bd42cb767e5ace86318937 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Tue, 24 Oct 2023 17:02:13 -0400 Subject: android: Refactor game metadata collection to new file This also removes irrelevant data and adds new information from/to the Game data class and RomMetadata struct --- .../main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | 31 ------ .../java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 2 +- .../src/main/java/org/yuzu/yuzu_emu/model/Game.kt | 17 ++-- .../java/org/yuzu/yuzu_emu/model/GamesViewModel.kt | 9 +- .../java/org/yuzu/yuzu_emu/utils/GameHelper.kt | 17 ++-- .../java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt | 3 +- .../java/org/yuzu/yuzu_emu/utils/GameMetadata.kt | 20 ++++ src/android/app/src/main/jni/CMakeLists.txt | 1 + src/android/app/src/main/jni/game_metadata.cpp | 112 +++++++++++++++++++++ src/android/app/src/main/jni/native.cpp | 44 -------- 10 files changed, 154 insertions(+), 102 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt create mode 100644 src/android/app/src/main/jni/game_metadata.cpp (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 115f72710..22c9b05de 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 @@ -215,32 +215,6 @@ object NativeLibrary { external fun initGameIni(gameID: String?) - /** - * Gets the embedded icon within the given ROM. - * - * @param filename the file path to the ROM. - * @return a byte array containing the JPEG data for the icon. - */ - external fun getIcon(filename: String): ByteArray - - /** - * Gets the embedded title of the given ISO/ROM. - * - * @param filename The file path to the ISO/ROM. - * @return the embedded title of the ISO/ROM. - */ - external fun getTitle(filename: String): String - - external fun getDescription(filename: String): String - - external fun getGameId(filename: String): String - - external fun getRegions(filename: String): String - - external fun getCompany(filename: String): String - - external fun isHomebrew(filename: String): Boolean - external fun setAppDirectory(directory: String) /** @@ -293,11 +267,6 @@ object NativeLibrary { */ external fun stopEmulation() - /** - * Resets the in-memory ROM metadata cache. - */ - external fun resetRomMetadata() - /** * Returns true if emulation is running (or is paused). */ diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index f9f88a1d2..0c82cdba8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -147,7 +147,7 @@ class GameAdapter(private val activity: AppCompatActivity) : private class DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean { - return oldItem.gameId == newItem.gameId + return oldItem.programId == newItem.programId } override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index 6527c64ab..b43978fce 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt @@ -12,15 +12,14 @@ import kotlinx.serialization.Serializable @Serializable class Game( val title: String, - val description: String, - val regions: String, val path: String, - val gameId: String, - val company: String, + val programId: String, + val developer: String, + val version: String, val isHomebrew: Boolean ) : Parcelable { - val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime" - val keyLastPlayedTime get() = "${gameId}_LastPlayed" + val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime" + val keyLastPlayedTime get() = "${programId}_LastPlayed" override fun equals(other: Any?): Boolean { if (other !is Game) { @@ -32,11 +31,9 @@ class Game( override fun hashCode(): Int { var result = title.hashCode() - result = 31 * result + description.hashCode() - result = 31 * result + regions.hashCode() result = 31 * result + path.hashCode() - result = 31 * result + gameId.hashCode() - result = 31 * result + company.hashCode() + result = 31 * result + programId.hashCode() + result = 31 * result + developer.hashCode() result = 31 * result + isHomebrew.hashCode() return result } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt index 004b25b04..8512ed17c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt @@ -14,15 +14,13 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.MissingFieldException import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.utils.GameHelper +import org.yuzu.yuzu_emu.utils.GameMetadata -@OptIn(ExperimentalSerializationApi::class) class GamesViewModel : ViewModel() { val games: StateFlow> get() = _games private val _games = MutableStateFlow(emptyList()) @@ -58,7 +56,8 @@ class GamesViewModel : ViewModel() { val game: Game try { game = Json.decodeFromString(it) - } catch (e: MissingFieldException) { + } catch (e: Exception) { + // We don't care about any errors related to parsing the game cache return@forEach } @@ -113,7 +112,7 @@ class GamesViewModel : ViewModel() { viewModelScope.launch { withContext(Dispatchers.IO) { - NativeLibrary.resetRomMetadata() + GameMetadata.resetMetadata() setGames(GameHelper.getGames()) _isReloading.value = false diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index 9001ca9ab..e6aca6b44 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt @@ -71,27 +71,26 @@ object GameHelper { fun getGame(uri: Uri, addedToLibrary: Boolean): Game { val filePath = uri.toString() - var name = NativeLibrary.getTitle(filePath) + var name = GameMetadata.getTitle(filePath) // If the game's title field is empty, use the filename. if (name.isEmpty()) { name = FileUtil.getFilename(uri) } - var gameId = NativeLibrary.getGameId(filePath) + var programId = GameMetadata.getProgramId(filePath) // If the game's ID field is empty, use the filename without extension. - if (gameId.isEmpty()) { - gameId = name.substring(0, name.lastIndexOf(".")) + if (programId.isEmpty()) { + programId = name.substring(0, name.lastIndexOf(".")) } val newGame = Game( name, - NativeLibrary.getDescription(filePath).replace("\n", " "), - NativeLibrary.getRegions(filePath), filePath, - gameId, - NativeLibrary.getCompany(filePath), - NativeLibrary.isHomebrew(filePath) + programId, + GameMetadata.getDeveloper(filePath), + GameMetadata.getVersion(filePath), + GameMetadata.getIsHomebrew(filePath) ) if (addedToLibrary) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt index 9fe99fab1..654d62f52 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt @@ -18,7 +18,6 @@ import coil.key.Keyer import coil.memory.MemoryCache import coil.request.ImageRequest import coil.request.Options -import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.model.Game @@ -36,7 +35,7 @@ class GameIconFetcher( } private fun decodeGameIcon(uri: String): Bitmap? { - val data = NativeLibrary.getIcon(uri) + val data = GameMetadata.getIcon(uri) return BitmapFactory.decodeByteArray( data, 0, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt new file mode 100644 index 000000000..0f3542ac6 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameMetadata.kt @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.utils + +object GameMetadata { + external fun getTitle(path: String): String + + external fun getProgramId(path: String): String + + external fun getDeveloper(path: String): String + + external fun getVersion(path: String): String + + external fun getIcon(path: String): ByteArray + + external fun getIsHomebrew(path: String): Boolean + + external fun resetMetadata() +} diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 7193903da..1c36661f5 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(yuzu-android SHARED native.h native_config.cpp uisettings.cpp + game_metadata.cpp ) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) diff --git a/src/android/app/src/main/jni/game_metadata.cpp b/src/android/app/src/main/jni/game_metadata.cpp new file mode 100644 index 000000000..24d9df702 --- /dev/null +++ b/src/android/app/src/main/jni/game_metadata.cpp @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include "core/loader/loader.h" +#include "jni/android_common/android_common.h" +#include "native.h" + +struct RomMetadata { + std::string title; + u64 programId; + std::string developer; + std::string version; + std::vector icon; + bool isHomebrew; +}; + +std::unordered_map m_rom_metadata_cache; + +RomMetadata CacheRomMetadata(const std::string& path) { + const auto file = + Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path); + auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); + + RomMetadata entry; + loader->ReadTitle(entry.title); + loader->ReadProgramId(entry.programId); + loader->ReadIcon(entry.icon); + + const FileSys::PatchManager pm{ + entry.programId, EmulationSession::GetInstance().System().GetFileSystemController(), + EmulationSession::GetInstance().System().GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + + if (control.first != nullptr) { + entry.developer = control.first->GetDeveloperName(); + entry.version = control.first->GetVersionString(); + } else { + FileSys::NACP nacp; + if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) { + entry.developer = nacp.GetDeveloperName(); + } else { + entry.developer = ""; + } + + entry.version = "1.0.0"; + } + + if (loader->GetFileType() == Loader::FileType::NRO) { + auto loader_nro = reinterpret_cast(loader.get()); + entry.isHomebrew = loader_nro->IsHomebrew(); + } else { + entry.isHomebrew = false; + } + + m_rom_metadata_cache[path] = entry; + + return entry; +} + +RomMetadata GetRomMetadata(const std::string& path) { + if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { + return search->second; + } + + return CacheRomMetadata(path); +} + +extern "C" { + +jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj, + jstring jpath) { + return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title); +} + +jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj, + jstring jpath) { + return ToJString(env, std::to_string(GetRomMetadata(GetJString(env, jpath)).programId)); +} + +jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj, + jstring jpath) { + return ToJString(env, GetRomMetadata(GetJString(env, jpath)).developer); +} + +jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj, + jstring jpath) { + return ToJString(env, GetRomMetadata(GetJString(env, jpath)).version); +} + +jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj, + jstring jpath) { + auto icon_data = GetRomMetadata(GetJString(env, jpath)).icon; + jbyteArray icon = env->NewByteArray(static_cast(icon_data.size())); + env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), + reinterpret_cast(icon_data.data())); + return icon; +} + +jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj, + jstring jpath) { + return static_cast(GetRomMetadata(GetJString(env, jpath)).isHomebrew); +} + +void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) { + return m_rom_metadata_cache.clear(); +} + +} // extern "C" diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 629be3d81..686b73588 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -558,10 +558,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass cla EmulationSession::GetInstance().HaltEmulation(); } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) { - EmulationSession::GetInstance().ResetRomMetadata(); -} - jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) { return static_cast(EmulationSession::GetInstance().IsRunning()); } @@ -667,46 +663,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c } } -jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz, - jstring j_filename) { - jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename)); - jbyteArray icon = env->NewByteArray(static_cast(icon_data.size())); - env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), - reinterpret_cast(icon_data.data())); - return icon; -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz, - jstring j_filename) { - jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename)); - return env->NewStringUTF(title.c_str()); -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz, - jstring j_filename) { - return j_filename; -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz, - jstring j_filename) { - return j_filename; -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz, - jstring j_filename) { - return env->NewStringUTF(""); -} - -jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz, - jstring j_filename) { - return env->NewStringUTF(""); -} - -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz, - jstring j_filename) { - return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); -} - void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) { // Create the default config.ini. Config{}; -- cgit v1.2.3 From 585b6e9d46b207a6b48a021ea35636fb8c92b405 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Tue, 24 Oct 2023 22:51:09 -0400 Subject: android: Fix resolving android URIs in native code --- .../main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | 29 +++++++++++++++---- .../java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt | 17 +++++++++++ src/android/app/src/main/jni/native.h | 8 +++--- src/common/fs/fs_android.cpp | 33 ++++++++++++++++++++++ src/common/fs/fs_android.h | 15 ++++++++++ src/common/fs/path_util.cpp | 10 +++++++ src/common/string_util.cpp | 12 ++++++++ 7 files changed, 115 insertions(+), 9 deletions(-) (limited to 'src') 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 22c9b05de..5fe235dba 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu import android.app.Dialog import android.content.DialogInterface +import android.net.Uri import android.os.Bundle import android.text.Html import android.text.method.LinkMovementMethod @@ -16,7 +17,7 @@ import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder import java.lang.ref.WeakReference import org.yuzu.yuzu_emu.activities.EmulationActivity -import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath +import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable @@ -68,7 +69,7 @@ object NativeLibrary { @Keep @JvmStatic fun openContentUri(path: String?, openmode: String?): Int { - return if (isNativePath(path!!)) { + return if (DocumentsTree.isNativePath(path!!)) { YuzuApplication.documentsTree!!.openContentUri(path, openmode) } else { FileUtil.openContentUri(path, openmode) @@ -78,7 +79,7 @@ object NativeLibrary { @Keep @JvmStatic fun getSize(path: String?): Long { - return if (isNativePath(path!!)) { + return if (DocumentsTree.isNativePath(path!!)) { YuzuApplication.documentsTree!!.getFileSize(path) } else { FileUtil.getFileSize(path) @@ -88,7 +89,7 @@ object NativeLibrary { @Keep @JvmStatic fun exists(path: String?): Boolean { - return if (isNativePath(path!!)) { + return if (DocumentsTree.isNativePath(path!!)) { YuzuApplication.documentsTree!!.exists(path) } else { FileUtil.exists(path) @@ -98,13 +99,31 @@ object NativeLibrary { @Keep @JvmStatic fun isDirectory(path: String?): Boolean { - return if (isNativePath(path!!)) { + return if (DocumentsTree.isNativePath(path!!)) { YuzuApplication.documentsTree!!.isDirectory(path) } else { FileUtil.isDirectory(path) } } + @Keep + @JvmStatic + fun getParentDirectory(path: String): String = + if (DocumentsTree.isNativePath(path)) { + YuzuApplication.documentsTree!!.getParentDirectory(path) + } else { + path + } + + @Keep + @JvmStatic + fun getFilename(path: String): String = + if (DocumentsTree.isNativePath(path)) { + YuzuApplication.documentsTree!!.getFilename(path) + } else { + FileUtil.getFilename(Uri.parse(path)) + } + /** * Returns true if pro controller isn't available and handheld is */ diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt index eafcf9e42..738275297 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt @@ -42,6 +42,23 @@ class DocumentsTree { return node != null && node.isDirectory } + fun getParentDirectory(filepath: String): String { + val node = resolvePath(filepath)!! + val parentNode = node.parent + if (parentNode != null && parentNode.isDirectory) { + return parentNode.uri!!.toString() + } + return node.uri!!.toString() + } + + fun getFilename(filepath: String): String { + val node = resolvePath(filepath) + if (node != null) { + return node.name!! + } + return filepath + } + private fun resolvePath(filepath: String): DocumentsNode? { val tokens = StringTokenizer(filepath, File.separator, false) var iterator = root diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index 2eb5c4349..b1db87e41 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h @@ -2,14 +2,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/detached_tasks.h" #include "core/core.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/service/acc/profile_manager.h" #include "core/perf_stats.h" -#include "jni/emu_window/emu_window.h" #include "jni/applets/software_keyboard.h" +#include "jni/emu_window/emu_window.h" #include "video_core/rasterizer_interface.h" -#include "common/detached_tasks.h" -#include "core/hle/service/acc/profile_manager.h" -#include "core/file_sys/registered_cache.h" #pragma once diff --git a/src/common/fs/fs_android.cpp b/src/common/fs/fs_android.cpp index 298a79bac..1dd826a4a 100644 --- a/src/common/fs/fs_android.cpp +++ b/src/common/fs/fs_android.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/fs/fs_android.h" +#include "common/string_util.h" namespace Common::FS::Android { @@ -28,28 +29,35 @@ void RegisterCallbacks(JNIEnv* env, jclass clazz) { env->GetJavaVM(&g_jvm); native_library = clazz; +#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ + F(JMethodID, JMethodName, Signature) #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ F(JMethodID, JMethodName, Signature) #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ F(JMethodID, JMethodName, Signature) #define F(JMethodID, JMethodName, Signature) \ JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature); + ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) ANDROID_STORAGE_FUNCTIONS(FS) #undef F #undef FS #undef FR +#undef FH } void UnRegisterCallbacks() { +#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) #define F(JMethodID) JMethodID = nullptr; + ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) ANDROID_STORAGE_FUNCTIONS(FS) #undef F #undef FS #undef FR +#undef FH } bool IsContentUri(const std::string& path) { @@ -95,4 +103,29 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) #undef F #undef FR +#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ + F(FunctionName, JMethodID, Caller) +#define F(FunctionName, JMethodID, Caller) \ + std::string FunctionName(const std::string& filepath) { \ + if (JMethodID == nullptr) { \ + return 0; \ + } \ + auto env = GetEnvForThread(); \ + jstring j_filepath = env->NewStringUTF(filepath.c_str()); \ + jstring j_return = \ + static_cast(env->Caller(native_library, JMethodID, j_filepath)); \ + if (!j_return) { \ + return {}; \ + } \ + const jchar* jchars = env->GetStringChars(j_return, nullptr); \ + const jsize length = env->GetStringLength(j_return); \ + const std::u16string_view string_view(reinterpret_cast(jchars), length); \ + const std::string converted_string = Common::UTF16ToUTF8(string_view); \ + env->ReleaseStringChars(j_return, jchars); \ + return converted_string; \ + } +ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) +#undef F +#undef FH + } // namespace Common::FS::Android diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h index b441c2a12..2c9234313 100644 --- a/src/common/fs/fs_android.h +++ b/src/common/fs/fs_android.h @@ -17,19 +17,28 @@ "(Ljava/lang/String;)Z") \ V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z") +#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \ + V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \ + "(Ljava/lang/String;)Ljava/lang/String;") \ + V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \ + "(Ljava/lang/String;)Ljava/lang/String;") + namespace Common::FS::Android { static JavaVM* g_jvm = nullptr; static jclass native_library = nullptr; +#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) #define F(JMethodID) static jmethodID JMethodID = nullptr; +ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) ANDROID_STORAGE_FUNCTIONS(FS) #undef F #undef FS #undef FR +#undef FH enum class OpenMode { Read, @@ -62,4 +71,10 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) #undef F #undef FR +#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName) +#define F(FunctionName) std::string FunctionName(const std::string& filepath); +ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) +#undef F +#undef FH + } // namespace Common::FS::Android diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 0c4c88cde..c3a81f9a9 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -401,6 +401,16 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se } std::string_view GetParentPath(std::string_view path) { + if (path.empty()) { + return path; + } + +#ifdef ANDROID + if (path[0] != '/') { + std::string path_string{path}; + return FS::Android::GetParentDirectory(path_string); + } +#endif const auto name_bck_index = path.rfind('\\'); const auto name_fwd_index = path.rfind('/'); std::size_t name_index; diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 4c7aba3f5..72c481798 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -14,6 +14,10 @@ #include #endif +#ifdef ANDROID +#include +#endif + namespace Common { /// Make a string lowercase @@ -63,6 +67,14 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ if (full_path.empty()) return false; +#ifdef ANDROID + if (full_path[0] != '/') { + *_pPath = Common::FS::Android::GetParentDirectory(full_path); + *_pFilename = Common::FS::Android::GetFilename(full_path); + return true; + } +#endif + std::size_t dir_end = full_path.find_last_of("/" // windows needs the : included for something like just "C:" to be considered a directory #ifdef _WIN32 -- cgit v1.2.3 From f04bc172ae4a24ae4431d65eabfedcc8667eb0bd Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sun, 29 Oct 2023 12:43:09 -0400 Subject: android: FileUtil: Add option to suppress log for native exists() calls We often check for the existence of files that only exist in ExeFS so this can spam logcat with useless messages when scanning for games. --- src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | 2 +- src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') 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 5fe235dba..e2c5b6acd 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 @@ -92,7 +92,7 @@ object NativeLibrary { return if (DocumentsTree.isNativePath(path!!)) { YuzuApplication.documentsTree!!.exists(path) } else { - FileUtil.exists(path) + FileUtil.exists(path, suppressLog = true) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt index 5ee74a52c..8c3268e9c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt @@ -144,7 +144,7 @@ object FileUtil { * @param path Native content uri path * @return bool */ - fun exists(path: String?): Boolean { + fun exists(path: String?, suppressLog: Boolean = false): Boolean { var c: Cursor? = null try { val mUri = Uri.parse(path) @@ -152,7 +152,9 @@ object FileUtil { c = context.contentResolver.query(mUri, columns, null, null, null) return c!!.count > 0 } catch (e: Exception) { - Log.info("[FileUtil] Cannot find file from given path, error: " + e.message) + if (!suppressLog) { + Log.info("[FileUtil] Cannot find file from given path, error: " + e.message) + } } finally { closeQuietly(c) } -- cgit v1.2.3 From e867768316a337dd6b226a1ac14452bc1fdc725a Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Mon, 30 Oct 2023 13:22:16 -0400 Subject: android: Simplify game card layout Using a material card view to shape the image was just a waste of a layout pass. A shapeable image view does what we want and does it faster. --- src/android/app/src/main/res/layout/card_game.xml | 45 +++++++++-------------- 1 file changed, 18 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/android/app/src/main/res/layout/card_game.xml b/src/android/app/src/main/res/layout/card_game.xml index 1f5de219b..6340171ec 100644 --- a/src/android/app/src/main/res/layout/card_game.xml +++ b/src/android/app/src/main/res/layout/card_game.xml @@ -1,63 +1,54 @@ - + app:cardCornerRadius="4dp" + app:cardElevation="0dp"> - - - - - + app:layout_constraintTop_toTopOf="parent" + app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.ExtraSmall" + tools:src="@drawable/default_icon" /> -- cgit v1.2.3 From f7755df2af7942ddcec09110ff7489cd3792fbda Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Mon, 30 Oct 2023 11:27:19 -0400 Subject: android: Reorder controller indexes and only use controllers Before we could ignore controller inputs by forwarding them to player two if a non-controller was connected before and recognized as an input device. --- .../yuzu/yuzu_emu/activities/EmulationActivity.kt | 3 ++ .../java/org/yuzu/yuzu_emu/utils/InputHandler.kt | 53 +++++++++++++++++++--- 2 files changed, 50 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 0eda27f7d..f37875ffe 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -64,6 +64,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { private var motionTimestamp: Long = 0 private var flipMotionOrientation: Boolean = false + private var controllerIds = InputHandler.getGameControllerIds() + private val actionPause = "ACTION_EMULATOR_PAUSE" private val actionPlay = "ACTION_EMULATOR_PLAY" private val actionMute = "ACTION_EMULATOR_MUTE" @@ -155,6 +157,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { super.onResume() nfcReader.startScanning() startMotionSensorListener() + InputHandler.updateControllerIds() buildPictureInPictureParams() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt index fec40e27d..fc6a8b5cb 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt @@ -3,17 +3,24 @@ package org.yuzu.yuzu_emu.utils +import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent import kotlin.math.sqrt import org.yuzu.yuzu_emu.NativeLibrary object InputHandler { + private var controllerIds = getGameControllerIds() + fun initialize() { // Connect first controller NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device)) } + fun updateControllerIds() { + controllerIds = getGameControllerIds() + } + fun dispatchKeyEvent(event: KeyEvent): Boolean { val button: Int = when (event.device.vendorId) { 0x045E -> getInputXboxButtonKey(event.keyCode) @@ -35,7 +42,7 @@ object InputHandler { } return NativeLibrary.onGamePadButtonEvent( - getPlayerNumber(event.device.controllerNumber), + getPlayerNumber(event.device.controllerNumber, event.deviceId), button, action ) @@ -58,9 +65,14 @@ object InputHandler { return true } - private fun getPlayerNumber(index: Int): Int { + private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int { + var deviceIndex = index + if (deviceId != -1) { + deviceIndex = controllerIds[deviceId]!! + } + // TODO: Joycons are handled as different controllers. Find a way to merge them. - return when (index) { + return when (deviceIndex) { 2 -> NativeLibrary.Player2Device 3 -> NativeLibrary.Player3Device 4 -> NativeLibrary.Player4Device @@ -238,7 +250,7 @@ object InputHandler { } private fun setGenericAxisInput(event: MotionEvent, axis: Int) { - val playerNumber = getPlayerNumber(event.device.controllerNumber) + val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) when (axis) { MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> @@ -297,7 +309,7 @@ object InputHandler { private fun setJoyconAxisInput(event: MotionEvent, axis: Int) { // Joycon support is half dead. Right joystick doesn't work - val playerNumber = getPlayerNumber(event.device.controllerNumber) + val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) when (axis) { MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> @@ -325,7 +337,7 @@ object InputHandler { } private fun setRazerAxisInput(event: MotionEvent, axis: Int) { - val playerNumber = getPlayerNumber(event.device.controllerNumber) + val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) when (axis) { MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> @@ -362,4 +374,33 @@ object InputHandler { ) } } + + fun getGameControllerIds(): Map { + val gameControllerDeviceIds = mutableMapOf() + val deviceIds = InputDevice.getDeviceIds() + var controllerSlot = 1 + deviceIds.forEach { deviceId -> + InputDevice.getDevice(deviceId)?.apply { + // Don't over-assign controllers + if (controllerSlot >= 8) { + return gameControllerDeviceIds + } + + // Verify that the device has gamepad buttons, control sticks, or both. + if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD || + sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK + ) { + // This device is a game controller. Store its device ID. + if (deviceId and id and vendorId and productId != 0) { + // Additionally filter out devices that have no ID + gameControllerDeviceIds + .takeIf { !it.contains(deviceId) } + ?.put(deviceId, controllerSlot) + controllerSlot++ + } + } + } + } + return gameControllerDeviceIds + } } -- cgit v1.2.3 From 6a7123826a426ed195257da24d75170bfc56f670 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 25 Oct 2023 21:26:56 -0400 Subject: qt: remove duplicate exit confirmation setting --- src/yuzu/configuration/shared_translation.cpp | 1 - src/yuzu/main.cpp | 12 +++++++++--- src/yuzu/uisettings.h | 4 ---- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 3fe448f27..1434b1a56 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -156,7 +156,6 @@ std::unique_ptr InitializeTranslations(QWidget* parent) { // Ui General INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); - INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 0df163029..2b430c40a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2174,6 +2174,7 @@ void GMainWindow::ShutdownGame() { return; } + play_time_manager->Stop(); OnShutdownBegin(); OnEmulationStopTimeExpired(); OnEmulationStopped(); @@ -3484,7 +3485,7 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) { } void GMainWindow::OnExit() { - OnStopGame(); + ShutdownGame(); } void GMainWindow::OnSaveConfig() { @@ -4847,7 +4848,12 @@ bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installe } bool GMainWindow::ConfirmClose() { - if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { + if (emu_thread == nullptr || + UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) { + return true; + } + if (!system->GetExitLocked() && + UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) { return true; } const auto text = tr("Are you sure you want to close yuzu?"); @@ -4952,7 +4958,7 @@ bool GMainWindow::ConfirmChangeGame() { } bool GMainWindow::ConfirmForceLockedExit() { - if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { + if (emu_thread == nullptr) { return true; } const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index b62ff620c..77d992c54 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -93,10 +93,6 @@ struct Values { Setting show_filter_bar{linkage, true, "showFilterBar", Category::Ui}; Setting show_status_bar{linkage, true, "showStatusBar", Category::Ui}; - Setting confirm_before_closing{ - linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default, - true, true}; - SwitchableSetting confirm_before_stopping{linkage, ConfirmStop::Ask_Always, "confirmStop", -- cgit v1.2.3 From 361dbdddcc001937d4ece558ce3aa8701180d11f Mon Sep 17 00:00:00 2001 From: Dzmitry Dubrova Date: Tue, 31 Oct 2023 15:45:11 +0300 Subject: service: am: Add support for LLE Software Keyboard Applet --- src/core/hle/service/am/am.cpp | 79 ++++++++++++++++++++++++++++++++++++++++-- src/core/hle/service/am/am.h | 3 ++ 2 files changed, 80 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index ff067c8d9..c80b29928 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -23,6 +23,7 @@ #include "core/hle/service/am/applets/applet_cabinet.h" #include "core/hle/service/am/applets/applet_mii_edit_types.h" #include "core/hle/service/am/applets/applet_profile_select.h" +#include "core/hle/service/am/applets/applet_software_keyboard_types.h" #include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/am/idle.h" @@ -1562,7 +1563,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) {16, nullptr, "GetMainAppletStorageId"}, {17, nullptr, "GetCallerAppletIdentityInfoStack"}, {18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"}, - {19, nullptr, "GetDesirableKeyboardLayout"}, + {19, &ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout, "GetDesirableKeyboardLayout"}, {20, nullptr, "PopExtraStorage"}, {25, nullptr, "GetPopExtraStorageEvent"}, {30, nullptr, "UnpopInData"}, @@ -1581,7 +1582,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) {120, nullptr, "GetLaunchStorageInfoForDebug"}, {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, {140, nullptr, "SetApplicationMemoryReservation"}, - {150, nullptr, "ShouldSetGpuTimeSliceManually"}, + {150, &ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually, "ShouldSetGpuTimeSliceManually"}, }; // clang-format on RegisterHandlers(functions); @@ -1596,6 +1597,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) case Applets::AppletId::PhotoViewer: PushInShowAlbum(); break; + case Applets::AppletId::SoftwareKeyboard: + PushInShowSoftwareKeyboard(); + break; default: break; } @@ -1672,6 +1676,14 @@ void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& rb.PushRaw(applet_info); } +void ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) { const Service::Account::ProfileManager manager{}; bool is_empty{true}; @@ -1691,6 +1703,14 @@ void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& rb.Push(user_count); } +void ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.Push(0); +} + void ILibraryAppletSelfAccessor::PushInShowAlbum() { const Applets::CommonArguments arguments{ .arguments_version = Applets::CommonArgumentVersion::Version3, @@ -1759,6 +1779,61 @@ void ILibraryAppletSelfAccessor::PushInShowMiiEditData() { queue_data.emplace_back(std::move(argument_data)); } +void ILibraryAppletSelfAccessor::PushInShowSoftwareKeyboard() { + const Applets::CommonArguments arguments{ + .arguments_version = Applets::CommonArgumentVersion::Version3, + .size = Applets::CommonArgumentSize::Version3, + .library_version = static_cast(Applets::SwkbdAppletVersion::Version524301), + .theme_color = Applets::ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + std::vector initial_string(0); + + const Applets::SwkbdConfigCommon swkbd_config{ + .type = Applets::SwkbdType::Qwerty, + .ok_text{}, + .left_optional_symbol_key{}, + .right_optional_symbol_key{}, + .use_prediction = false, + .key_disable_flags{}, + .initial_cursor_position = Applets::SwkbdInitialCursorPosition::Start, + .header_text{}, + .sub_text{}, + .guide_text{}, + .max_text_length = 500, + .min_text_length = 0, + .password_mode = Applets::SwkbdPasswordMode::Disabled, + .text_draw_type = Applets::SwkbdTextDrawType::Box, + .enable_return_button = true, + .use_utf8 = false, + .use_blur_background = true, + .initial_string_offset{}, + .initial_string_length = static_cast(initial_string.size()), + .user_dictionary_offset{}, + .user_dictionary_entries{}, + .use_text_check = false, + }; + + Applets::SwkbdConfigNew swkbd_config_new{}; + + std::vector argument_data(sizeof(arguments)); + std::vector swkbd_data(sizeof(swkbd_config) + sizeof(swkbd_config_new)); + std::vector work_buffer(swkbd_config.initial_string_length * sizeof(char16_t)); + + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + std::memcpy(swkbd_data.data(), &swkbd_config, sizeof(swkbd_config)); + std::memcpy(swkbd_data.data() + sizeof(swkbd_config), &swkbd_config_new, + sizeof(Applets::SwkbdConfigNew)); + std::memcpy(work_buffer.data(), initial_string.data(), + swkbd_config.initial_string_length * sizeof(char16_t)); + + queue_data.emplace_back(std::move(argument_data)); + queue_data.emplace_back(std::move(swkbd_data)); + queue_data.emplace_back(std::move(work_buffer)); +} + IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_) : ServiceFramework{system_, "IAppletCommonFunctions"} { // clang-format off diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 64b3f3fe2..8f8cb8a9e 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -347,11 +347,14 @@ private: void GetLibraryAppletInfo(HLERequestContext& ctx); void ExitProcessAndReturn(HLERequestContext& ctx); void GetCallerAppletIdentityInfo(HLERequestContext& ctx); + void GetDesirableKeyboardLayout(HLERequestContext& ctx); void GetMainAppletAvailableUsers(HLERequestContext& ctx); + void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx); void PushInShowAlbum(); void PushInShowCabinetData(); void PushInShowMiiEditData(); + void PushInShowSoftwareKeyboard(); std::deque> queue_data; }; -- cgit v1.2.3 From e8cb8b2668c86ddad527cb8ff7de7f992080dece Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Mon, 30 Oct 2023 19:29:00 -0400 Subject: android: Implement applet launcher --- .../main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | 32 +++++- .../org/yuzu/yuzu_emu/adapters/AppletAdapter.kt | 90 ++++++++++++++++ .../adapters/CabinetLauncherDialogAdapter.kt | 72 +++++++++++++ .../yuzu_emu/fragments/AppletLauncherFragment.kt | 113 +++++++++++++++++++++ .../fragments/CabinetLauncherDialogFragment.kt | 41 ++++++++ .../yuzu_emu/fragments/HomeSettingsFragment.kt | 15 +++ .../yuzu_emu/fragments/MessageDialogFragment.kt | 5 +- .../main/java/org/yuzu/yuzu_emu/model/Applet.kt | 55 ++++++++++ .../src/main/java/org/yuzu/yuzu_emu/model/Game.kt | 10 +- .../java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 3 +- .../yuzu/yuzu_emu/utils/DirectoryInitialization.kt | 2 +- src/android/app/src/main/jni/native.cpp | 45 ++++++++ src/android/app/src/main/res/drawable/ic_album.xml | 9 ++ .../app/src/main/res/drawable/ic_applet.xml | 9 ++ src/android/app/src/main/res/drawable/ic_edit.xml | 9 ++ src/android/app/src/main/res/drawable/ic_mii.xml | 18 ++++ .../app/src/main/res/drawable/ic_refresh.xml | 9 ++ .../app/src/main/res/drawable/ic_restore.xml | 9 ++ .../app/src/main/res/layout/card_applet_option.xml | 57 +++++++++++ .../app/src/main/res/layout/dialog_list.xml | 15 +++ .../app/src/main/res/layout/dialog_list_item.xml | 30 ++++++ .../main/res/layout/fragment_applet_launcher.xml | 31 ++++++ .../src/main/res/navigation/home_navigation.xml | 15 +++ src/android/app/src/main/res/values/strings.xml | 18 ++++ 24 files changed, 703 insertions(+), 9 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CabinetLauncherDialogFragment.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Applet.kt create mode 100644 src/android/app/src/main/res/drawable/ic_album.xml create mode 100644 src/android/app/src/main/res/drawable/ic_applet.xml create mode 100644 src/android/app/src/main/res/drawable/ic_edit.xml create mode 100644 src/android/app/src/main/res/drawable/ic_mii.xml create mode 100644 src/android/app/src/main/res/drawable/ic_refresh.xml create mode 100644 src/android/app/src/main/res/drawable/ic_restore.xml create mode 100644 src/android/app/src/main/res/layout/card_applet_option.xml create mode 100644 src/android/app/src/main/res/layout/dialog_list.xml create mode 100644 src/android/app/src/main/res/layout/dialog_list_item.xml create mode 100644 src/android/app/src/main/res/layout/fragment_applet_launcher.xml (limited to 'src') 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 e2c5b6acd..07f1b4842 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 @@ -252,7 +252,7 @@ object NativeLibrary { external fun reloadKeys(): Boolean - external fun initializeEmulation() + external fun initializeSystem() external fun defaultCPUCore(): Int @@ -505,6 +505,36 @@ object NativeLibrary { */ external fun initializeEmptyUserDirectory() + /** + * Gets the launch path for a given applet. It is the caller's responsibility to also + * set the system's current applet ID before trying to launch the nca given by this function. + * + * @param id The applet entry ID + * @return The applet's launch path + */ + external fun getAppletLaunchPath(id: Long): String + + /** + * Sets the system's current applet ID before launching. + * + * @param appletId One of the ids in the Service::AM::Applets::AppletId enum + */ + external fun setCurrentAppletId(appletId: Int) + + /** + * Sets the cabinet mode for launching the cabinet applet. + * + * @param cabinetMode One of the modes that corresponds to the enum in Service::NFP::CabinetMode + */ + external fun setCabinetMode(cabinetMode: Int) + + /** + * Checks whether NAND contents are available and valid. + * + * @return 'true' if firmware is available + */ + external fun isFirmwareAvailable(): Boolean + /** * Button type for use in onTouchEvent */ diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt new file mode 100644 index 000000000..a21a705c1 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.adapters + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.content.res.ResourcesCompat +import androidx.fragment.app.FragmentActivity +import androidx.navigation.findNavController +import androidx.recyclerview.widget.RecyclerView +import org.yuzu.yuzu_emu.HomeNavigationDirections +import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.databinding.CardAppletOptionBinding +import org.yuzu.yuzu_emu.model.Applet +import org.yuzu.yuzu_emu.model.AppletInfo +import org.yuzu.yuzu_emu.model.Game + +class AppletAdapter(val activity: FragmentActivity, var applets: List) : + RecyclerView.Adapter(), + View.OnClickListener { + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): AppletAdapter.AppletViewHolder { + CardAppletOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .apply { root.setOnClickListener(this@AppletAdapter) } + .also { return AppletViewHolder(it) } + } + + override fun onBindViewHolder(holder: AppletViewHolder, position: Int) = + holder.bind(applets[position]) + + override fun getItemCount(): Int = applets.size + + override fun onClick(view: View) { + val applet = (view.tag as AppletViewHolder).applet + val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId) + if (appletPath.isEmpty()) { + Toast.makeText( + YuzuApplication.appContext, + R.string.applets_error_applet, + Toast.LENGTH_SHORT + ).show() + return + } + + if (applet.appletInfo == AppletInfo.Cabinet) { + view.findNavController() + .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment) + return + } + + NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId) + val appletGame = Game( + title = YuzuApplication.appContext.getString(applet.titleId), + path = appletPath + ) + val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) + view.findNavController().navigate(action) + } + + inner class AppletViewHolder(val binding: CardAppletOptionBinding) : + RecyclerView.ViewHolder(binding.root) { + lateinit var applet: Applet + + init { + itemView.tag = this + } + + fun bind(applet: Applet) { + this.applet = applet + + binding.title.setText(applet.titleId) + binding.description.setText(applet.descriptionId) + binding.icon.setImageDrawable( + ResourcesCompat.getDrawable( + binding.icon.context.resources, + applet.iconId, + binding.icon.context.theme + ) + ) + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt new file mode 100644 index 000000000..e7b7c0f2f --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.adapters + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.res.ResourcesCompat +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.RecyclerView +import org.yuzu.yuzu_emu.HomeNavigationDirections +import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.databinding.DialogListItemBinding +import org.yuzu.yuzu_emu.model.CabinetMode +import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder +import org.yuzu.yuzu_emu.model.AppletInfo +import org.yuzu.yuzu_emu.model.Game + +class CabinetLauncherDialogAdapter(val fragment: Fragment) : + RecyclerView.Adapter(), + View.OnClickListener { + private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder { + DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) } + .also { return CabinetModeViewHolder(it) } + } + + override fun getItemCount(): Int = cabinetModes.size + + override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) = + holder.bind(cabinetModes[position]) + + override fun onClick(view: View) { + val mode = (view.tag as CabinetModeViewHolder).cabinetMode + val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId) + NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId) + NativeLibrary.setCabinetMode(mode.id) + val appletGame = Game( + title = YuzuApplication.appContext.getString(R.string.cabinet_applet), + path = appletPath + ) + val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) + fragment.findNavController().navigate(action) + } + + inner class CabinetModeViewHolder(val binding: DialogListItemBinding) : + RecyclerView.ViewHolder(binding.root) { + lateinit var cabinetMode: CabinetMode + + init { + itemView.tag = this + } + + fun bind(cabinetMode: CabinetMode) { + this.cabinetMode = cabinetMode + binding.icon.setImageDrawable( + ResourcesCompat.getDrawable( + binding.icon.context.resources, + cabinetMode.iconId, + binding.icon.context.theme + ) + ) + binding.title.setText(cabinetMode.titleId) + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt new file mode 100644 index 000000000..1f66b440d --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.navigation.findNavController +import androidx.recyclerview.widget.GridLayoutManager +import com.google.android.material.transition.MaterialSharedAxis +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.adapters.AppletAdapter +import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding +import org.yuzu.yuzu_emu.model.Applet +import org.yuzu.yuzu_emu.model.AppletInfo +import org.yuzu.yuzu_emu.model.HomeViewModel + +class AppletLauncherFragment : Fragment() { + private var _binding: FragmentAppletLauncherBinding? = null + private val binding get() = _binding!! + + private val homeViewModel: HomeViewModel by activityViewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) + returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentAppletLauncherBinding.inflate(inflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + homeViewModel.setNavigationVisibility(visible = false, animated = true) + homeViewModel.setStatusBarShadeVisibility(visible = false) + + binding.toolbarApplets.setNavigationOnClickListener { + binding.root.findNavController().popBackStack() + } + + val applets = listOf( + Applet( + R.string.album_applet, + R.string.album_applet_description, + R.drawable.ic_album, + AppletInfo.PhotoViewer + ), + Applet( + R.string.cabinet_applet, + R.string.cabinet_applet_description, + R.drawable.ic_nfc, + AppletInfo.Cabinet + ), + Applet( + R.string.mii_edit_applet, + R.string.mii_edit_applet_description, + R.drawable.ic_mii, + AppletInfo.MiiEdit + ) + ) + + binding.listApplets.apply { + layoutManager = GridLayoutManager( + requireContext(), + resources.getInteger(R.integer.grid_columns) + ) + adapter = AppletAdapter(requireActivity(), applets) + } + + setInsets() + } + + private fun setInsets() = + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { _: View, windowInsets: WindowInsetsCompat -> + val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) + + val leftInsets = barInsets.left + cutoutInsets.left + val rightInsets = barInsets.right + cutoutInsets.right + + val mlpAppBar = binding.toolbarApplets.layoutParams as ViewGroup.MarginLayoutParams + mlpAppBar.leftMargin = leftInsets + mlpAppBar.rightMargin = rightInsets + binding.toolbarApplets.layoutParams = mlpAppBar + + val mlpListApplets = + binding.listApplets.layoutParams as ViewGroup.MarginLayoutParams + mlpListApplets.leftMargin = leftInsets + mlpListApplets.rightMargin = rightInsets + binding.listApplets.layoutParams = mlpListApplets + + binding.listApplets.updatePadding(bottom = barInsets.bottom) + + windowInsets + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CabinetLauncherDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CabinetLauncherDialogFragment.kt new file mode 100644 index 000000000..5933677fd --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CabinetLauncherDialogFragment.kt @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.fragments + +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter +import org.yuzu.yuzu_emu.databinding.DialogListBinding + +class CabinetLauncherDialogFragment : DialogFragment() { + private lateinit var binding: DialogListBinding + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogListBinding.inflate(layoutInflater) + binding.dialogList.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = CabinetLauncherDialogAdapter(this@CabinetLauncherDialogFragment) + } + + return MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.cabinet_launcher) + .setView(binding.root) + .create() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return binding.root + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index f273c880a..6e19fc6c0 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 @@ -30,6 +30,7 @@ import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialSharedAxis import org.yuzu.yuzu_emu.BuildConfig import org.yuzu.yuzu_emu.HomeNavigationDirections +import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding @@ -131,6 +132,20 @@ class HomeSettingsFragment : Fragment() { } ) ) + add( + HomeSetting( + R.string.applets, + R.string.applets_description, + R.drawable.ic_applet, + { + binding.root.findNavController() + .navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment) + }, + { NativeLibrary.isFirmwareAvailable() }, + R.string.applets_error_firmware, + R.string.applets_error_description + ) + ) add( HomeSetting( R.string.select_games_folder, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt index 541b22f47..a6183d19e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt @@ -8,6 +8,7 @@ import android.content.DialogInterface import android.content.Intent import android.net.Uri import android.os.Bundle +import android.text.Html import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentActivity import androidx.fragment.app.activityViewModels @@ -32,7 +33,9 @@ class MessageDialogFragment : DialogFragment() { if (titleId != 0) dialog.setTitle(titleId) if (titleString.isNotEmpty()) dialog.setTitle(titleString) - if (descriptionId != 0) dialog.setMessage(descriptionId) + if (descriptionId != 0) { + dialog.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY)) + } if (descriptionString.isNotEmpty()) dialog.setMessage(descriptionString) if (helpLinkId != 0) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Applet.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Applet.kt new file mode 100644 index 000000000..8677674a3 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Applet.kt @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.model + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import org.yuzu.yuzu_emu.R + +data class Applet( + @StringRes val titleId: Int, + @StringRes val descriptionId: Int, + @DrawableRes val iconId: Int, + val appletInfo: AppletInfo, + val cabinetMode: CabinetMode = CabinetMode.None +) + +// Combination of Common::AM::Applets::AppletId enum and the entry id +enum class AppletInfo(val appletId: Int, val entryId: Long = 0) { + None(0x00), + Application(0x01), + OverlayDisplay(0x02), + QLaunch(0x03), + Starter(0x04), + Auth(0x0A), + Cabinet(0x0B, 0x0100000000001002), + Controller(0x0C), + DataErase(0x0D), + Error(0x0E), + NetConnect(0x0F), + ProfileSelect(0x10), + SoftwareKeyboard(0x11), + MiiEdit(0x12, 0x0100000000001009), + Web(0x13), + Shop(0x14), + PhotoViewer(0x015, 0x010000000000100D), + Settings(0x16), + OfflineWeb(0x17), + LoginShare(0x18), + WebAuth(0x19), + MyPage(0x1A) +} + +// Matches enum in Service::NFP::CabinetMode with extra metadata +enum class CabinetMode( + val id: Int, + @StringRes val titleId: Int = 0, + @DrawableRes val iconId: Int = 0 +) { + None(-1), + StartNicknameAndOwnerSettings(0, R.string.cabinet_nickname_and_owner, R.drawable.ic_edit), + StartGameDataEraser(1, R.string.cabinet_game_data_eraser, R.drawable.ic_refresh), + StartRestorer(2, R.string.cabinet_restorer, R.drawable.ic_restore), + StartFormatter(3, R.string.cabinet_formatter, R.drawable.ic_clear) +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index b43978fce..de84b2adb 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt @@ -11,12 +11,12 @@ import kotlinx.serialization.Serializable @Parcelize @Serializable class Game( - val title: String, + val title: String = "", val path: String, - val programId: String, - val developer: String, - val version: String, - val isHomebrew: Boolean + val programId: String = "", + val developer: String = "", + val version: String = "", + val isHomebrew: Boolean = false ) : Parcelable { val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime" val keyLastPlayedTime get() = "${programId}_LastPlayed" diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index 233aa4101..ba1177426 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -403,6 +403,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } else { firmwarePath.deleteRecursively() cacheFirmwareDir.copyRecursively(firmwarePath, true) + NativeLibrary.initializeSystem() getString(R.string.save_file_imported_success) } } catch (e: Exception) { @@ -648,7 +649,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } // Reinitialize relevant data - NativeLibrary.initializeEmulation() + NativeLibrary.initializeSystem() gamesViewModel.reloadGames(false) return@newInstance getString(R.string.user_data_import_success) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt index 3c9f6bad0..79a07f7ef 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt @@ -15,7 +15,7 @@ object DirectoryInitialization { fun start() { if (!areDirectoriesReady) { initializeInternalStorage() - NativeLibrary.initializeEmulation() + NativeLibrary.initializeSystem() areDirectoriesReady = true } } diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 686b73588..f7931a89d 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -755,4 +755,49 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv* } } +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, jclass clazz, + jlong jid) { + auto bis_system = + EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents(); + if (!bis_system) { + return ToJString(env, ""); + } + + auto applet_nca = + bis_system->GetEntry(static_cast(jid), FileSys::ContentRecordType::Program); + if (!applet_nca) { + return ToJString(env, ""); + } + + return ToJString(env, applet_nca->GetFullPath()); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz, + jint jappletId) { + EmulationSession::GetInstance().System().GetAppletManager().SetCurrentAppletId( + static_cast(jappletId)); +} + +void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass clazz, + jint jcabinetMode) { + EmulationSession::GetInstance().System().GetAppletManager().SetCabinetMode( + static_cast(jcabinetMode)); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env, jclass clazz) { + auto bis_system = + EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents(); + if (!bis_system) { + return false; + } + + // Query an applet to see if it's available + auto applet_nca = + bis_system->GetEntry(0x010000000000100Dull, FileSys::ContentRecordType::Program); + if (!applet_nca) { + return false; + } + return true; +} + } // extern "C" diff --git a/src/android/app/src/main/res/drawable/ic_album.xml b/src/android/app/src/main/res/drawable/ic_album.xml new file mode 100644 index 000000000..f2b63813f --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_album.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/android/app/src/main/res/drawable/ic_applet.xml b/src/android/app/src/main/res/drawable/ic_applet.xml new file mode 100644 index 000000000..b154e6f56 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_applet.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/android/app/src/main/res/drawable/ic_edit.xml b/src/android/app/src/main/res/drawable/ic_edit.xml new file mode 100644 index 000000000..ac22ce8a5 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/android/app/src/main/res/drawable/ic_mii.xml b/src/android/app/src/main/res/drawable/ic_mii.xml new file mode 100644 index 000000000..1271ec401 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_mii.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/src/android/app/src/main/res/drawable/ic_refresh.xml b/src/android/app/src/main/res/drawable/ic_refresh.xml new file mode 100644 index 000000000..d0d87ecc2 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_refresh.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/android/app/src/main/res/drawable/ic_restore.xml b/src/android/app/src/main/res/drawable/ic_restore.xml new file mode 100644 index 000000000..d6d9d4017 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_restore.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/android/app/src/main/res/layout/card_applet_option.xml b/src/android/app/src/main/res/layout/card_applet_option.xml new file mode 100644 index 000000000..19fbec9f1 --- /dev/null +++ b/src/android/app/src/main/res/layout/card_applet_option.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/layout/dialog_list.xml b/src/android/app/src/main/res/layout/dialog_list.xml new file mode 100644 index 000000000..7de2b2c3a --- /dev/null +++ b/src/android/app/src/main/res/layout/dialog_list.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/android/app/src/main/res/layout/dialog_list_item.xml b/src/android/app/src/main/res/layout/dialog_list_item.xml new file mode 100644 index 000000000..39f3558ff --- /dev/null +++ b/src/android/app/src/main/res/layout/dialog_list_item.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/src/android/app/src/main/res/layout/fragment_applet_launcher.xml b/src/android/app/src/main/res/layout/fragment_applet_launcher.xml new file mode 100644 index 000000000..fe8fae40f --- /dev/null +++ b/src/android/app/src/main/res/layout/fragment_applet_launcher.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml index 82749359d..6d4c1f86d 100644 --- a/src/android/app/src/main/res/navigation/home_navigation.xml +++ b/src/android/app/src/main/res/navigation/home_navigation.xml @@ -25,6 +25,9 @@ + + + + + diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 9e4854221..b92978140 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -124,6 +124,24 @@ Share save file Failed to export save + + Applet launcher + Launch system applets using installed firmware + Firmware not installed + Applet not available + prod.keys file and firmware are installed and try again.]]> + Album + See images stored in the user screenshots folder with the system photo viewer + Mii edit + View and edit Miis with the system editor + Cabinet + Edit and delete data stored on amiibo + Cabinet launcher + Nickname and owner settings + Game data eraser + Restorer + Formatter + Gaia isn\'t real Copied to clipboard -- cgit v1.2.3 From 133788d0d4c12df7d7e39c4962cadadc781c596c Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Tue, 31 Oct 2023 02:23:57 -0400 Subject: android: Initialize filesystem components during application start --- src/android/app/src/main/jni/native.cpp | 22 +++++++++++++--------- src/android/app/src/main/jni/native.h | 1 + 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index f7931a89d..0e458df38 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -247,6 +247,17 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) } } +void EmulationSession::InitializeSystem() { + // Initialize filesystem. + m_system.SetFilesystem(m_vfs); + m_system.GetUserChannel().clear(); + m_manual_provider = std::make_unique(); + m_system.SetContentProvider(std::make_unique()); + m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, + m_manual_provider.get()); + m_system.GetFileSystemController().CreateFactories(*m_vfs); +} + Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) { std::scoped_lock lock(m_mutex); @@ -254,9 +265,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string m_window = std::make_unique(&m_input_subsystem, m_native_window, m_vulkan_library); - m_system.SetFilesystem(m_vfs); - m_system.GetUserChannel().clear(); - // Initialize system. jauto android_keyboard = std::make_unique(); m_software_keyboard = android_keyboard.get(); @@ -277,11 +285,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string }); // Initialize filesystem. - m_manual_provider = std::make_unique(); - m_system.SetContentProvider(std::make_unique()); - m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, - m_manual_provider.get()); - m_system.GetFileSystemController().CreateFactories(*m_vfs); ConfigureFilesystemProvider(filepath); // Initialize account manager @@ -663,11 +666,12 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c } } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz) { // Create the default config.ini. Config{}; // Initialize the emulated system. EmulationSession::GetInstance().System().Initialize(); + EmulationSession::GetInstance().InitializeSystem(); } jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) { diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index b1db87e41..0aa2b085b 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h @@ -43,6 +43,7 @@ public: const Core::PerfStatsResults& PerfStats() const; void ConfigureFilesystemProvider(const std::string& filepath); + void InitializeSystem(); Core::SystemResultStatus InitializeEmulation(const std::string& filepath); bool IsHandheldOnly(); -- cgit v1.2.3 From 1d7ff850d6fe57540f838fa32c5f183139198d3e Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Tue, 31 Oct 2023 20:19:33 -0400 Subject: android: Update translations from transifex --- src/android/app/src/main/res/values-ar/strings.xml | 385 ++++++++++++++++++++ .../app/src/main/res/values-ckb/strings.xml | 336 +++++++++++++++++ src/android/app/src/main/res/values-de/strings.xml | 119 ++++-- src/android/app/src/main/res/values-es/strings.xml | 180 ++++++--- src/android/app/src/main/res/values-fr/strings.xml | 180 ++++++--- src/android/app/src/main/res/values-he/strings.xml | 367 +++++++++++++++++++ src/android/app/src/main/res/values-hu/strings.xml | 402 +++++++++++++++++++++ src/android/app/src/main/res/values-it/strings.xml | 192 +++++++--- src/android/app/src/main/res/values-ja/strings.xml | 218 +++++++---- src/android/app/src/main/res/values-ko/strings.xml | 273 +++++++------- src/android/app/src/main/res/values-nb/strings.xml | 113 +++--- src/android/app/src/main/res/values-pl/strings.xml | 81 +++-- .../app/src/main/res/values-pt-rBR/strings.xml | 210 ++++++++--- .../app/src/main/res/values-pt-rPT/strings.xml | 192 +++++++--- src/android/app/src/main/res/values-ru/strings.xml | 159 ++++++-- src/android/app/src/main/res/values-uk/strings.xml | 90 +---- src/android/app/src/main/res/values-vi/strings.xml | 340 +++++++++++++++++ .../app/src/main/res/values-zh-rCN/strings.xml | 138 +++++-- .../app/src/main/res/values-zh-rTW/strings.xml | 137 ++++++- 19 files changed, 3425 insertions(+), 687 deletions(-) create mode 100644 src/android/app/src/main/res/values-ar/strings.xml create mode 100644 src/android/app/src/main/res/values-ckb/strings.xml create mode 100644 src/android/app/src/main/res/values-he/strings.xml create mode 100644 src/android/app/src/main/res/values-hu/strings.xml create mode 100644 src/android/app/src/main/res/values-vi/strings.xml (limited to 'src') diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml new file mode 100644 index 000000000..07dffffe8 --- /dev/null +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -0,0 +1,385 @@ + + + + المحاكي نشط + اظهار اشعار دائم عندما يكون المحاكي نشطاً + يوزو يعمل + الإشعارات والأخطاء + اظهار اشعار عند حصول اي مشكلة. + لم يتم منح إذن الإشعار + + + مرحبًا + والانتقال إلى المحاكاة يوزو تعرف على كيفية إعداد. + لنبدأ + المفاتيح + اختر ملف <b>prod.keys</b> من الزر ادناه + إختيار المفاتيح + الألعاب + اختر مجلد <b>العابك</b> من الزر ادناه. + إنهاء + كل شيء جاهز./n استمتع بألعابك! + استمر + التالي + عودة + إضافة ألعاب + إختار مجلد ألعابك + مكتمل + + + الألعاب + البحث + الإعدادات + لم يتم العثور على ملفات او لم يتم تحديد مسار العاب. + بحث وتصفية الألعاب + تحديد مجلد الألعاب + يسمح لـ يوزو بملء قائمة الألعاب + تخطُ اختيار مجلد الالعاب؟ + لن يتم عرض الألعاب في قائمة الألعاب إذا لم يتم تحديد مجلد + https://yuzu-emu.org/help/quickstart/#dumping-games + البحث عن ألعاب + إعدادات البحث + تم تحديد مجلد الألعاب + تثبيت prod.keys + مطلوب لفك تشفير ألعاب البيع بالتجزئة + تخطي إضافة المفاتيح؟ + مطلوب مفاتيح صالحة لمحاكاة ألعاب البيع بالتجزئة. ستعمل تطبيقات البيرة المنزلية فقط إذا تابعت + https://yuzu-emu.org/help/quickstart/#guide-introduction + التنبيهات + امنح إذن الإشعار باستخدام الزر أدناه + منح الإذن + تخطي منح إذن الإشعارات؟ + لن يتمكن يوزو من إشعارك بالمعلومات المهمة + تم رفض الإذن + لقد رفضت هذا الإذن عدة مرات ويتعين عليك الآن منحه يدويًا في إعدادات النظام + حول + بناء الإصدار، والاعتمادات، وأكثر من ذلك + مساعدة + تخطي + إلغاء + تثبيت مفاتيح أميبو + مطلوب لاستخدام أميبو في اللعبة + تم تحديد ملف مفاتيح غير صالح + تم تثبيت المفاتيح بنجاح + خطأ في قراءة مفاتيح التشفير + مفاتيح التشفير غير صالحة + https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys + الملف المحدد غير صحيح أو تالف. يرجى إعادة المفاتيح الخاصة بك + GPU تثبيت برنامج تشغيل + قم بتثبيت برامج تشغيل بديلة للحصول على أداء أو دقة أفضل + إعدادات متقدمة + إعدادات متقدمة: %1$s + تكوين إعدادات المحاكي + لعبت مؤخرا + أضيف مؤخرا + بيع بالتجزئة + البيرة المنزلية + فتح مجلد يوزو + إدارة ملفات يوزو الداخلية + تعديل مظهر التطبيق + لم يتم العثور على مدير الملفات + لا يمكن فتح مجلد يوزو + الرجاء تحديد موقع مجلد المستخدم باستخدام اللوحة الجانبية لمدير الملفات يدويًا + إدارة حفظ البيانات + حفظ البيانات التي تم العثور عليها. يرجى اختيار أحد الخيارات التالية + استيراد أو تصدير ملفات الحفظ + تم الاستيراد بنجاح + بنية مجلد الحفظ غير صالحة + يجب أن يكون اسم المجلد الفرعي الأول هو معرف عنوان اللعبة. + استيراد + تصدير + تثبيت البرامج الثابتة + تثبيت البرامج الثابتة + تم تثبيت البرامج الثابتة بنجاح + فشل تثبيت البرامج الثابتة + مشاركة سجلات التصحيح + مشاركة ملف سجل يوزو لتصحيح المشكلات + لم يتم العثور على ملف السجل + تثبيت محتوى اللعبة + DLC قم بتثبيت تحديثات اللعبة أو + جارٍ تثبيت المحتوى + لا يُسمح بتثبيت الألعاب الأساسية لتجنب التعارضات المحتملة. + %1$d تم التثبيت بنجاح + %1$d تمت الكتابة فوقه بنجاح + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + برامج التشغيل المخصصة غير مدعومة + تحميل برنامج التشغيل المخصص غير معتمد حاليًا لهذا الجهاز.\nحدد هذا الخيار مرة أخرى في المستقبل لمعرفة ما إذا تمت إضافة الدعم! + إدارة بيانات يوزو + استيراد/تصدير البرامج الثابتة والمفاتيح وبيانات المستخدم والمزيد! + مشاركة ملف الحفظ + فشل تصدير الحفظ + + نسخ إلى الحافظة + محاكي سويتش مفتوح المصدر + المساهمين + https://github.com/yuzu-emu/yuzu/graphs/contributors + المشاريع التي تجعل تطبيق يوزو لنظام أندرويد ممكنًا + البناء + بيانات المستخدم + جارٍ تصدير بيانات المستخدم + جارٍ استيراد بيانات المستخدم + استيراد بيانات المستخدم + نسخة احتياطية يوزو غير صالحة + تم تصدير بيانات المستخدم بنجاح + تم استيراد بيانات المستخدم بنجاح + تم إلغاء التصدير + https://discord.gg/u77vRWY + https://yuzu-emu.org/ + https://github.com/yuzu-emu + + + الوصول المبكر + احصل على الوصول المبكر + https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea + الميزات المتطورة، والوصول المبكر إلى التحديثات، وأكثر من ذلك + مزايا الوصول المبكر + ميزات متطورة + الوصول المبكر إلى التحديثات + لا يوجد التثبيت اليدوي + الدعم ذو الأولوية + المساعدة في الحفاظ على اللعبة + امتناننا الأبدي + هل انت مهتم؟ + + + الحد من السرعة + يحد من سرعة المحاكاة بنسبة محددة من السرعة العادية + الحد من السرعة في المئة + يحدد النسبة المئوية للحد من سرعة المحاكاة. 100% هي السرعة الطبيعية. ستؤدي القيم الأعلى أو الأدنى إلى زيادة أو تقليل حد السرعة. + دقة وحدة المعالجة المركزية + %1$s%2$s + + + وضع الإرساء + زيادة الدقة، وانخفاض الأداء. يتم استخدام الوضع المحمول عند تعطيله، مما يؤدي إلى خفض الدقة وزيادة الأداء. + المنطقة التي تمت محاكاتها + لغة المحاكاه + حدد التاريخ و الساعة في الوقت الحقيقي + حدد وقت الساعة في الوقت الفعلي + ساعة مخصصة في الوقت الحقيقي + يسمح لك بتعيين ساعة مخصصة في الوقت الفعلي منفصلة عن وقت النظام الحالي لديك + تعيين ساعة مخصصة في الوقت الحقيقي + + + مستوى الدقة + (Handheld/Docked) الدقة + VSync وضع + الاتجاه + تناسب الابعاد + طريقة مكافحة التعرج + استخدم تظليل غير متزامن + يجمع التظليل بشكل غير متزامن، مما يقلل من التأتأة ولكنه قد يؤدي إلى حدوث بعض الأخطاء. + استخدم التنظيف التفاعلي + تحسين دقة العرض في بعض الألعاب على حساب الأداء + يقلل من التأتأة عن طريق تخزين وتحميل التظليلات التي تم إنشاؤها محليًا. + + + وحدة المعالج المركزية + تصحيح أخطاء وحدة المعالجة المركزية + يضع وحدة المعالجة المركزية في وضع التصحيح البطيء. + GPU + API + تصحيح الأخطاء الرسومية + يضبط واجهة برمجة تطبيقات الرسومات على وضع تصحيح الأخطاء البطيء. + Fastmem + + + محرك الإخراج + حجم + يحدد حجم إخراج الصوت + + + افتراضي + الإعدادات المحفوظة + الإعدادات المحفوظة لـ %1$s + القائمة غير المنفذة + جاري تحميل + إيقاف تشغيل + هل تريد إعادة تعيين هذا الإعداد مرة أخرى إلى قيمته الافتراضية؟ + إعادة تعيين إلى الافتراضي + إعادة تعيين جميع الإعدادات؟ + سيتم إعادة تعيين كافة الإعدادات المتقدمة إلى تكوينها الافتراضي. هذا لا يمكن التراجع عنها. + إعادة تعيين الأعدادات + إغلاق + معرفة المزيد + تلقائي + إرسال + قيمه خاليه + استيراد + تصدير + فشل التصدير + فشل الاستيراد + إلغاء + + + GPU حدد برنامج تشغيل + الحالي الخاص بك؟ GPU هل ترغب في استبدال برنامج تشغيل + تثبيت + افتراضي + يستخدم تعريف معالج الرسوميات الافتراضي + تم تحديد برنامج تشغيل غير صالح ، باستخدام النظام الافتراضي + تعريف معالج الرسوميات الخاص بالنظام + جارٍ تثبيت برنامج التشغيل… + + + إعدادات + عام + النظام + الرسوميات + الصوت + السمة واللون + تصحيح الأخطاء + + + الخاص بك ROM تم تشفير + حدث خطأ أثناء تهيئة مركز الفيديو + ROM غير قادر على تحميل + غير موجود ROM ملف + + + الخروج من المحاكاة + منجز + عداد إطار/ثانية + تبديل عناصر التحكم + مركز العصا النسبي + مزلاق أزرار الاتجاهات + الاهتزازات الديناميكية + عرض التراكب + تبديل الكل + ضبط التراكب + حجم + العتامه + إعادة تعيين التراكب + تحرير التراكب + إيقاف المحاكاة مؤقتًا + إلغاء الإيقاف المؤقت للمضاهاة + خيارات التراكب + + جارٍ تحميل الإعدادات + + + لوحة المفاتيح البرمجية + + + إلغاء + استمر + لم يتم العثور على أرشيف النظام + أرشيف النظام + خطأ في الحفظ/التحميل + خطا فادح + سيؤدي إيقاف تشغيل هذا الإعداد إلى تقليل أداء المحاكاة بشكل ملحوظ! للحصول على أفضل تجربة، يوصى بترك هذا الإعداد ممكنًا. + %1$s %2$s + لا توجد لعبة قابلة للتمهيد + + + اليابان + الولايات المتحدة الأمريكية + أوروبا + أستراليا + الصين + كوريا + تايوان + + + Byte + KB + MB + GB + TB + PB + EB + + + Vulkan + لاشيء + + + عادي + عالي + Extreme (بطيء) + + + 0.5X (360p/540p) + 0.75X (540p/810p) + 1X (720p/1080p) + 2X (1440p/2160p) (بطيء) + 3X (2160p/3240p) (بطيء) + 4X (2880p/4320p) (بطيء) + + + Immediate (Off) + Mailbox + FIFO (On) + FIFO Relaxed + + + Nearest Neighbor + Bilinear + Bicubic + Gaussian + ScaleForce + AMD FidelityFX™ Super Resolution + + + لا شيء + FXAA + SMAA + + + افقي + عمودي + تلقائي + + + (16:9) افتراضي + 4:3 فرض + 21:9 فرض + 16:10 فرض + تمتد إلى النافذة + + + دقه + غير آمن + Paranoid (Slow) + + + أزرار الاتجاهات + العصا اليسرى + العصا اليمنى + شاشة الإستقبال + لقطة شاشة + + + تحضير التظليل + بناء التظليل + + + تغيير سمة التطبيق + افتراضي + Material You + + + تغيير وضع السمة + اتبع النظام + فاتح + غامق + + + cubeb + + + خلفيات سوداء + عند استخدام المظهر الداكن، قم بتطبيق خلفيات سوداء. + + + صورة داخل صورة + تصغير النافذة عند وضعها في الخلفية + توقف + تشغيل + كتم + إلغاء الكتم + + + التراخيص + AMD ترقية عالية الجودة من + diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml new file mode 100644 index 000000000..d2e5fee19 --- /dev/null +++ b/src/android/app/src/main/res/values-ckb/strings.xml @@ -0,0 +1,336 @@ + + + + ئەم نەرمەکاڵایە یارییەکانی کۆنسۆلی نینتێندۆ سویچ کارپێدەکات. هیچ ناونیشانێکی یاری و کلیلی تێدا نییە..<br /><br />پێش ئەوەی دەست پێ بکەیت، تکایە شوێنی فایلی prod.keys ]]> دیاریبکە لە نێو کۆگای ئامێرەکەت.<br /><br />زیاتر فێربە]]> + ئیمولەیشن کارایە + ئاگادارکردنەوەیەکی بەردەوام نیشان دەدات کاتێک ئیمولەیشن کاردەکات. + یوزو کاردەکات + ئاگاداری و هەڵەکان + ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت. + مۆڵەتی ئاگادارکردنەوە نەدراوە! + + + بەخێربێیت! + فێربە چۆن <b>yuzu</b> ڕێکبخەیت و بچییە ناو ئیمولەیشن. + دەست پێبکە + کلیلەکان + فایلی <b>prod.keys</b> هەڵبژێرە بە دوگمەی خوارەوە. + کلیلەکان هەڵبژێرە + یاریەکان + فۆڵدەری <b>Games</b> هەڵبژێرە بە دوگمەی خوارەوە. + تەواو + تۆ تەواو ئامادەیت.\nچێژ لە یارییەکانت وەربگرە! + بەردەوام بوون + دواتر + گەڕانەوە + زیادکردنی یاری + فۆڵدەری یارییەکانت هەڵبژێرە + + یاریەکان + گەڕان + ڕێکخستنەکان + تا ئێستا هیچ فایلێک نەدۆزراوەتەوە یان هیچ ناونیشانێکی یاری هەڵنەبژێردراوە. + گەڕان و فلتەرکردنی یارییەکان + فۆڵدەری یارییەکان هەڵبژێرە + ڕێگە بە یوزو دەدات بۆ پڕکردنەوەی لیستی یارییەکان + هەڵبژاردنی فۆڵدەری یارییەکان تێپەڕدەکەیت؟ + یارییەکان لە لیستی یارییەکاندا پیشان نادرێن ئەگەر فۆڵدەرێک هەڵنەبژێردرێت. + https://yuzu-emu.org/help/quickstart/#dumping-games + گەڕان بەدوای یارییەکاندا + ناونیشانی یارییەکان هەڵبژێردرا + دابمەزرێنە prod.keys + پێویستە بۆ کۆدکردنەوەى یارییە تاکەکەسییەکان + زیادکردنی کلیلەکان تێپەڕدەکەیت؟ + کلیلی دروست پێویستە بۆ وەرگرتنی یارییەکانی تاکەکەسی. تەنها ئەپەکانی homebrew کاردەکەن ئەگەر بەردەوام بیت. + https://yuzu-emu.org/help/quickstart/#guide-introduction + ئاگادارکردنەوەکان + بە دوگمەی خوارەوە مۆڵەتی ئاگادارکردنەوەکە بدە. + مۆڵەت بدە + پێدانی مۆڵەتی ئاگادارکردنەوە تێپەڕدەکەیت؟ + یوزو ناتوانێت لە زانیاری گرنگ ئاگادارت بکاتەوە. + مۆڵەت پێدان ڕەتکرایەوە + زۆر جار ئەم مۆڵەتەت ڕەتکردۆتەوە و ئێستا دەبێت بە دەستی ڕێگەپێدان بکەیت لە ڕێکخستنەکانی سیستەمدا. + دەربارە + وەشانی دروستکردن، بیتبێن و زۆر شتیتر + یارمەتی + پەڕاندن + ڕەتکردنەوە + دامەزراندنی کلیلی Amiibo + پێویستە بۆ بەکارهێنانی Amiibo لە یاریدا + فایلی کلیلێکی نادروست هەڵبژێردرا + کلیلەکان بە سەرکەوتوویی دامەزران + هەڵە لە خوێندنەوەی کۆدکردنی کلیل + دڵنیابەوە کە فایلی کلیلەکانت درێژکراوەی .keys ی هەیە و دووبارە هەوڵبدەرەوە. + دڵنیابە کە فایلی کلیلەکانت درێژکراوەی .bin ی هەیە و دووبارە هەوڵبدەرەوە. + کلیلی کۆدکردنی نادروستە + https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys + فایلە هەڵبژێردراوەکە هەڵەیە یان تێکچووە. تکایە دووبارە کلیلەکانت دەربێنەوە. + دامەزراندنی وەگەڕخەری GPU + دامەزراندنی وەگەڕخەری بەدیل بۆ ئەوەی بە ئەگەرێکی زۆرەوە کارایی باشتر یان وردبینی هەبێت + ڕێکخستنە پێشکەوتووەکان + سازدانی ڕێکخستنەکانی ئیمولەیتەر + بەم دواییە یاری کردووە + بەم دواییە زیادکرا + بەتاک + هۆم بریو + کردنەوەی فۆڵدەری یوزو + بەڕێوەبردنی فایلە ناوخۆییەکانی یوزو + دەستکاری کردنی شێوازی ئەپەکە + هیچ فایل بەڕێوەبەرێک نەدۆزرایەوە + نەتوانرا ناونیشانی یوزو بکرێتەوە + تکایە شوێنی فۆڵدەری بەکارهێنەر لەگەڵ پانێڵی لایەنی فایل بەڕێوەبارەکان بە دەست بدۆزەرەوە. + بەڕێوەبردنی داتای پاشەکەوتکراو + داتای پاشەکەوتکراو دۆزراوە. تکایە لە خوارەوە بژاردەیەک هەڵبژێرە. + هاوردەکردن یان هەناردەکردنی فایلی پاشەکەوتکراو + بە سەرکەوتوویی هاوردە کرا + پێکهاتەی شوێنی پاشەکەوتکراو نادروستە + ناوی یەکەمی فۆڵدەر دەبێت ناسنامەی ناونیشانی یارییەکە بێت. + هاوردەکردن + هەناردەکردن + دامەزراندنی پتەوواڵا + پتەوواڵا دەبێت لە ئەرشیفی زیپدا بێت و پێویستە بۆ بووتکردنی هەندێک یاری + دامەزرانی پتەوواڵا + پتەوواڵا بە سەرکەوتوویی دامەزرا + دامەزراندنی پتەوواڵا شکستی هێنا + هاوبەشی پێکردنی لۆگەکانی چاککردنەوە + فایلە لۆگەکەی یوزو هاوبەش بکە بۆ چاککردنی کێشەکان + هیچ فایلێکی لۆگ نەدۆزراوە + دامەزراندنی ناوەڕۆکی یاری + دامەزراندنی نوێکاری یارییەکان یان DLC + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + + گایا ڕاستەقینە نییە + کۆپی کرا بۆ تەختەی نووسین + ئیمۆلیتەرێکی سەرچاوە-کراوەی سویچ + بەشداربووان + دروستکراوە لەگەڵ \u2764 لەلایەن تیمەکەی یوزو + https://github.com/yuzu-emu/yuzu/graphs/contributors + ئەو پڕۆژانەی کە یوزوی بۆ ئەندرۆید ڕەخساند + بونیات + https://discord.gg/u77vRWY + https://yuzu-emu.org/ + https://github.com/yuzu-emu + + + بەزوویی دەسپێگەشتن + بەدەستهێنانی بەزوویی دەسپێگەشتن + https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea + تایبەتمەندییە پێشکەوتووەکان، بەزوویی دەستگەیشتن بە نوێکارییەکان و زۆر شتی تر + سوودەکانی بەزوویی دەسپێگەشتن + تایبەتمەندییە پێشکەوتووەکان + زوو دەستگەیشتن بە نوێکارییەکان + چیتر دامەزراندنی دەستی نییە + پشتگیری لە پێشینە + یارمەتیدانی پاراستنی یارییەکان + سوپاس و پێزانینی هەمیشەییمان + ئایا تۆ خوازیاریت؟ + + + سنووردارکردنی خێرایی + خێرایی ئیمولەیشن سنووردار دەکات بۆ ڕێژەیەکی دیاریکراو لە خێرایی ئاسایی. + سنووردارکردنی لەسەدای خێرایی + ڕێژەی سەدی دیاری دەکات بۆ سنووردارکردنی خێرایی ئیمولەیشن. 100% خێرایی ئاساییە. بەهایی بەرزتر یان نزمتر دەبێتە هۆی زیاد یان کەمکردنەوەی سنووری خێرایی. + وردی CPU + + دۆخی دۆککراو + ڕوونی زیاد دەکات، کارایی کەم دەکاتەوە. دۆخی دەستی بەکاردێت کاتێک لەکاردەخرێت، ئەمەش ڕوونی دادەبەزێنێت و کارایی زیاد دەکات. + ناوچەی ئیمولەیشن + زمانی ئیمولەیتەر + هەڵبژاردنی بەرواری RTC + هەڵبژاردنی کاتی RTC + RTCی تایبەتمەند + ڕێگەت پێدەدات کاتژمێرێکی کاتی ڕاستەقینەی تایبەتمەند دابنێیت کە جیاوازە لە کاتی ئێستای سیستەمەکەت. + دانانی RTCی تایبەتمەند + + + ئاستی وردبینی + ڕوونی (دۆخی دەستی/دۆخی دۆک) + دۆخی VSync + ڕێژەی ڕووبەری شاشە + فلتەری گونجاندنی پەنجەرە + شێوازی دژە-خاوڕۆیی + ناچاریکردن بۆ زۆرترین کاتژمێر (تەنها ئەدرینۆ) + GPU ناچار دەکات بە زۆرترین کاتژمێر کاربکات (هێشتا سنووردارکردنی گەرمی جێبەجێ دەکرێت). + بەکارهێنانی سێبەری ناهاوسەنگ + سێبەرەکان بە شێوەیەکی ناهاوسەنگ کۆدەکاتەوە، پچڕپچڕی کەمدەکاتەوە بەڵام لەوانەیە گلێچ دروستکا. + بەکارهێنانی بەرپێچدەرەوە + وردی ڕێندەرکردن لە هەندێک یاریدا باشتر دەکات لەسەر تێچووی کارایی. + بیرگەخێرای سێبەری دیسک + پچڕپچڕی کەمدەکاتەوە بە هەڵگرتن و بارکردنی سێبەری دروستکراو لە ناوخۆدا. + + + CPU + API گرافیک + چاککردنەوەی گرافیک + API ی گرافیکەکان ڕێکدەخات بۆ دۆخی چاککردنی خاو. + قەبارەی دەنگی + دیاریکردنی قەبارەی دەنگی دەرچووی بیستۆک و بزوێنەری دەنگی دەرەکی. + + + بنەڕەت + ڕێکخستنە پاشەکەوتکراوەکان + ڕێکخستنە پاشەکەوتکراوەکان بۆ %1$s + هەڵە لە پاشەکەوتکردن %1$s.ini: %2$s + بارکردن... + ئایا دەتەوێت ئەم ڕێکخستنە بگەڕێنیتەوە بۆ بەهای بنەڕەتی خۆی؟ + دوبارە ڕێکخستنەوەی بۆ بنەڕەت + هەموو ڕێکخستنەکان دوبارە ڕێک دەخاتەوە؟ + هەموو ڕێکخستنە پێشکەوتووەکان دەگەڕێنەوە بۆ ڕێکخستنی بنەڕەتی خۆیان. پاشگەز بوونەوەی نییه. + دوبارە ڕێککردنەوەی ڕێکخستنەکان + داخستن + زیاتر فێربە + خودکار + پێشکەشکردن + هاوردەکردن + هەناردەکردن + + هەڵبژاردنی وەگەڕخەری GPU + حەز دەکەیت وەگەڕخەری GPU ی ئێستات بگۆڕیت؟ + دامەزراندن + بنەڕەت + بەکارهێنانی وەگەڕخەری GPU ی بنەڕەت + وەگەڕخەری نادروست هەڵبژێردرا، بە بەکارهێنانی بنەڕەتی سیستەم! + وەگەڕخەری GPU ی سیستەم + دامەزراندنی وەگەڕخەر... + + + ڕێکخستنەکان + گشتی + سیستەم + گرافیک + دەنگ + ڕەنگ و ڕووکار + چاککردنەوە + + + ڕۆمەکەت کۆدکراوە + prod.keys فایلەکەت بۆ ئەوەی بتوانرێت یارییەکان کۆد بکرێنەوە.]]> + هەڵەیەک لە دەستپێکردنی ناوەکی ڤیدیۆکەدا ڕوویدا + ئەمەش بەزۆری بەهۆی وەگەڕخەرێکی ناتەبای GPU ەوەیە. دامەزراندنی وەگەڕخەری GPU ی تایبەتمەندکراو لەوانەیە ئەم کێشەیە چارەسەر بکات. + ناتوانرێت ڕۆم باربکرێت + فایلی ڕۆم بوونی نییە + + + دەرچوون لە ئیمولەیشن + تەواو + FPS ژمێر + گۆڕینی کۆنتڕۆڵ + ناوەندی گێڕ بەنزیکەیی + خلیسکانی 4 دوگمەکە + لەرینەوەی پەنجەلێدان + نیشاندانی داپۆشەر + گۆڕینی سەرجەم + ڕێکخستنی داپۆشەر + پێوەر + ڕوونی + دووبارە ڕێکخستنەوەی داپۆشەر + دەستکاریکردنی داپۆشەر + وەستاندنی ئیمولەیشن + لادانی وەستاندنی ئیمولەیشن + هەڵبژاردەکانی داپۆشەر + + بارکردنی ڕێکخستنەکان... + + + کیبۆردی نەرمەکاڵا + + + دەربارە + بەردەوام بوون + ئەرشیفی سیستەم نەدۆزراوە + %s دیار نییە. تکایە ئەرشیفی سیستەمەکەت فڕێ بدە.\nبەردەوامی ئیمولەیشن لەوانەیە ببێتە هۆی تێکچوون و فڕێدانەدەرەوە. + ئەرشیفێکی سیستەم + هەڵەی پاشەکەوتکردن/بارکردن + هەڵەی کوشندە + هەڵەیەکی کوشندە ڕوویدا. بۆ وردەکارییەکان لۆگەکە بپشکنە.\nبەردەوامی ئیمولەیشن لەوانەیە ببێتە هۆی تێکچوون و فڕێدانەدەرەوە. + کوژاندنەوەی ئەم ڕێکخستنە دەبێتە هۆی کەمکردنەوەی کارایی ئیمولەیشن! بۆ باشترین ئەزموون، باشترە ئەم ڕێکخستنە چالاک بهێڵیتەوە. + + ژاپۆن + ئەمریکا + ئەورووپا + ئوسترالیا + چین + کۆریا + تایوان + + GB + + ڤوڵکان + هیچ + + + ئاسایی + بەرز + ئەوپەڕ (خاو) + + + 0.5X (360p/540p) + 0.75X (540p/810p) + 1X (720p/1080p) + 2X (1440p/2160p) (خاو) + 3X (2160p/3240p) (خاو) + 4X (2880p/4320p) (خاو) + + + دەستبەجێ (کوژاوە) + سندوقی پۆستە + FIFO (پێکراو) + FIFO ئارام + + + نزیکترین دراوسێ + دوو هێڵی + دووخشتەکی + گاوسی + پێوەرهێز + AMD FidelityFX™ سوپەر ووردبینی + + + هیچ + FXAA + SMAA + + خودکار + + + بنەڕەت (16:9) + ڕووبەری 4:3 + ڕووبەری 21:9 + ڕووبەری 16:10 + کشانی پڕ بەشاشە + + + وورد + ناسەقامگیر + بەگومان (خاو) + + + 4 دوگمەکە + گێڕی چەپ + گێڕی ڕاست + ماڵەوە + وێنەگرتنی شاشە + + + ئامادەکردنی سێبەرەکان + دروستکردنی سێبەرەکان + + + گۆڕینی ڕووکاری ئەپەکە + بنەڕەت + کەرەستەی تۆ + + + گۆڕینی دۆخی ڕووکار + پەیڕەوی کردنی سیستەم + ڕوناکی + تاریک + + + پاشبنەمای ڕەش + لە کاتی بەکارهێنانی ڕووکاری تاریکدا، پاشبنەمای ڕەش دادەنێ. + + + مۆڵەتەکان + بەرزکردنەوەی کوالێتی بەرز لە کۆمپانیای AMD + diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 72a47fbdb..9c6590b5e 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -1,5 +1,5 @@ - + Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.<br /><br />Bevor du beginnst, bitte halte deine prod.keys ]]> auf deinem Gerät bereit. .<br /><br />Mehr Infos]]> Emulation ist aktiv @@ -25,6 +25,7 @@ Zurück Spiele hinzufügen Spieleverzeichnis auswählen + Fertig! Spiele @@ -38,6 +39,7 @@ Spiele werden in der Spieleliste nicht angezeigt, wenn kein Ordner ausgewählt ist. https://yuzu-emu.org/help/quickstart/#dumping-games Spiele suchen + Einstellungen suchen Spieleverzeichnis ausgewählt prod.keys installieren Zum Entschlüsseln von Spielen benötigt @@ -60,8 +62,11 @@ 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 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys + Die ausgewählte Datei ist falsch oder beschädigt. Bitte kopieren Sie Ihre Schlüssel erneut. GPU-Treiber installieren Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren Erweiterte Einstellungen @@ -84,7 +89,17 @@ Der erste Unterordnername muss die Titel-ID des Spiels sein. Importieren Exportieren - + Firmware installieren + Die Firmware muss in einem ZIP-Archiv vorliegen und wird zum Booten einiger Spiele benötigt + Firmware wird installiert + Die Firmware wurde erfolgreich installiert! + Bei der Firmware installation ist etwas fehlgeschlagen. + Debug-Logs teilen + Debug-Logs an yuzu zur Untersuchung absenden + Keine Log-Datei gefunden + Spiel installieren + Spiel Update oder DLC installieren + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates Gaia ist nicht real In die Zwischenablage kopiert @@ -92,7 +107,10 @@ Beitragende Gemacht mit \u2764 vom yuzu Team https://github.com/yuzu-emu/yuzu/graphs/contributors + Projekte, die yuzu für Android möglich machen Build + Nutzerdaten + Export abgebrochen https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu @@ -107,45 +125,39 @@ Früherer Zugriff auf Updates Keine manuelle Installation Priorisierte Unterstützung + Beitrag zur Erhaltung der Spiele Unsere ewige Dankbarkeit Bist du interessiert? - Geschwindigkeitsbegrenzung aktivieren - Wenn aktiviert, wird die Emulationsgeschwindigkeit auf einen Prozentsatz der normalen Geschwindigkeit begrenzt. + Limitierte Geschwindigkeit + Limitiert die Geschwindigkeit auf einen von dir festgelegten Prozentsatz. Geschwindkeitsbegrenzung in Prozent - Legt den Prozentsatz der Bergrenzung der Emulationsgeschwindigkeit fest. Mit dem Standardwert von 100% wird die Emulation auf die normale Geschwindigkeit begrenzt. Höhere oder niedrigere Werte erhöhen oder verringern die Geschwindigkeitsbegrenzung. + Gibt die prozentuale Geschwindigkeit der Emulation an. 100% sind normal. Werte darüber oder drunter werden die Geschwindigkeit entsprechend verändern. CPU-Genauigkeit - - Dock-Modus - Emuliert im Dock-Modus, was die Auflösung verbessert, aber die Leistung senkt. + Gedockter Modus + Der Docked Modus erhöht die Auflösung, verringert die aber die Leistung. Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die Leistung. Emulierte Region Emulierte Sprache RTC-Datum auswählen RTC-Zeit auswählen - Benutzerdefinierte RTC aktivieren - Mit dieser Einstellung kann eine benutzerdefinierte Echtzeituhr unabhängig von der aktuellen Systemzeit verwendet werden. - Benutzerdefinierte RTC einstellen - + Benutzerdefinierte Echtzeituhr - API Genauigkeitsstufe - Auflösung VSync-Modus + Orientierung Seitenverhältnis Fensteranpassungsfilter - Kantenglättungs-Methode Maximale Taktfrequenz erzwingen (nur Adreno) Erzwingt den Betrieb der GPU mit der maximal möglichen Taktfrequenz (Temperaturbeschränkungen werden weiterhin angewendet). Asynchrone Shader nutzen - Kompiliert Shader asynchron, was Ruckler reduziert, aber zu Glitches führen kann. - Grafik-Debugging aktivieren - Wenn aktiviert, schaltet die Grafik-API in einen langsameren Debugging-Modus. - Nutze Festplatten-Shader-Cache - Ruckeln wird durch das Speichern und Laden von generierten Shadern auf der Festplatte reduziert. - - + + CPU + CPU Debugging + GPU + API + Graphik-Debugging Lautstärke Legt die Lautstärke der Audioausgabe fest. @@ -154,14 +166,22 @@ Einstellungen gespeichert Einstellungen für %1$s gespeichert Fehler beim Speichern von %1$s.ini: %2$s + Unimplementiertes Menü Lädt... Möchtest du diese Einstellung auf den Standardwert zurücksetzen? Auf Standard zurücksetzen Alle Einstellungen zurücksetzen? - Alle erweiterten Einstellungen werden auf ihren Standardwert zurückgesetzt. Dies kann nicht rückgängig gemacht werden. Einstellungen zurückgesetzt Schließen Mehr erfahren + Auto + Absenden + Null + Importieren + Exportieren + Export fehlgeschlagen + Import fehlgeschlagen + Abbrechen GPU-Treiber auswählen @@ -169,6 +189,7 @@ Installieren Standard Standard GPU-Treiber wird verwendet + Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet! System GPU-Treiber Treiber wird installiert... @@ -179,6 +200,7 @@ Grafik Audio Theme und Farbe + Debug Das ROM ist verschlüsselt @@ -192,22 +214,15 @@ Emulation beenden Fertig FPS Zähler - Steuerung umschalten - Relative Stick-Mitte - DPad Slide - Haptik - Overlay anzeigen Alle umschalten Overlay anpassen Größe Transparenz Overlay zurücksetzen Overlay bearbeiten - Emulation pausieren - Emulation fortsetzen Overlay-Optionen - Lädt Einstellungen... + Lade Einstellungen... Software-Tastatur @@ -221,7 +236,7 @@ Schwerwiegender Fehler Ein schwerwiegender Fehler ist aufgetreten. Einzelheiten wurden im Log protokolliert.\nDas Fortsetzen der Emulation kann zu Abstürzen und Bugs führen. Das Deaktivieren dieser Einstellung führt zu erheblichen Leistungsverlusten! Für ein optimales Erlebnis wird empfohlen, sie aktiviert zu lassen. - + %1$s %2$s Japan USA @@ -231,6 +246,15 @@ Korea Taiwan + + Byte + KB + MB + GB + TB + PB + EB + Vulkan Keiner @@ -267,12 +291,15 @@ FXAA SMAA + Portrait + Auto + Standard (16:9) 4:3 erzwingen 21:9 erzwingen Erzwinge 16:10 - Auf Fenster anpassen + Auf Bildschirmgröße anpsassen Akkurat @@ -280,9 +307,9 @@ Paranoid (Langsam) - Steuerkreuz - Linker Analogstick - Rechter Analogstick + D-Pad + Linker Stick + Rechter Stick Home Screenshot @@ -291,18 +318,30 @@ Shader werden erstellt - App-Theme ändern + App-Thema ändern Standard Material You - Theme-Modus ändern + Themen-Modus ändern System folgen Hell Dunkel + + cubeb + - Schwarze Hintergünde verwenden + Schwarze Hintergründe Bei Verwendung des dunklen Themes, schwarze Hintergründe verwenden. - + + Bild im Bild + Pause + Stummschalten + Ton aktivieren + + + Lizenzen + Hochwertiges Upscaling von AMD + diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index e5bdd5889..103ac6e65 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -1,7 +1,7 @@ - + - Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o keys no vienen incluidos.<br /><br />Antes de empezar, por favor, localice el archivo prod.keys ]]>en el almacenamiento de su dispositivo..<br /><br />Saber más]]> + Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o claves no vienen incluidos.<br /><br />Antes de empezar, por favor, localice el archivo prod.keys ]]>en el almacenamiento de su dispositivo..<br /><br />Saber más]]> Emulación activa Muestra una notificación persistente cuando la emulación está activa. yuzu esta ejecutándose @@ -25,6 +25,7 @@ Atrás Añadir Juegos Selecciona la carpeta de juegos + ¡Completado! Juegos @@ -37,7 +38,8 @@ ¿Omitir la selección de la carpeta de juegos? No se mostrará ningún juego si no se ha seleccionado una carpeta de juegos. https://yuzu-emu.org/help/quickstart/#dumping-games - Buscar Juegos + Buscar juegos + Buscar configuración Directorio de juegos seleccionado Instalar prod.keys Requerido para descifrar juegos @@ -58,15 +60,18 @@ Cancelar Instalar clave de Amiiboo Necesario para usar Amiibo en el juego - Archivo de claves inválido seleccionado + Archivo de claves seleccionado invá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 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys El archivo seleccionado es incorrecto o está corrupto. Vuelva a redumpear sus claves. Instalar driver de GPU Instale drivers alternativos para obtener un rendimiento o una precisión potencialmente mejores Opciones avanzadas + Configuración avanzada: %1$s Configurar las opciones del emulador Jugado recientemente Añadido recientemente @@ -86,6 +91,33 @@ El nombre de la primera subcarpeta debe ser el Title ID del juego. Importar Exportar + Instalar firmware + El firmware debe estar en un archivo ZIP y es necesario para ejecutar algunos juegos + Instalando firmware + Firmware instalado con éxito + Falló la instalación de firmware + Asegúrese de que los archivos nca del firmware estén en la raíz del zip e inténtelo de nuevo. + Compartir registros de depuración + Comparta el archivo de registro de yuzu para depurar problemas + No se encontró ningún archivo de registro + Instalar contenido de juego + Instalar actualizaciones o DLC + Instalando contenido... + Error instalando archivo(s) a la NAND + Asegúrese de que el/los contenido(s) son válidos y que el archivo prod.keys esté instalado. + La instalación de los juegos base no está permitida para así evitar posibles conflictos. + Sólo hay soporte para el contenido en NSP y XCI. Asegúrese de que el/los contenido(s) son válidos. + %1$d error(es) de instalación + Contenido(s) de juego instalado/s con éxito + %1$d instalado con éxito + %1$d sobreescrito con éxito + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + Drivers personalizados no soportados + En estos momentos, la carga de drivers personalizados no está disponible para este dispositivo..\n¡Comprueba esta opción en el futuro para ver si ya está añadido el soporte a ese dispositivo! + Administrar datos de yuzu + Importa/exporta el firmware, las keys, los datos de usuario, ¡y más! + Compartir archivo de guardado + La exportación del guardado falló Gaia no es real @@ -94,7 +126,18 @@ Contribuidores Hecho con \u2764 del equipo yuzu https://github.com/yuzu-emu/yuzu/graphs/contributors + Proyectos que hacen que yuzu para Android sea una realidad Versión + Datos de usuario + Importa/exporta todos los datos de usuario.\n\nCuando se importen los datos de usuario, ¡los demás datos de usuario existentes serán borrados! + Exportando datos de usuario... + Importando datos de usuario... + Importar datos de usuario + Backup de válido + Datos de usuario exportados con éxito + Datos de usuario importados con éxito + Exportación cancelada + Asegúrese de que las carpetas de datos de usuario estén en la raíz de la carpeta del zip y contengan un archivo config en config/config.ini e inténtelo de nuevo. https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu @@ -114,41 +157,53 @@ ¿Estás interesado? - Activar limite de velocidad - Cuando está habilitado, la velocidad de emulación se limitará a un porcentaje específico de la velocidad normal. + Limitar velocidad + Limita la velocidad de emulación a un porcentaje específico de la velocidad normal. Limitar porcentaje de velocidad - Especifica el porcentaje para limitar la velocidad de emulación. Con el valor predeterminado del 100 %, la emulación se limitará a la velocidad normal. Valores más altos o más bajos aumentarán o disminuirán el límite de velocidad. + Especifica el porcentaje para limitar la velocidad de emulación. 100% es la velocidad normal. Valores más altos o bajos incrementarán o disminuirán el límite de velocidad. Precisión de CPU + %1$s%2$s - Modo sobremesa - Emula en modo sobremesa, lo que aumenta la resolución perjudicando el rendimiento. + Modo Sobremesa + Incrementa la resolución al coste de reducir el rendimiento. El Modo Portátil es usado cuando está desactivado, reduciendo la resolución y mejorando así el rendimiento. Región emulada Idioma emulado - Seleccionar Fecha RTC - Seleccionar Tiempo RTC - Habilitar RTC Personalizado - Esta configuración le permite configurar un reloj de tiempo real personalizado diferente a la hora actual de su sistema - Establecer RTC Personalizado + Seleccionar fecha RTC + Seleccionar tiempo RTC + RTC personalizado + Te permite tener un reloj personalizado en tiempo real diferente del tiempo del propio sistema. + Configurar RTC personalizado - API Nivel de precisión - Resolución + Resolución (Portátil/Sobremesa) Modo VSync + Orientación Relación de aspecto Filtro de adaptación de ventana - Metodo Anti Aliasing + Método anti-aliasing Forzar velocidad al máximo (solo Adreno) Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas). Usar shaders asíncronos - Compila shaders de forma asincrónica, lo que reducirá los parones pero puede introducir fallos. - Habilitar la depuración de gráficos - Cuando esté marcado, la API de gráficos entra en un modo de depuración más lento. - Usar caché de shaders en disco - Reduzca los parones almacenando y cargando shaders generados en el disco. + Compila shaders de manera asíncrona, reduciendo los parones, pero puede introducir fallos. + Usar limpieza reactiva + Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento. + Caché de shaders en disco + Reduce los parones almacenando y cargando shaders generados. + + + CPU + Depuración de CPU + Pone la CPU en un modo de depuración lento. + GPU + API + Depuración de gráficos + Configura la API gráfica a un modo de depuración lento. + Fastmem + Motor de salida Volumen Especifica el volumen de la salida de audio. @@ -157,14 +212,24 @@ Configuración guardada Configuración guardada para %1$s Error guardando %1$s.ini: %2$s + Menú sin implementar Cargando... + Saliendo... ¿Desea restablecer esta configuración a su valor predeterminado? Restablecer a predeterminado ¿Restablecer todas las configuraciones? - Todas las configuraciones avanzadas se restablecerán a su configuración predeterminada. Esto no se puede deshacer. + Todas las opciones avanzadas se restablecerán a su configuración predeterminada. Esta acción no se puede deshacer. Reiniciar la configuracion Cerrar - Más información + Saber más + Auto + Enviar + Null + Importar + Exportar + La exportación falló + La importación falló + Cancelando Seleccionar driver GPU @@ -172,6 +237,7 @@ Instalar Predeterminado Usando el driver de GPU por defecto + ¡Driver no válido, utilizando el predeterminado del sistema! Driver GPU del sistema Instalando driver... @@ -182,10 +248,11 @@ Gráficos Audio Tema y color + Depuración Su ROM está encriptada - cartuchos de juegos o titulos instalados.]]> + cartuchos de juegos o títulos instalados.]]> 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. @@ -196,25 +263,25 @@ Salir de la emulación Hecho Contador de FPS - Alternar Controles - Centro Relativo del Stick - Deslizamiento de la Cruceta - Hápticos - Mostrar pantalla - Alternar Todo - Ajustar pantalla + Alternar controles + Centro relativo del stick + Deslizamiento de la cruceta + Toques hápticos + Mostrar overlay + Alternar todo + Ajustar overlay Escala Opacidad - Reiniciar pantalla - Editar pantalla - Pausar Emulación - Reanudar Emulación - Opciones de pantalla + Reiniciar overlay + Editar overlay + Pausar emulación + Despausar emulación + Opciones de overlay Cargando configuración... - Software del teclado + Teclado de software Abortar @@ -226,6 +293,9 @@ Error fatal Ocurrió un error fatal. Consulte el registro para obtener más detalles.\nContinuar con la emulación puede provocar bloqueos y errores. ¡Desactivar esta configuración reducirá significativamente el rendimiento de la emulación! Para obtener la mejor experiencia, se recomienda dejar esta configuración habilitada. + RAM de dispositivo: %1$s\nRecomendado: %2$s + %1$s %2$s + ¡No hay ningún juego ejecutable presente! Japón @@ -236,7 +306,14 @@ Corea Taiwán - + + Byte + KB + MB + GB + TB + PB + EB Vulkan @@ -274,6 +351,11 @@ FXAA SMAA + + Paisaje + Retrato + Auto + Predeterminado (16:9) Forzar 4:3 @@ -298,7 +380,7 @@ Construyendo shaders - Cambiar Tema + Cambiar tema Predeterminado Material You @@ -308,8 +390,22 @@ Claro Oscuro + + cubeb + - Usar Fondos Negros + Fondos oscuros Cuando utilice el modo oscuro, aplique fondos negros. - + + Picture in Picture + Minimizar ventana cuando esté en segundo plano + Pausar + Jugar + Mutear + Desmutear + + + Licencias + Upscaling de alta calidad de AMD + diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index 1e02828aa..5a827c50b 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -1,5 +1,5 @@ - + Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.<br /><br />Avant de commencer, veuillez localiser votre fichier prod.keys ]]> sur le stockage de votre appareil.<br /><br />En savoir plus]]> L\'émulation est active @@ -19,12 +19,13 @@ Jeux Sélectionnez votre dossier <b>de Jeux</b> avec le bouton ci-dessous. Terminé - Vous êtes prêt.\nProfitez de vos jeux ! + Vous êtes prêt.\nProfitez de vos jeux ! Continuer Suivant Retour Ajouter des jeux - Sélectionner votre dossier de jeux + Sélectionner le dossier des jeux + Terminé ! Jeux @@ -32,12 +33,13 @@ Paramètres Aucun fichier n\'a été trouvé ou aucun répertoire de jeu n\'a encore été sélectionné. Rechercher et filtrer les jeux - Sélectionner le dossier de jeux + Sélectionner le dossier des jeux Permet à yuzu de remplir la liste des jeux - Ne pas sélectionner le dossier des jeux ? + Ne pas sélectionner le dossier des jeux ? Les jeux ne seront pas affichés dans la liste des jeux si aucun dossier n\'est sélectionné. https://yuzu-emu.org/help/quickstart/#dumping-games Rechercher des jeux + Rechercher un paramètre Répertoire de jeux sélectionné Installer prod.keys Nécessaire pour décrypter les jeux commerciaux. @@ -46,7 +48,7 @@ https://yuzu-emu.org/help/quickstart/#guide-introduction Notifications Accordez l\'autorisation de notification avec le bouton ci-dessous. - Donner la permission + Accorder la permission Ne pas accorder la permission de notification ? yuzu ne pourra pas vous communiquer d\'informations importantes. Permission refusée @@ -61,12 +63,15 @@ 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 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys Le fichier sélectionné est incorrect ou corrompu. Veuillez dumper à nouveau vos clés. Installer le pilote du GPU - Installez des pilotes alternatifs pour des performances ou une précision potentiellement meilleures + Installer des pilotes alternatifs pour des performances ou une précision potentiellement meilleures Paramètres avancés + Paramètres avancés : %1$s Configurer les paramètres de l\'émulateur Joué récemment Ajouté récemment @@ -86,6 +91,33 @@ Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu. Importer Exporter + Installer le firmware + Le firmware doit être dans une archive ZIP et est nécessaire pour démarrer certains jeux. + Installation du firmware + Firmware installé avec succès + L\'installation du firmware a échoué + Assurez-vous que les fichiers NCA du firmware se trouvent à la racine du fichier ZIP, puis réessayez. + Partager les logs de débogage + Partagez le fichier de log de yuzu pour déboguer les problèmes. + Aucun fichier de log trouvé + Installer le contenu du jeu + Installer une mise à jour ou un DLC + Installation du contenu en cours... + Erreur lors de l\'installation du fichier dans la NAND + Veuillez vous assurer que le contenu est valide et que le fichier prod.keys est installé. + L\'installation de jeux de base n\'est pas autorisée afin d\'éviter d\'éventuels conflits. + Seuls les contenus NSP et XCI sont pris en charge. Veuillez vérifier que le contenu du jeu est valide. + %1$d erreur(s) d\'installation + Contenu du jeu installé avec succès + %1$d installé avec succès + %1$d écrasé avec succès + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + Pilotes personnalisés non supporté + Le chargement des pilotes personnalisés ne sont pas actuellement pris en charge pour ce périphérique. Vérifiez à nouveau cette option à l\'avenir pour voir si la prise en charge a été ajoutée ! + Gérer les données de yuzu + Importer/exporter le firmware, les clés, les données utilisateur, et bien plus encore ! + Partager le fichier de sauvegarde + Échec de l\'exportation de la sauvegarde Gaia n\'est pas réel @@ -94,7 +126,18 @@ Contributeurs Fait avec \u2764 de l\'équipe yuzu https://github.com/yuzu-emu/yuzu/graphs/contributors + Des projets qui rendent possible yuzu pour Android Build + Données utilisateur + Importer/exporter toutes les données de l\'application.\n\nLors de l\'importation des données utilisateur, toutes les données utilisateur existantes seront supprimées ! + Exportation des données utilisateur... + Importation des données utilisateur... + Importer des données utilisateur + Backup yuzu invalide + Les données utilisateur ont été exportés avec succès + Les données utilisateur ont été importées avec succès + Exportation annulée + Assurez-vous que les dossiers de données utilisateur se trouvent à la racine du dossier ZIP et contiennent un fichier de configuration à config/config.ini, puis réessayez. https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu @@ -114,64 +157,87 @@ Es tu intéressé ? - Activer la vitesse limite - Lorsqu\'elle est activée, la vitesse d\'émulation sera limitée à un pourcentage spécifié de la vitesse normale. + Limitation de vitesse + Limiter la vitesse d\'émulation à un pourcentage spécifié de la vitesse normale Limite en pourcentage de vitesse - Spécifie le pourcentage pour limiter la vitesse d\'émulation. Avec la valeur par défaut de 100%, l\'émulation sera limitée à la vitesse normale. Des valeurs supérieures ou inférieures augmenteront ou diminueront la limite de vitesse. + Spécifier le pourcentage pour limiter la vitesse d\'émulation. 100% correspond à la vitesse normale. Des valeurs plus élevées ou plus basses augmenteront ou diminueront la limite de vitesse. Précision du CPU + %1$s%2$s Mode TV - Émuler en mode TV augmente la résolution au détriment des performances. + Augmenter la résolution, ce qui diminue les performances. Le mode portable est utilisé lorsque la fonction est désactivée, ce qui réduit la résolution et améliore les performances. Région émulée Langue émulée Sélectionner la date RTC Sélectionner l\'heure RTC - Activer l\'horloge RTC personnalisée - Ce paramètre vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système. + RTC personnalisé + Vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système. Définir l\'horloge RTC personnalisée - API Niveau de précision - Résolution + Résolution (Mode Portable/Mode TV) Mode VSync + Orientation Format Filtre de fenêtre adaptatif - Méthode d\'anticrénelage : - Forcer la fréquence d\'horloge maximale (Adreno uniquement) - Force le GPU à fonctionner au maximum d\'horloges possibles (les contraintes thermiques seront toujours appliquées). + Méthode d\'anticrénelage + Forcer les fréquences maximales (Adreno uniquement) + Forcer le GPU à fonctionner à ses fréquences maximales possibles (les contraintes thermiques seront toujours appliquées). Utiliser les shaders asynchrones - Compile les shaders de manière asynchrone, ce qui réduira les saccades mais peut entraîner des problèmes visuels. - Activer le débogage des graphismes - Lorsque cette case est cochée, l\'API graphique entre dans un mode de débogage plus lent. - Utiliser les shader cache de disque - Réduire les saccades en stockant et en chargeant les shaders générés sur le disque. + Compile les shaders de manière asynchrone, réduisant les saccades mais pouvant entraîner des problèmes visuels. + Utiliser le vidage réactif + Améliore la précision du rendu dans certains jeux au détriment des performances. + Utiliser les shader cache + Réduire les saccades en stockant et en chargeant localement les shaders générés + + + CPU + Débogage du CPU + Place le CPU en mode lent de débogage. + GPU + API + Débogage des graphismes + Définit l\'API graphique en mode de débogage lent. + Fastmem + Moteur de sortie Volume - Spécifie le volume de la sortie audio. + Spécifier le volume de la sortie audio. - Défaut + Par défaut Paramètres enregistrés Paramètres enregistrés pour %1$s Erreur lors de l\'enregistrement de %1$s.ini: %2$s + Menu non implémenté Chargement... - Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ? + Extinction en cours... + Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ? Réinitialiser par défaut Réinitialiser tous les réglages ? Tous les paramètres avancés seront réinitialisés à leur configuration par défaut. Ça ne peut pas être annulé. Paramètres réinitialisés Fermer - Plus d\'informations + En savoir plus + Auto + Soumettre + Nul + Importer + Exporter + L\'exportation a échoué + L\'importation a échoué + Annulation Sélectionner le pilote du GPU Souhaitez vous remplacer votre pilote actuel ? Installer - Défaut - Utilisation du pilote de GPU par défaut + Par défaut + Utilisation du pilote du GPU par défaut + Pilote non valide sélectionné, utilisation du paramètre par défaut du système ! Pilote du GPU du système Installation du pilote... @@ -182,13 +248,14 @@ Vidéo Audio Thème et couleur + Débogage Votre ROM est cryptée - cartouches de jeu ou titres installés.]]> + cartouches de jeu ou de vos titres installés.]]> 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 du GPU incompatible. L\'installation d\'un pilote du GPU personnalisé peut résoudre ce problème. + 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 @@ -198,8 +265,8 @@ Compteur FPS Activer/Désactiver les contrôles Centre du stick relatif - Glissement du DPad - Haptique + Glissement du D-pad + Toucher haptique Afficher l\'overlay Tout basculer Ajuster l\'overlay @@ -225,7 +292,10 @@ Erreur de sauvegarde/chargement Erreur fatale Une erreur fatale s\'est produite. Consultez les logs pour plus de détails.\nContinuer l\'émulation peut entraîner des plantages et des bogues. - La désactivation de ce paramètre réduira considérablement les performances d\'émulation ! Pour une expérience optimale, il est recommandé de laisser ce paramètre activé. + La désactivation de ce paramètre réduira considérablement les performances d\'émulation ! Pour une expérience optimale, il est recommandé de laisser ce paramètre activé. + Mémoire RAM de l\'appareil : %1$s\nRecommandé : %2$s + %1$s %2$s + Aucun jeu démarreable présent ! Japon @@ -236,7 +306,14 @@ Corée Taïwan - + + Octet + Ko + Mo + GB + To + Po + Eo Vulkan @@ -274,6 +351,11 @@ FXAA SMAA + + Paysage + Portrait + Auto + Par défaut (16:9) Forcer le 4:3 @@ -288,8 +370,8 @@ Pavé directionnel - Stick Gauche - Stick Droit + Stick gauche + Stick droit Home Capture d\'écran @@ -299,7 +381,7 @@ Changer le thème de l\'application - Défaut + Par défaut Material You @@ -308,8 +390,22 @@ Lumineux Sombre - - Utiliser des arrière-plans noirs - Lorsque vous utilisez le thème sombre, appliquer des arrière-plans noirs. + + cubeb - + + Arrière-plan noir + Lorsque vous utilisez le thème sombre, appliquer un arrière-plan noir. + + + Lecteur réduit + Réduire la fenêtre lorsqu\'elle est placée en arrière-plan + Pause + Jouer + Couper le son + Remettre le son + + + Licences + Mise à l\'échelle de haute qualité par AMD. + diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml new file mode 100644 index 000000000..0af78a57c --- /dev/null +++ b/src/android/app/src/main/res/values-he/strings.xml @@ -0,0 +1,367 @@ + + + + התוכנה תריץ משחקים לקונסולת ה Nintendo Switch. אף משחק או קבצים בעלי זכויות יוצרים נכללים.<br /><br /> לפני שאת/ה מתחיל בבקשה מצא את קובץ prod.keys]]> על המכשיר.<br /><br />קרא עוד]]> + אמולציה פעילה + מציג התראה מתמשכת כאשר האמולציה פועלת. + yuzu רץ + התראות ותקלות + מציג התראות כאשר משהו הולך לא כשורה. + הרשאות התראות לא ניתנה! + + + ברוכים הבאים! + למד איך להפעיל <b>yuzu</b> וקפוץ ישר לאמולציה. + כדי להתחיל + מפתחות + בחר את קובץ ה <b>prod.keys</b> שלך עם הכפתור למטה. + בחר מפתחות + משחקים + בחר את התיקיית ה <b>Games</b> שלך עם הכפתור למטה. + סיום + את/ה מוכן. \nתהנה/י מהמשחקים שלך + המשך + הבא + אחורה + הוסף משחקים + בחר/י את תיקיית המשחקים שלך + הושלם! + + + משחקים + חפש + הגדרות + לא נמצאו קבצים או לנבחרה ספריית קבצים בינתיים. + חפש וסנן משחקים + בחר תיקיית משחקים + אפשר ל yuzu לאכלס את רשימת המשחקים + לדלג על בחירת תיקיית המשחקים? + משחקים לא יוצגו ברשימת המשחקים אם לנבחרה תיקיית משחקים. + https://yuzu-emu.org/help/quickstart/#dumping-games + חפש משחקים + חפש בהגדרות + ספריית משחקים נבחרה + התקן prod.keys + הכרחי בכדי לפענח משחקים + לדלג על הוספת מפתחות? + מפתחות חוקיים הכרחיים כדי לשחק במשחקים. רק אפליקציות פירטיות יפעלו אם תמשיך. + https://yuzu-emu.org/help/quickstart/#guide-introduction + התראות + תן גישה להתראות עם הכפתור למטה. + תן הרשאה + דלג על מתן הרשאה להתראות? + yuzu לא יוכל להתריע לך על מידע חשוב. + הרשאה נדחתה + את/ה דיחת את ההרשאה יותר מדי פעמים ועכשיו את/ה צריך/ה לתת גישה באופן ידני בהגדרות. + אודות + מספר גירסה, קרדיטים ועוד + עזרה + דלג + ביטול + התקן מפתחות Amiibo + נחוץ כדי להשתמש ב Amiibo במשחק + קובץ מפתחות לא חוקי נבחר + מפתחות הותקנו בהצלחה + שגיאה בקריאת מפתחות ההצפנה + ודא שלקובץ המפתחות שלך יש סיומת של key. ונסה/י שוב. + ודא/י שלקובץ המפתחות שלך יש סיומת של bin. ונסה/י שוב. + מפתחות הצפנה לא חוקיים + https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys + קבוץ שנבחר מושחת או לא נכון. בבקשה הוצא מחדש את המפתחות שלך. + התקן דרייבר למעבד הגרפי + התקן דרייברים אחרים בשביל סיכוי לביצועים או דיוק גבוההים יותר + הגדרות מתקדמות + הגדרות מתקדמות: %1$s + הדר את הגדרות האמולטור + שוחק לאחרונה + הוסף לאחרונה + קמעונאי + Homebrew + פתח את תיקיית yuzu + נה ל את הקבצים הפנימיין של yuzu + ערוך את נראות האפליקציה + לא נמצא מנהל קבצים + לא יכול לפתוח את ספריית yuzu + בבקשה מקם את תיקיית המשתמש בפנל הצידי של מנהל הקבצים באופן ידני. + נהל מידע שמור + מידע שמור לא נמצא. בבקשה בחר/י אופציה מלמטה + יבא או יצא קבצי שמירה + יובא בהצלחה + מבנה ספריית השמירות לא חוקי + התת תיקייה הראשונה חייב להיות ה title ID של המשחק + ייבוא + ייצוא + התקן firmware + ה frimware חייב להיות בקובץ zip והוא הכרחי להפעלת חלק מהמשחקים + מתקין frimware + ה frimware הותקן בהצלחה + התקנת ה frimware נכשלה + ודא שקבצי ה firmware nca נמצאים בשורש ה zip ונסה שוב. + שתף את יומני הרישום של מיפוי הבאגים + שתף את קובץ יומני הרישום של yuzu בכדי לתקן בעיות + לא נמצא קובץ יומן רישום + התקן תוכן משחק + התקן עדכוני משחק או DLC + מתקין תוכן... + תקלה בהתקנת הקובץ (או קבצים) ל NAND + בבקשה ודא שהתוכן (או תכנים) חוקיים ושקובץ ה prod.keys מותקן. + התקנת משחק בסיס נדחת בכדי להימנע מקונפליקטים אפשריים. + רק קבצי NSP ו XCI נתמכים. בבקשה ודא שתוכן (או תכנים) המשחק חוקי. + %1$dבעיה (בעיות) התקנה + תוכן (או תכני) המשחק הותקנו בהצלחה + %1$d הותקן בהצלחה + %1$d נדרס/נכתב מעל בהצלחה + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + דרייברים מותאמים אישית לא נתמכים + הטענת דרייבים מותאמים אישית לא נתמך כרגע על מכשיר זה. \nבבקשה בדוק אופציה זו בעתיד בכדי לראות אם נוספה תמיכה! + נהל את המידע של yuzu + יבא/יצא firmware, keys, מידע של משתמש ועוד! + שתף קובץ שמירה + נכשל בייצוא שמירה + + + Gaia לא אמיתית + הועתק ללוח + אמולטור Switch עם קוד פתוח + תורמים + נוצר עם \u2764 מקבוצת yuzu + https://github.com/yuzu-emu/yuzu/graphs/contributors + פרוייקטים שהופכים את yuzu ל Android אפשרי + גרסה + נתוני משתמש + יבא/יצא את כל נתוני האפליקציה.\n\nכאשר מייבאים את נתוני המשתמש, כל נתוני המשתמש הקיימים ימחקו! + מייצא נתוני משתמש... + מייבא נתוני משתמש... + יבא נתוני משתמש + גיבוי yuzu לא חוקי + נתוני משתמש יוצאו בהצלחה + נתוני משתמש יובאו בהצלחה + ייצוא בוטל + ודא שנתוני המשתמש נמצאים בשורש קובץ ה zip ושהוא מכיל קובץ סידור ב config/config.ini ונסה שוב. + https://discord.gg/u77vRWY + https://yuzu-emu.org/ + https://github.com/yuzu-emu + + + גישה מוקדמת + קבל גישה מוקדמת + https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea + תכונות חותכות קצה, גישה מוקדמת לעדכונים, ועוד + יתרונות של גישה מקודמת + תכונות חותכות קצה + גישה מוקדמת לעדכונים + ללא התקנה ידנית + תמיכה בעדיפות + עוזר בשמירת משחקים + התודה האינסופית שלנו + אתה מעוניין? + + + הגבל מהירות + מגביל את מהירות האמולציה לאחוז מהירות המבוקש מהמהירות הרגילה. + הגבל את אחוז המהירות + מדייק את אחוז מהירות האמולציה. 100% זה מהירות רגילה. ערכים גדולים או קטנים יאיצו או יאטו את מהירות האמולציה. + דיוק המעבד + %1$s%2$s + + + מצב עגינה + מעלה את הרזולוציה, פוגע בביצועים. משתמש במצב נייד כאשר מנוטרל, מפחית את הרזולוציה ומעלה את הביצועים. + אזור אמולציה + שפת אמולציה + בחר תאריך RTC + בחר זמן RTC + RTC מותאם אישית + מאפשר לך לקבוע שעון זמן אמת נפרד משעון המערכת שלך. + קבע RTC מותאם אישית + + + רמת דיוק + רזולוציה (מעוגן/נייד) + מצב VSync + כיוון + יחס רוחב גובה + פילטר מתאם חלון + שיטת Anti-aliasing + החזק מהירות שעון מקסימלית (רק ל Adreno) + מכריח לדחוף את מהירויות המעבד הגרפי למקסימום (הגבלות חום ימשיכו לתפקד). + משפר את הדיוק של האמולציה במשחקים מסויימים במחיר של ביצועים. + + מעבד + מכניס את המעבד למצב דיבאג איטי + מעבד גרפי + + מנוע פלט + עוצמת שמע + + ברירת מחדל + הגדרות שמורות + הגדרות שמורות עבור %1$s + תקלה בשמירת %1$s.ini: %2$s + טוען... + כיבוי... + אתה מעוניין לאפס את ההגדרה הזו חזרה לברירת המחדל? + אפס לברירת המחדל + לאפס את כל ההגדרות? + כל ההגדרות המתקדמות יאופסו לברירת המחדל. לא ניתן לבטל פעולה זו. + אפס הגדרות + סגור + למד עוד + אוטומטי + שלח + ייבוא + ייצוא + ייצוא נכשל + ייבוא נכשל + מבטל + + + בחר דרייבר למעבד הגרפי + אתה מעוניין להחליף את הדרייבר של המעבד הגרפי שלך? + התקן + ברירת מחדל + משתמש בדרייבר ברירת המחדל של המעבד הגרפי + דרייבר לא חוקי נבחר, משתמש בברירת המחדל של המערכת! + דרייבר של המעבד הגרפי של המערכת + מתקין דרייבר... + + + הגדרות + כללי + מערכת + גרפיקה + שמע + צבע ונושא + + המשחק שלך מוצפן + אין אפשרות לטעון את המשחק + קובץ המשחק לא קיים + + + צא מהאמולציה + סיום + סופר FPS + קנה מידה + שקיפות + עצור אמולציה + המשך אמולציה + טוען הגדרות... + + + מקלדת תוכנה + + + אודות + המשך + ארכיון מערכת לא נמצא + %s חסר. בבקשה הוצא תא ארכיוני המערכת שלך./nהמשכת האמולציה עלולה לגרום לקריסות ובאגים. + ארכיון מערכת + בעיית שמירה/טעינה + שגיאה חמורה + RAM המכשיר: %1$s/nמומלץ: %2$s + %1$s%2$s + אין משחק שניתן להריץ! + + + יפן + ארה״ב + אירופה + אוסטרליה + סין + קוריאה + טייוואן + + + בייט + KB + MB + GB + TB + PB + EB + + + Vulkan + אין שום דבר + + + רגיל + גבוה + אקסטרים (איטי) + + + 0.5X (360p/540p) + 0.75X (540p/810p) + 1X (720p/1080p) + 2X (1440p/2160p) (איטי) + 3X (2160p/3240p) (איטי) + 4X (2880p/4320p) (איטי) + + תיבת דואר + FIFO (On) + FIFO נינוח + + + השכן הקרוב ביותר + ScaleForce + AMD FidelityFX™ Super Resolution + + + אין שום דבר + FXAA + SMAA + + + לרוחב + לאורך + אוטומטי + + + ברירת מחדל (16:9) + הכרח 4:3 + הכרח 21:9 + הכרח 16:10 + הרחב לגודל המסך + + + מדויק + לא בטוח + פראנואידי (איטי) + + + D-pad + ג׳ויסטיק שמאלי + ג׳ויסטיק ימני + בית + צילום מסך + + + שנה את נושא האפליקצייה + ברירת מחדל + חומר אתה/מאטיריאל יו + + + שנה את מצב הנושא + עקוב אחרי המערכת + בהיר + כהה + + + cubeb + + + רקעים שחורים + כשמתשמשים במצב כהה, שם רקעים שחורים. + + + תמונה בתוך תמונה + הקטן את החלון כאשר נמצא ברקע + עצור + שחק + השתק + בטל השתקה + + + רישיונות + אפסקיילינג באיכות גבוהה מ AMD + diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml new file mode 100644 index 000000000..6563ba288 --- /dev/null +++ b/src/android/app/src/main/res/values-hu/strings.xml @@ -0,0 +1,402 @@ + + + + Ez a szoftver Nintendo Switch játékkonzolhoz készült játékokat futtat. Nem tartalmaz játékokat vagy kulcsokat. .<br /><br />Mielőtt hozzákezdenél, kérjük, válaszd ki a prod.keys]]> fájl helyét a készülék tárhelyén<br /><br />Tudj meg többet]]> + Emuláció aktív + Állandó értesítést jelenít meg, amíg az emuláció fut. + A yuzu fut + Megjegyzések és hibák + Értesítések megjelenítése, ha valami rosszul sül el. + Nincs engedély az értesítés megjelenítéséhez! + + + Üdvözöljük! + Ismerkedj meg a <b>yuzu</b> beállításával és ugorj bele az emulációba. + Vágjunk bele + Kulcsok + Válaszd ki a(z) <b>prod.keys</b> fájlodat az alábbi gombbal. + Kulcsok kiválasztása + Játékok + +Válaszd ki a(z) <b>Games</b> mappát az alábbi gombbal. + Kész + Minden kész.\nJó szórakozást! + Folytatás + Következő + Vissza + Játékok hozzáadása + Játékaid mappa kiválasztása + Kész! + + + Játékok + Keresés + Beállítások + Nem található fájl, vagy még nincs kiválasztva könyvtár. + Játékok keresése és szűrése + Játékmappa kiválasztása + Kihagyod a játékok mappa kiválasztását? + A játékok nem jelennek meg a Játékok listában, ha egy mappa nincs kijelölve. + https://yuzu-emu.org/help/quickstart/#dumping-games + Játékok keresése + Beállítások keresése + Játékok könyvtár kiválasztva + prod.keys telepítése + Kiskereskedelmi játékok dekódolásához szükséges + Kihagyod a kulcsok hozzáadását? + A kiskereskedelmi játékok emulálásához érvényes kulcsokra van szükség. Csak a homebrew alkalmazások fognak működni, ha folytatod. + https://yuzu-emu.org/help/quickstart/#guide-introduction + Értesítések + Értesítési engedélyek megadása az alábbi gombbal. + Engedély megadása + Kihagyod az értesítési engedély megadását? + yuzu nem fog tudni értesíteni a fontos imformációkról + Engedély megtagadva + Túl gyakran utasítottad el a hozzáférést, így manuálisan kell jóváhagynod a rendszer beállításokban. + A programról + Build verzió, készítők, és még több + Segítség + Kihagyás + 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 + https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys + A kiválasztott fájl helytelen, vagy sérült. Állíts össze egy új kulcsot. + 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 + Haladó beállítások + Haladó beállítások: %1$s + Emulátorbeállítások konfigurálása + Nemrég játszva + Nemrég hozzáadva + Kiskereskedelmi + yuzu mappa megnyitása + yuzu belső fájljainak kezelése + Az alkalmazás megjelenésének módosítása + Nem található fájlkezelő + Nem sikerült megnyitni a yuzu könyvtárat + Kérjük, manuálisan keresd meg a felhasználói mappát a fájlkezelő oldalsó paneljével. + Mentésadatok kezelése + Mentés található. Kérjük, válassz egyet az alábbi opciók közül. + Mentési fájlok importálás vagy exportálása + Sikeresen importálva + Érvénytelen mentési könyvtárstruktúra + Az első almappa neve a játék azonosítója kell, hogy legyen. + Importálás + Exportálás + Firmware telepítés + A firmwarenek ZIP archívumban kell lennie, és szükséges a játékok indításához + Firmware telepítése + Firmware sikeresen telepítve + Firmware telepítése sikertelen + Győződj meg róla, hogy a firmware nca fájlok a zip gyökerénél vannak, és próbáld meg újra. + Hibakereső logok megosztása + A yuzu naplófájl megosztása a problémák elhárításához + Nem található log fájl + Játéktartalom telepítése + Játékfrissítések vagy DLC telepítése + Tartalom telepítése... + Hiba történt a fájl(ok) NAND-ra telepítése közben + Győződj meg róla, hogy a tartalom valós, és a prod.keys fájl telepítve van. + Az alapjátékok telepítése nem engedélyezett az esetleges konfliktusok elkerülése érdekében. + Csak NSP és XCI tartalom támogatott. Győződj meg róla, hogy a játéktartalom érvényes. + %1$d telepítési hiba + Játéktartalom sikeresen telepítve + %1$d sikeresen telepítve + %1$d sikeresen felülírva + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + Egyéni illesztőprogramok nem támogatottak + Egyéni illesztőprogram telepítése jelenleg nem támogatott ezen az eszközön.\nNézz vissza később, hátha hozzáadtuk a támogatását! + yuzu adatok kezelése + Firmware, kulcsok, felhasználói adatok és egyebek importálása/exportálása + Mentési fájl megosztása + A mentés exportálása sikertelen + + + Gaia nem valódi + Másolva a vágólapra + Egy nyílt forráskódú Switch emulátor + Hozzájárulók + \u2764 által készítve a yuzu csapattól + https://github.com/yuzu-emu/yuzu/graphs/contributors + Projektek, amik nélkül a yuzu nem jöhetett volna létre Androidra + Felhasználói adatok + Az összes alkalmazásadat importálása/exportálása.\n\nA felhasználói adatok importálásakor az összes meglévő felhasználói adat törlődik! + Felhasználói adatok exportálása... + Felhasználói adatok importálása... + Felhasználói adatok importálása + Érvénytelen yuzu biztonsági másolat + Felhasználói adatok sikeresen exportálva + Felhasználói adatok sikeresen importálva + Exportálás megszakítva + Ellenőrizd, hogy a felhasználói adatok mappái a zip mappa gyökerében vannak, és tartalmaznak egy konfig fájlt a config/config.ini címen, majd próbáld meg újra. + https://discord.gg/u77vRWY + https://yuzu-emu.org/ + https://github.com/yuzu-emu + + + Korai hozzáférés + Szerezz korai hozzáférést + https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea + Legújabb funkciók, korai hozzáférés a frissítésekhez, és sok más + Korai hozzáférés előnyei + Legújabb funkciók + Korai hozzáférés a frissítésekhez + Automatikus telepítések + Priorizált támogatás + Valamint az örök hálánk + Érdekel a dolog? + + + Sebességkorlát + Korlátozza az emuláció sebességét a normál sebesség adott százalékára. + Sebességkorlát százaléka + Az emuláció sebességét határozza meg. 100% a normál sebesség. A magasabb értékek növelik, az alacsonyabbak csökkentik a sebességkorlátot. + CPU pontosság + %1$s%2$s + + + Dokkolt mód + Növeli a felbontást, de csökkenti a teljesítményt. Kikapcsolás esetén a Kézi mód van használatban, ami kisebb felbontást, de nagyobb teljesítményt eredményez. + Emulált régió + Emulált nyelv + Válassz RTC dátumot + Válassz RTC időt + Egyéni RTC + Megadhatsz egy valós idejű órát, amely eltér a rendszer által használt órától. + Egyéni RTC beállítása + + + Pontosság szintje + Felbontás (Kézi/Dockolt) + VSync mód + Orientáció + Képarány + Ablakhoz alkalmazkodó szűrő + Élsimítási módszer + Maximum órajel kényszerítése (csak Adreno) + Kényszeríti a GPU-t a lehető legnagyobb órajelen működésre (a hőmérséklet korlátozások továbbra is érvényben maradnak). + Aszinkron árnyékolók használata + Aszinkron módon fordítja az árnyékolókat, ami csökkenti az akadozást, de hibákat okozhat. + Reaktív ürítés használata + Javítja a renderelési pontosságot néhány játékban a teljesítmény rovására. + Lemez árnyékoló gyorsítótár + Csökkenti az akadásokat azáltal, hogy helyileg tárolja és tölti be a generált árnyékolókat. + + + CPU + CPU hibakeresés + Lassú hibakereső módba állítja a CPU-t. + GPU + API + Grafikai hibakeresés + Lassú hibakeresési módba állítja a grafikus API-t . + + Kimeneti rendszer + Hangerő + Hangkimenet hangerejének megadása + + + Alapértelmezett + Beállítások elmentve + Beállítások elmentve a következőhöz: %1$s + Mentési hiba%1$s .ini: %2$s + Nem implementált menü + Betöltés... + Leállítás... + Szeretnéd visszaállítani a beállítások az alapértelmezett értékekre? + Alaphelyzetbe állítás + Alaphelyzetbe állítod a beállításokat? + Minden haladó beállítás vissza lesz állítva az alapértelmezett konfigurációra. Ez a művelet nem vonható vissza. + Beállítások alaphelyzetbe állítva + Bezárás + Tudj meg többet + Automatikus + Küldés + Nulla + Importálás + Exportálás + Exportálás sikertelen + Importálás sikertelen + Megszakítás + + + Válassz GPU illesztőprogramot + Szeretnéd lecserélni a jelenlegi GPU illesztőprogramot? + Telepítés + Alapértelmezett + Alapértelmezett GPU illesztőprogram használata + Érvénytelen driver kiválasztva, a rendszer alapértelmezett lesz használva! + Rendszer GPU illesztőprogram + Illesztőprogram telepítése... + + + Beállítások + Általános + Rendszer + Grafika + Hang + Téma és színek + Hibakeresés + + + ROM titkosítva + 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 + + + Emuláció bezárása + Kész + FPS számláló + Irányítás átkapcsolása + D-pad csúsztatása + Érintés haptikája + Átfedés mutatása + Össze átkapcsolása + Átfedés testreszabása + Skálázás + Átlátszóság + Átfedés visszaállítása + Átfedés módosítása + Emuláció szünetelése + Emuláció folytatása + Átfedés beállításai + + Beállítások betöltése... + + + Szoftver billenytűzet + + + Megszakítás + Folytatás + Nem található rendszerarchívum + %s hiányzik. Kérjük, mentsd ki a rendszerarchívumaidat.\nAz emuláció folytatása összeomlásokhoz és hibákhoz vezethet. + Egy rendszerarchívum + Mentési/betöltési hiba + Végzetes hiba + Végzetes hiba történt. Ellenőrizd a logot a részletekért.\nAz emuláció folytatása összeomlást és hibákat eredményzhet. + Ennek a beállításnak a kikapcsolása jelentős mértékben csökkenti a teljesítményt! A legjobb élmény érdekében javasolt a beállítás bekapcsolva tartása. + Eszköz RAM: %1$s\nAjánlott: %2$s + %1$s %2$s + Nincs indítható játék! + + + Japán + USA + Európa + Ausztrália + Kína + Korea + Tajvan + + + Bájt + KB + MB + GB + TB + PB + EB + + + Vulkan + Nincs + + + Normál + Magas + Extrém (Lassú) + + + 0.5X (360p/540p) + 0.75X (540p/810p) + 1X (720p/1080p) + 2X (1440p/2160p) (Lassú) + 3X (2160p/3240p) (Lassú) + 4X (2880p/4320p) (Lassú) + + + Azonnali (Ki) + Postaláda + FIFO (Be) + FIFO Relaxált + + + Legközelebbi szomszéd + Bilineáris + Bikubikus + Gauss-féle + ScaleForce + AMD FidelityFX™ Super Resolution + + + Nincs + FXAA + SMAA + + + Fekvő + Álló + Automatikus + + + Alapértelmezett (16:9) + 4:3 kényszerítése + 21:9 kényszerítése + 16:10 kényszerítése + Ablakhoz nyújtás + + + Pontos + Nem biztonságos + Paranoid (Lassú) + + + D-pad + Bal kar + Jobb kar + Home + Képernyőmentés + + + Árnyékolók előkészítése + Árnyékolók létrehozása + + + Alkalmazás témájának módosítása + Alapértelmezett + + Téma váltása + Rendszerbeállítások használata + Világos + Sötét + + + cubeb + + + Fekete háttér + Sötét téma használatakor fekete háttér használata. + + + Kép a képben + Ablak minimalizálása, amikor háttérbe kerül + Szünet + Lejátszás + Némítás + Némítás feloldása + + + Licenszek + Magas minőségű felskálázás az AMD-től + diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml index 09c9345b0..5afebb4c4 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml @@ -1,5 +1,5 @@ - + Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.<br /><br />Prima di iniziare, perfavore individua il file prod.keys ]]> nella memoria del tuo dispositivo.<br /><br />Scopri di più]]> L\'emulatore è attivo @@ -13,9 +13,9 @@ Benvenuto! Scopri come configurare <b>yuzu</b> e passare all\'emulazione. Iniziare - Pulsanti + Chiavi Seleziona il tuo file <b>prod.keys</b> con il pulsante in basso. - Selezione Pulsanti + Seleziona le chiavi Giochi Seleziona la cartella <b>Games</b> con il pulsante in basso. Fatto @@ -25,6 +25,7 @@ Indietro Aggiungi giochi Seleziona la cartella dei giochi + Completato! Giochi @@ -38,6 +39,7 @@ I giochi non saranno mostrati nella lista dei giochi se una cartella non è selezionata. https://yuzu-emu.org/help/quickstart/#dumping-games Cerca giochi + Cerca impostazione Cartella dei giochi selezionata Installa prod.keys Necessario per decrittografare i giochi @@ -61,15 +63,18 @@ 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 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys Il file selezionato è incorretto o corrotto. Per favore riesegui il dump delle tue chiavi. Installa i driver GPU Installa driver alternativi per potenziali prestazioni migliori o accuratezza. Impostazioni avanzate + Impostazioni Avanzate: %1$s Configura le impostazioni dell\'emulatore - Giocato recentemente - Aggiunto recentemente + Giocati recentemente + Aggiunti recentemente Rivenditore Homebrew Apri la cartella di yuzu @@ -86,6 +91,33 @@ La prima sotto cartella deve chiamarsi come l\'ID del titolo del gioco. Importa Esporta + Installa firmware + Il firmware deve essere in un archivio ZIP ed è necessario per avviare alcuni giochi + Installando il firmware + Firmware installato con successo + L\'installazione del firmware è fallita + Accertati che i file .nca del firmware siano contenuti direttamente nella radice dello .zip e riprova. + Condividi log di debug + Condividi i log di yuzu per ricevere supporto + Nessun file di log trovato + Installa contenuti di gioco + Installa aggiornamenti o DLC + Installazione dei contenuti... + Errore durante l\'installazione del contenuto in NAND. + Accertati che i contenuti da installare siano validi e che le prod.keys siano presenti. + Installare i giochi base in NAND non è permesso, perché potrebbe causare dei conflitti con altri tipi di contenuti(Aggiornamenti e DLC) + Solo i tipi NSP e XCI sono supportati. Verifica che i contenuti di gioco siano validi. + Errori di installazione: %1$d + Contenuto/i di gioco installato/i con successo. + %1$dinstallato con successo. + %1$dsovrascritto con successo + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + I driver personalizzati non sono supportati. + I driver personalizzati non sono attualmente supportati su questo dispositivo.\n Ricontrolla in futuro. + Gestisci i dati di Yuzu + Importa/Esporta il firmware, le keys, i dati utente, e altro! + Condividi i tuoi dati di salvataggio + Errore durante l\'esportazione del salvataggio Gaia non è reale @@ -94,7 +126,18 @@ Collaboratori Realizzato con \u2764 dal team yuzu https://github.com/yuzu-emu/yuzu/graphs/contributors + Progetti che rendono yuzu per Android possibile Compilazione + Dati Utente + Importa/Esporta tutti i dati dell\'applicazione.\n\nDurante l\'importazione dei Dati Utente, quelli già esistenti verranno ELIMINATI. + Esportazione dei Dati Utente... + Importazione dei Dati Utente... + Importa i Dati Utente + Backup di Yuzu Invalido + Dati Utente esportati con successo + Dati Utente importati con successo. + Esportazione annullata + Assicurati che la cartella dei Dati dell\'utente stiano nella radice del file.zip e che sia presente una cartella config in config/config.ini, poi, riprova. https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu @@ -114,41 +157,53 @@ Sei interessato? - Abilita il limite di velocità - Quando abilitato, la velocità di emulazione verrà limitata a una specifica percentuale della velocità normale. + Limita velocità + Limita la velocità dell\'emulazione a una specifica percentuale della velocità normale. Limite velocità percentuale - Specifica la percentuale del limite della velocità di emulazione. Con quella preimpostata al 100% l\'emulazione verrà limitata alla velocità normale. Valori più alti o bassi aumenteranno o diminuiranno il limite di velocità. + Specifica la percentuale per limitare la velocità di emulazione. 100% è la velocità normale. Valori maggiori o minori aumenteranno o diminuiranno il limite di velocità Accuratezza della CPU + %1$s%2$s - Modalità docked - Emula in modalità docked, questo aumenta la risoluzione a spese delle performance. + Modalità Docked + Aumenta la risoluzione, diminuendo le performance. La modalità portatile è usata quando disabilitato, diminuendo la risoluzione e aumentando le performance. Regione emulata Lingua emulata - Seleziona la data dall\'orologio in tempo reale - Seleziona il tempo dall\'orologio in tempo reale - Abilità l\'orologio in tempo reale personalizzato - Questa impostazione ti permette di impostare un orologio in tempo reale personalizzato separato da quello del tuo sistema corrente. - Imposta l\'orologio in tempo reale personalizzato + Imposta la data + Imposta l\'ora, i minuti e i secondi. + RTC Personalizzato + Ti permette di impostare un orologio in tempo reale personalizzato, completamente separato da quello di sistema. + Imposta un orologio in tempo reale personalizzato - API Livello di accuratezza - Risoluzione + Risoluzione (Portatile/Docked) Modalità VSync - Rapporto d\'aspetto - Filtro di adattamento alla finestra + Orientamento + Rapporto d\'aspetto: + Filtro adattivo della finestra Metodo di anti-aliasing Forza clock massimi (solo Adreno) Forza la GPU a girare col massimo clock possibile (i vincoli alla temperatura saranno comunque applicati) Usa shaders asincrone - Compila le shaders asincronamente, questo riduce lo shutter ma potrebbe introdurre dei glitch. - Abilità il debug grafico - Quando l\'opzione è selezionata, l\'API grafica entra in una modalità di debug più lenta - Usa cache shader su disco - Riduce lo stuttering salvando e caricando le shader generate sul disco. + Compila le shader in modo asincrone, riducendo lo stutter. Può causare glitch grafici. + Abilita il Reactive Flushing + Migliora l\'accuratezza della grafica in alcuni giochi, al costo delle performance. + Usa la cache delle shader + Riduce lo stuttering caricando le shader già compilate all\'avvio. + + + CPU + Debug della CPU + Imposta la CPU in modalità Debug (Più lento) + GPU + API + Debug GPU + Imposta l\'API grafica in uno stato dedicato al Debugging. Impatta di molto sulle performance. + Fastmem + Motore di Output Volume Specifica il volume dell\'audio in uscita. @@ -157,14 +212,24 @@ Impostazioni salvate Impostazioni salvate per %1$s Errore nel salvare %1$s.ini %2$s + Menu non implementato Caricamento… + Spegnimento... Vuoi ripristinare queste impostazioni al loro valore originale? Riportare alle impostazioni originali Resettare tutte le impostazioni? - Tutte le Impostazioni Avanzate saranno ripristinate a quelle originali. Questa operazione non è reversibile + Le impostazione avanzate verranno completamente reimpostate. Questa operazione è IRREVERSIBILE. Reimposta le impostazioni Chiudi Per saperne di più + Automatico + Invia + Nullo + Importa + Esporta + Esportazione Fallita + Importazione Fallita + Cancellazione Seleziona il driver della GPU @@ -172,6 +237,7 @@ Installa Predefinito Utilizza il driver predefinito della GPU. + Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema! Driver GPU del sistema Installando i driver... @@ -182,10 +248,11 @@ Grafica Audio Tema e colori + Debug La tua ROM è criptata - cartuccia di gioco o i titoli installati.]]> + dump delle tue cartucce di giocooppure dei titoli già installati.]]> 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. @@ -193,28 +260,28 @@ Il file della ROM non esiste - Uscire dall\'emulazione + Arresta emulazione Fatto - Contatore degli FPS + Contatore FPS Controlli a interruttore Centro relativo degli Stick - Slittamento del Pad Direzionale - Aptico - Mostra Overlay - Attiva/disattiva tutto - Aggiusta Overlay + DPad A Scorrimento + Feedback Aptico + Mostra l\'Overlay + Attiva/Disattiva tutto + Modifica l\'Overlay Scala Opacità - Reimposta Overlay - Modifica Overlay - Metti in pausa l\'emulazione - Riprendi Emulazione - Impostazioni Overlay + Reimposta l\'Overlay + Modifica l\'Overlay + Sospendi l\'emulazione + Riprendi l\'emulazione + Opzioni overlay - Caricamento delle impostazioni... + Carico le impostazioni... - Tastiera software + Tastiera Software Interrompi @@ -226,6 +293,9 @@ Errore Fatale Un errore fatale è accaduto. Controlla i log per i dettagli.\nContinuare ad emulare potrebbe portare bug o causare crash. Disattivare questa impostazione può ridurre significativamente le performance di emulazione! Per una migliore esperienza, è consigliato lasciare questa impostazione attivata. + RAM Totale:%1$s\nRaccomandati: %2$s + %1$s%2$s + Non è presente alcun gioco avviabile. Giappone @@ -236,7 +306,14 @@ Corea Taiwan - + + Byte + Kb + Mb + GB + Tb + Pb + Eb Vulkan @@ -274,12 +351,17 @@ FXAA SMAA + + Layout Orizzontale + Layout Verticale + Automatico + Predefinito (16:9) Forza 4:3 Forza 21:9 Forza 16:10 - Allunga a finestra + Adatta alla finestra Accurata @@ -287,9 +369,9 @@ Paranoico (Lento) - D-Pad - Levetta sinistra - Levetta destra + D-pad + Analogico sinistro + Analogico destro Home Screenshot @@ -298,7 +380,7 @@ Costruendo gli shaders - Cambia il tema dell\'app + Cambia tema dell\'app Predefinito Material You @@ -308,8 +390,22 @@ Chiaro Scuro + + cubeb + - Usa sfondi neri + Sfondi neri Quando utilizzi il tema scuro, applica sfondi neri. - + + Picture in Picture + Minimizza la finestra quando viene impostata in background + Pausa + Gioca + Silenzia + Riattiva + + + Licenze + Upscaling di alta qualità da parte di AMD + diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml index a0ea78bef..3be4e7d26 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml @@ -1,11 +1,12 @@ - + - このソフトウェアは、Nintendo Switch用のゲームを実行します。 ゲームソフトやキーは含まれません。<br /><br />事前に、 prod.keys ]]> ファイルをデバイスのストレージに配置しておいてください。<br /><br />詳細]]> + このソフトウェアでは、Nintendo Switchのゲームを実行できます。 ゲームソフトやキーは含まれません。<br /><br />事前に、 prod.keys ]]> ファイルをストレージに配置しておいてください。<br /><br />詳細]]> エミュレーションが有効です エミュレーションの実行中に常設通知を表示します。 yuzu は実行中です - 問題が発生したときに通知を表示します。 + 通知とエラー + 問題の発生時に通知を表示します。 通知が許可されていません! @@ -16,7 +17,7 @@ 下のボタンから <b>prod.keys</b> ファイルを選択してください。 キーを選択 ゲーム - 下のボタンから<b>ゲーム</b>があるフォルダを選択してください。 + 下のボタンから<b>ゲーム</b>のあるフォルダを選択してください。 完了 準備が完了しました。\nゲームをお楽しみください! 続行 @@ -24,48 +25,53 @@ 戻る ゲームを追加 ゲームフォルダを選択 + 完了! ゲーム 検索 設定 - ファイルが見つからないか、ゲームディレクトリがまだ選択されていません。 + ファイルが存在しないかゲームフォルダが選択されていません。 ゲームの検索と絞り込み - ゲームフォルダを選択 - yuzu がゲームリストに追加できるようにします + ゲームフォルダ + ゲームをyuzuのゲームリストに追加します ゲームフォルダの選択をスキップしますか? - フォルダを選択しない場合、ゲームはゲームリストに表示されません。 + フォルダを選択しないと、ゲームがリストに表示されません。 https://yuzu-emu.org/help/quickstart/#dumping-games ゲームを検索 - ゲームディレクトリが選択されました - prod.keys をインストール - ゲームの復号化に必要 + 検索設定 + フォルダを選択しました + prod.keys + 製品版ゲームの復号化に必要です キーの追加をスキップしますか? 製品版ゲームのエミュレーションには、有効なキーが必要です。続行すると自作アプリしか機能しません。 https://yuzu-emu.org/help/quickstart/#guide-introduction 通知 - 下のボタンで通知の権限を許可してください。 + 下のボタンで通知を許可してください。 許可 通知の許可をスキップしますか? yuzuは重要なお知らせを通知できません。 権限が拒否されました - この権限を複数回拒否したため、システム設定で手動で許可する必要があります。 + この権限を複数回拒否したため、設定から手動で許可する必要があります。 情報 ビルドバージョン、クレジットなど ヘルプ スキップ キャンセル - Amiibo キーをインストール - ゲーム内での Amiibo の使用に必要 - 無効なキーファイルが選択されました + Amiibo + ゲーム内での Amiibo の使用に必要です + 無効なキーファイルです 正常にインストールされました - 暗号化キーの読み取りエラー - 暗号化キーが無効です + 暗号化キーの読み込み失敗 + キーの拡張子が.keysであることを確認し、再度お試しください。 + キーの拡張子が.binであることを確認し、再度お試しください。 + 暗号化キーが無効 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys - 選択されたファイルが不正または破損しています。キーを再ダンプしてください。 - GPUドライバーをインストール + ファイルが間違っているか破損しています。キーを再ダンプしてください。 + GPUドライバー 代替ドライバーをインストールしてパフォーマンスや精度を向上させます 高度な設定 + 高度な設定: %1$s エミュレーターの設定を構成します 最近プレイした 最近追加された @@ -77,15 +83,34 @@ ファイルマネージャーが見つかりませんでした yuzuのディレクトリを開けません ファイルマネージャのサイドパネルでユーザーフォルダを手動で探してください。 - セーブデータを管理 - セーブデータが見つかりました。以下のオプションから選択してください。 + セーブデータ + セーブデータが見つかりました。操作を選択してください。 セーブファイルをインポート/エクスポート インポートが完了しました - セーブデータのディレクトリ構造が無効です + セーブデータのディレクトリ構造が無効 最初のサブフォルダ名は、ゲームのタイトルIDである必要があります。 インポート エクスポート - + ファームウェア + ファームウェアはZIPアーカイブである必要があり、一部のゲームを起動するのに必要です + ファームウェアをインストール中 + インストールが完了しました + インストール失敗 + デバッグログ + yuzuのログファイルを共有して問題をデバッグします + ログが見つかりません + 追加コンテンツ + 更新データやDLCをインストールします + コンテンツをインストール中... + NSPとXCI形式のコンテンツのみサポートされています。ゲームコンテンツが有効なものであるかご確認ください。 + %1$d のインストールエラー + ゲームコンテンツのインストールに成功しました + %1$d のインストールに成功しました + %1$d の上書きに成功しました + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + カスタムドライバはサポートされていません + yuzu データを管理 + セーブファイルを共有 ガイアは実在しない クリップボードにコピーしました @@ -93,7 +118,15 @@ 貢献者 yuzuチームの\u2764で作られた https://github.com/yuzu-emu/yuzu/graphs/contributors + yuzu for Androidの作成を可能にしたプロジェクト ビルド + ユーザデータ + ユーザデータをエクスポート中... + ユーザデータをインポート中... + ユーザデータをインポート + ユーザデータのエクスポートに成功しました + ユーザデータのインポートに成功しました + エクスポートをキャンセルしました https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu @@ -105,72 +138,91 @@ 最先端の機能、アップデートの早期アクセスなど 早期アクセスのメリット 最先端の機能 - アップデートの早期アクセス + アップデートへの早期アクセス 手動インストールが不要 - 優先的なサポート + 優先サポート ゲームの保存に貢献 - 私たちの永遠の感謝 + 私たちから永遠の感謝 興味がありますか? - 速度制限を有効化 - 有効にすると、エミュレーション速度が任意の割合に制限されます。 - エミュレーション速度の制限 - エミュレーション速度を制限する割合を指定します。デフォルトの100%では、エミュレーションは通常の速度に制限されます。値が高いまたは低いほど、速度制限が増加または減少します。 + エミュレーション速度を制限 + エミュレーション速度を指定した割合に制限します。 + エミュレーション速度 + エミュレーション速度を制限するパーセンテージを指定します。100%は通常速度です。値の増減で速度も増減します。 CPU精度 - TVモード - TVモードでエミュレートします。パフォーマンスが犠牲になりますが、解像度が向上します。 + 高解像度、低パフォーマンス。無効時には携帯モードが使用されます(低解像度、高パフォーマンス)。 地域 言語 RTCの日付を選択 RTCの時刻を選択 - カスタムRTC - 現在のシステム時間とは別にカスタムのリアルタイムクロックを設定できます。 + カスタム RTC + 現在のシステム時間とは別に、任意のリアルタイムクロックを設定できます。 カスタムRTCを設定 - API 精度 - 解像度 + 解像度(携帯モード/TVモード) 垂直同期モード + 画面の向き アスペクト比 ウィンドウ適応フィルター アンチエイリアス方式 最大クロックを強制 (Adrenoのみ) - GPUを可能な限り最大クロックで動作させます (過熱制限は引き続き適用されます)。 + GPUを最大限可能な周波数で動作させます (過熱制限は引き続き適用されます)。 非同期シェーダー シェーダーを非同期でコンパイルします。コマ落ちが軽減されますが、不具合が発生する可能性があります。 + 即時書き込み + 一部のゲームにおいて、パフォーマンスを犠牲にしながらも、レンダリング精度を向上させます。 + ディスクシェーダーキャッシュ + 生成したシェーダーを端末に保存して読み込み、コマ落ちを軽減します。 + + + CPU + CPU デバッギング + GPU + API グラフィックデバッグ - オンにすると、グラフィックAPI は低速のデバッグモードに入ります。 - シェーダーキャッシュを使用 - 生成したシェーダーをディスクに保存して読み込むことで、コマ落ちを軽減します。 + グラフィックAPIを低速デバッグモードに設定します。 + Fastmem + 出力エンジン 音量 オーディオ出力の音量を指定します デフォルト 設定を保存しました - %1$sの設定を保存しました + %1$s の設定を保存しました %1$s.ini の保存エラー: %2$s + 未実装のメニュー 読み込み中… + 終了中... この設定を初期値にリセットしますか? 初期設定に戻す すべての設定をリセットしますか? - すべての詳細設定が初期設定に戻されます。この操作は元に戻せません。 + すべての詳細設定が初期値に戻されます。この操作は元に戻せません。 設定をリセットしました 閉じる 詳細情報 + 自動 + 送信 + インポート + エクスポート + エクスポート失敗 + インポート失敗 + キャンセル中 GPUドライバを選択 - 現在のGPUドライバーを置き換えますか? + 現在のGPUドライバを置き換えますか? インストール デフォルト - デフォルトのGPUドライバーを使用します + デフォルトのドライバを使用します + 選択されたドライバが無効、システムのデフォルトを使用します! システムのGPUドライバ インストール中… @@ -181,33 +233,34 @@ グラフィック サウンド テーマと色 + デバッグ ROMが暗号化されています - ゲームカートリッジやインストール済みのタイトルを再度ダンプするためのガイドに従ってください。]]> - prod.keys ファイルがインストールされていることを確認してください。]]> + prod.keys ファイルがインストールされていることを確認してください。]]> ビデオコアの初期化中にエラーが発生しました これは通常、互換性のないGPUドライバーが原因で発生します。 カスタムGPUドライバーをインストールすると、問題が解決する可能性があります。 ROMの読み込みに失敗しました ROMファイルが存在しません - エミュレーションを終了 + 終了 完了 FPSカウンター - コントロールを切り替え - 十字キーのスライド操作 - 振動 - オーバーレイを表示 - すべて選択 - オーバーレイを調整 + ボタンの表示設定 + スティックを固定しない + 十字キーをスライド操作 + タッチ振動 + ボタンを表示 + すべて切替 + 見た目を調整 大きさ 不透明度 リセット - オーバーレイを編集 - エミュレーションを一時停止 - エミュレーションを再開 - オーバーレイオプション + 位置を編集 + 一時停止 + 再開 + 表示オプション 設定をロード中… @@ -220,10 +273,13 @@ システムアーカイブが見つかりません %s が見つかりません。システムアーカイブをダンプしてください。\nエミュレーションを続行すると、クラッシュやバグが発生する可能性があります。 システムアーカイブ - セーブ/ロード エラー + セーブ/ロードエラー 致命的なエラー 致命的なエラーが発生しました。詳細はログを確認してください。\nエミュレーションを続行するとクラッシュやバグが発生する可能性があります。 - この設定をオフにすると、エミュレーションのパフォーマンスが著しく低下します!最高の体験を得るためには、この設定を有効にしておくことをお勧めします。 + この設定をオフにすると、エミュレーションのパフォーマンスが著しく低下します!最高の体験を得るためには、この設定を有効にしておくことを推奨します。 + デバイス RAM: %1$s\n推奨: %2$s + %1$s %2$s + 起動できるゲームがありません! 日本 @@ -234,7 +290,14 @@ 韓国 台湾 - + + Byte + KB + MB + GB + TB + PB + EB Vulkan @@ -242,7 +305,7 @@ 標準 - 高い + 最高 (低速) @@ -272,12 +335,17 @@ FXAA SMAA + + 横長 + 縦長 + 自動 + デフォルト (16:9) 強制 4:3 強制 21:9 強制 16:10 - ウィンドウに合わせる + 画面に合わせる 正確 @@ -289,7 +357,7 @@ Lスティック Rスティック HOMEボタン - スクリーンショット + キャプチャーボタン シェーダーを準備しています @@ -306,8 +374,22 @@ ライト ダーク - - 黒色の背景を使用 - ダークテーマの使用時は、黒色の背景を有効にしてください。 + + cubeb - + + 完全な黒を使用 + ダークテーマの背景色に黒が適用されます。 + + + ピクチャーインピクチャー + バックグラウンド時にウインドウを最小化する + 中断 + プレイ + 消音 + 消音解除 + + + ライセンス + AMDの高品質アップスケーリング + diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index 214f95706..1b9160a23 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -1,9 +1,9 @@ - + - 이 소프트웨어는 닌텐도 스위치 게임 콘솔용 게임을 실행합니다. 게임 타이틀이나 keys는 포함되어 있지 않습니다.<br /><br />시작하기 전에 장치 저장소에서 prod.keys ]]> 파일을 찾아주세요.<br /><br />자세히 알아보기]]> + 이 소프트웨어는 Nintendo Switch 게임을 실행합니다. 게임 타이틀이나 키는 포함되어 있지 않습니다.<br /><br />시작하기 전에 장치 저장소에서 prod.keys ]]> 파일을 찾아주세요.<br /><br />자세히 알아보기]]> 에뮬레이션이 활성화됨 - 에뮬레이션이 실행 중일 때 영구 알림을 표시합니다. + 에뮬레이션이 실행 중일 때 지속적으로 알림을 표시합니다. yuzu가 실행 중입니다. 알림 및 오류 문제가 발생하면 알림을 표시합니다. @@ -11,26 +11,25 @@ 환영합니다! - <b>yuzu</b> 를 설정하고 에뮬레이션으로 이동하는 방법을 알아보세요. + <b>yuzu</b>를 설정하고 에뮬레이션을 시작하세요. 시작하기 - Keys - 아래 버튼을 사용하여 <b>prod.keys</b> 파일을 선택합니다. - keys 선택 + 키 설정 + 아래 버튼으로 <b>prod.keys</b> 파일을 선택합니다. + 키 선택 게임 아래 버튼으로 <b>게임</b> 폴더를 선택합니다. 완료 - 모든 준비가 완료되었습니다.\n게임을 즐기세요! + 모두 준비되었습니다.\n게임을 즐기세요! 계속 다음 - 뒤로 + 이전 게임 추가 게임 폴더 선택 - 게임 검색 설정 - 파일을 찾을 수 없거나 아직 게임 디렉토리를 선택하지 않았습니다. + 파일을 찾을 수 없거나 아직 게임 디렉터리를 선택하지 않았습니다. 게임 검색 및 필터링 게임 폴더 선택 yuzu가 게임 목록을 채울 수 있도록 허용 @@ -38,140 +37,160 @@ 폴더를 선택하지 않으면 게임 목록에 게임이 표시되지 않습니다. https://yuzu-emu.org/help/quickstart/#dumping-games 게임 검색 - 게임 디렉터리 선택 + 게임 디렉터리를 설정했습니다. prod.keys 설치 - 판매용 게임 암호 해독에 요구 - keys 추가를 건너뛰겠습니까? - 정품 게임을 에뮬레이트하려면 유효한 keys가 필요합니다. 계속하면 자체 제작 앱만 작동합니다. + 패키지 게임 암호 해독에 필요 + 키 추가를 건너뛰겠습니까? + 패키지 게임을 에뮬레이트하려면 유효한 키 값이 필요합니다. 이 단계를 건너뛰면 홈브류 게임만 실행할 수 있습니다. https://yuzu-emu.org/help/quickstart/#guide-introduction 알림 아래 버튼으로 알림 권한을 부여합니다. - 권한 부여 - 알림 권한 부여를 건너뛰겠습니까? - yuzu는 중요한 정보를 알려드리지 않습니다. + 알림 켜기 + 알림을 끄겠습니까? + yuzu가 중요한 정보를 알려드리지 않습니다. 권한 거부됨 - 이 권한을 너무 많이 거부했으므로 이제 시스템 설정에서 수동으로 권한을 부여해야 합니다. + 권한 허용을 너무 많이 거부하여 시스템 설정에서 수동으로 권한을 부여해야 합니다. 정보 빌드 버전, 크레딧 등 도움말 건너뛰기 취소 - Amiibo keys 설치 - 게임에서 아미보 사용 시 필요 - 잘못된 keys 파일 선택 - keys가 성공적으로 설치됨 - 암호화 keys 읽기 오류 - 잘못된 암호화 keys + amiibo 키 설치 + 게임에서 amiibo 사용 시 필요 + 잘못된 키 파일이 선택됨 + 키 값을 설치했습니다. + 암호화 키 읽기 오류 + 키 파일의 확장자가 .keys인지 확인하고 다시 시도하세요. + 키 파일의 확장자가 .bin인지 확인하고 다시 시도하세요. + 암호화 키가 올바르지 않음 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys - 선택한 파일이 잘못되었거나 손상되었습니다. keys를 다시 덤프하세요. + 선택한 파일이 잘못되었거나 손상되었습니다. 키를 다시 덤프하세요. GPU 드라이버 설치 잠재적으로 더 나은 성능 또는 정확성을 위해 대체 드라이버를 설치하세요. 고급 설정 에뮬레이터 설정 구성 - 최근 플레이한 게임 - 최근 추가한 게임 - 판매용 + 최근 플레이 + 최근 추가 + 패키지 홈브류 yuzu 폴더 열기 yuzu의 내부 파일 관리 - 앱 모양 수정 + 앱 디자인 편집 파일 관리자를 찾을 수 없음 - yuzu 디렉토리를 열 수 없음 + yuzu 디렉터리를 열 수 없음 파일 관리자의 사이드 패널에서 사용자 폴더를 수동으로 찾아주세요. 저장 데이터 관리 - 데이터를 저장했습니다. 아래에서 옵션을 선택하세요. + 저장 데이터를 발견했습니다. 아래에서 옵션을 선택하세요. 저장 파일 가져오기 또는 내보내기 - 가져오기 성공 - 저장 디렉터리 구조가 잘못됨 + 데이터를 불러왔습니다. + 올바르지 않은 저장 디렉터리 구조 첫 번째 하위 폴더 이름은 게임의 타이틀 ID여야 합니다. 가져오기 내보내기 - + 펌웨어 설치 + 펌웨어는 ZIP 파일이며 일부 게임을 부팅하는 데 필요합니다. + 펌웨어 설치 + 펌웨어를 설치했습니다. + 펌웨어 설치 실패 + 디버그 로그 공유 + yuzu의 로그 파일을 공유하여 문제 디버깅하기 + 로그 파일을 찾을 수 없습니다. + 게임 콘텐츠 설치 + 게임 업데이트 또는 DLC 설치 + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates 가이아는 진짜가 아님 - 클립보드에 복사 - 오픈 소스 스위치 에뮬레이터 + 클립보드에 복사되었습니다. + 오픈 소스 Switch 에뮬레이터 기여자 yuzu 팀의 \u2764로 제작 https://github.com/yuzu-emu/yuzu/graphs/contributors + Android용 yuzu를 가능하게 하는 프로젝트 빌드 https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu - 미리 체험하기 - 미리 체험하기 신청 + 앞서 해보기 + 앞서 해보기 신청 https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea - 최첨단 기능, 미리 체험하기 업데이트 등 - 미리 체험하기 혜택 - 최첨단 기능 - 미리 체험하기 업데이트 + 최신 기능, 업데이트 미리 체험 등 + 앞서 해보기 혜택 + 최신 기능 + 업데이트 미리 체험 수동 설치 불필요 우선 지원 - 게임 보존 도움주기 - 영원한 감사의 마음을 전합니다 + 게임 보존 지원 + 우리의 영원한 감사의 마음 관심 있으세요? - 제한 속도 활성화 - 활성화하면 에뮬레이션 속도가 정상 속도의 지정된 비율로 제한됩니다. + 속도 제한 + 에뮬레이션 속도를 정상 속도의 지정된 비율로 제한합니다. 속도 제한 비율 - 에뮬레이션 속도를 제한할 비율을 지정합니다. 기본값인 100%로 설정하면 에뮬레이션이 정상 속도로 제한됩니다. 값이 높거나 낮으면 속도 제한이 증가하거나 감소합니다. + 에뮬레이션 속도의 제한 비율을 지정합니다. 100%가 정상 속도입니다. 값이 높거나 낮으면 속도 제한이 증가하거나 감소합니다. CPU 정확도 - - 도킹 모드 - 도킹 모드에서 에뮬레이션하면 성능이 저하되는 대신 해상도가 향상됩니다. - 에뮬레이트된 지역 - 에뮬레이트된 언어 + 독 모드 + 해상도를 높이며 성능이 저하됩니다. 비활성화시 휴대 모드가 사용되며 해상도는 낮아지고 성능은 향상됩니다. + 에뮬레이트 지역 + 에뮬레이트 언어 RTC 날짜 선택 RTC 시간 선택 - 커스텀 RTC 활성화 - 이 설정을 사용하면 현재 시스템 시간과 별도로 사용자 지정 실시간 시계를 설정할 수 있음 - 커스텀 RTC 설정 + 사용자 지정 RTC + 현재 시스템 시간과 별도로 사용자 지정 실시간 시계를 설정할 수 있습니다. + 사용자 지정 RTC 설정 - API 정확도 수준 - 해상도 + 해상도 (휴대 모드/독 모드) 수직동기화 모드 화면비 - 창 적응 필터 - 안티-에일리어싱 방법 - 최대 클럭 강제 설정 (아드레노만 해당) + 윈도우 적응 필터 + 안티에일리어싱 방법 + 최대 클럭 강제 설정 (아드레노 전용) GPU가 가능한 최대 클럭으로 실행되도록 강제합니다 (열 제약 조건은 여전히 적용됩니다). 비동기 셰이더 사용 - 셰이더를 비동기식으로 컴파일하므로 끊김 현상이 줄어들지만 글리치가 발생할 수 있습니다. - 그래픽 디버깅 활성화 - 이 옵션을 선택하면 그래픽 API가 느린 디버깅 모드로 전환됩니다. - 디스크 셰이더 캐시 사용 - 생성된 셰이더를 디스크에 저장하고 불러오기하여 끊김 현상을 줄입니다. - - + 셰이더를 비동기식으로 컴파일하여 끊김 현상을 줄이지만 글리치가 발생할 수 있습니다. + 반응형 플러싱 사용 + 일부 게임에서 성능 저하를 감수하고 렌더링 정확도를 향상합니다. + 디스크 셰이더 캐시 + 생성된 셰이더를 로컬에 저장하고 로드하여 끊김 현상을 줄입니다. + + + CPU + API + 그래픽 디버깅 + 그래픽 API를 느린 디버깅 모드로 설정합니다. 볼륨 오디오 출력의 볼륨을 지정합니다. 기본값 - 저장된 설정 - %1$s를 위해 저장된 설정 - %1$s.ini 저장 중 오류: %2$s - 불러오기 중... - 이 설정을 기본값으로 되돌리겠습니까? + 설정이 저장되었습니다. + %1$s 전용 설정이 저장되었습니다. + %1$s.ini 저장 중 오류 발생: %2$s + 불러오는 중... + 이 설정을 기본값으로 재설정하겠습니까? 기본값으로 재설정 모든 설정을 초기화하겠습니까? - 모든 고급 설정이 기본 구성으로 재설정됩니다. 이 설정은 되돌릴 수 없습니다. + 모든 고급 설정이 기본 구성으로 재설정됩니다. 이 작업은 되돌릴 수 없습니다. 설정 초기화 닫기 - 자세히 알아보기 - + 자세히 + 자동 + 제출 + Null + 가져오기 + 내보내기 GPU 드라이버 선택 - 현재 사용 중인 GPU 드라이버를 교체하겠습니까? + 현재 사용중인 GPU 드라이버를 변경하겠습니까? 설치 기본값 - 기본 GPU 드라이버 사용 + 기본 GPU 드라이버를 사용합니다. + 잘못된 드라이브가 선택되었습니다. 시스템 기본값을 사용합니다. 시스템 GPU 드라이버 드라이버 설치 중... @@ -182,51 +201,50 @@ 그래픽 오디오 테마 및 색상 + 디버그 - 롬이 암호화되었음 - 게임 카트리지 또는 설치된 타이틀를 다시 덤프하세요.]]> - prod.keys 파일이 설치되어 있는지 확인하세요.]]> + 롬 파일이 암호화되어있음 + prod.keys 파일이 설치되어 있는지 확인하세요.]]> 비디오 코어를 초기화하는 동안 오류 발생 - 이 문제는 일반적으로 호환되지 않는 GPU 드라이버로 인해 발생합니다. 사용자 지정 GPU 드라이버를 설치하면 이 문제가 해결될 수 있습니다. - 롬을 불러올 수 없음 + 일반적으로 이 문제는 호환되지 않는 GPU 드라이버로 인해 발생합니다. 사용자 지정 GPU 드라이버를 설치하면 이 문제가 해결될 수 있습니다. + 롬 파일을 불러올 수 없음 롬 파일이 존재하지 않음 에뮬레이션 종료 완료 - FPS 카운터 - 토글 제어 - 상대 스틱 센터 - 십자패드 슬라이드 - 햅틱 - 오버레이 표시 - 모두 토글 - 오버레이 조정 - 스케일 + FPS 표시 + 컨트롤러 선택 + 스틱의 중심 이동 + 십자키 슬라이드 + 터치 햅틱 + 컨트롤러 표시 + 모두 선택 + 컨트롤러 조정 + 크기 불투명도 - 오버레이 재설정 - 오버레이 편집 + 컨트롤러 설정 초기화 + 컨트롤러 위치 편집 에뮬레이션 일시 중지 에뮬레이션 일시 중지 해제 - 오버레이 옵션 + 화면 오버레이 설정 - 설정 불러오기 중... + 설정 불러오는 중... - 가상 키보드 + 소프트웨어 키보드 - 정보 + 중단 계속 시스템 아카이브를 찾을 수 없음 %s가 누락되었습니다. 시스템 아카이브를 덤프하세요.\n에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다. 시스템 아카이브 저장하기/불러오기 오류 - 치명적인 오류 - 치명적인 오류가 발생했습니다. 자세한 내용은 로그를 확인하십시오.\n에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다. + 치명적 오류 + 치명적 오류가 발생했습니다. 자세한 내용은 로그를 확인하십시오.\n에뮬레이션을 계속하면 충돌 및 버그가 발생할 수 있습니다. 이 설정을 끄면 에뮬레이션 성능이 크게 저하됩니다! 최상의 환경을 위해 이 설정을 활성화된 상태로 두는 것이 좋습니다. - 일본 미국 @@ -234,12 +252,11 @@ 호주 중국 대한민국 - 타이완 - - + 대만 + 영국 하계 표준시(GB) - 불칸 + Vulcan 없음 @@ -256,17 +273,17 @@ 4X (2880p/4320p) (느림) - 즉시 (끔) + 즉각 표시 (끄기) 메일박스 - FIFO (켬) - FIFO 릴랙스 + FIFO (켜기) + FIFO Relaxed - 가장 가까운 이웃 - 이중선형 - 고등차수보간 + 최근접 보간 + 쌍선형 보간 + 쌍입방 보간 가우시안 - 스케일포스 + ScaleForce AMD FidelityFX™ 초고해상도 @@ -274,27 +291,29 @@ FXAA SMAA + 자동 + 기본 (16:9) 강제 4:3 강제 21:9 강제 16:10 - 창에 맞게 늘림 + 화면에 맞춤 정확함 - 안전하지 않음 - 편집증 (느림) + 최적화 (안전하지 않음) + 최적화하지 않음 (느림) - 십자패드 + 십자키 L 스틱 R 스틱 스크린샷 - 셰이더 준비하기 + 셰이더 준비하는 중 셰이더 빌드 중 @@ -303,13 +322,19 @@ Material You - 테마 모드 변경 - 팔로우 시스템 - 밝음 - 어두움 + 다크 모드 설정 + 시스템 값 사용 + 라이트 모드 + 다크 모드 - 검은색 배경 사용 - 어두운 테마를 사용할 때는 검은색 배경을 적용합니다. + 검정 배경 + 어두운 테마를 사용할 때는 검정 배경을 적용합니다. + + 음소거 + 음소거 해제 - + + 라이센스 + AMD의 고품질 업스케일링 + diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml index 5443cef42..3162a9d41 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml @@ -1,5 +1,5 @@ - + Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.<br /><br />Før du begynner, må du finne prod.keys ]]> filen din på enhetslagringen.<br /><br />Lær mer]]> Emulering er aktiv @@ -25,7 +25,6 @@ Tilbake Legg til spill Velg din spillmappe - Spill Søk @@ -37,7 +36,7 @@ Hoppe over valg av spillmappe? Spill vises ikke i Spill-listen hvis en mappe ikke er valgt. https://yuzu-emu.org/help/quickstart/#dumping-games - Søk i spill + Søk i spill| Spillkatalogen er valgt Installer prod.keys Nødvendig for å dekryptere spill @@ -61,6 +60,8 @@ 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 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys Den valgte filen er feil eller ødelagt. Vennligst dump nøklene på nytt. @@ -86,7 +87,17 @@ Det første undermappenavnet må være spillets tittel-ID. Importer Eksporter - + Installer fastvare + Fastvaren må være i et ZIP-arkiv og er nødvendig for å starte noen spill. + Installering av fastvare + Fastvaren er vellykket installert + Installasjon av fastvare mislyktes + Del feilsøkingslogger + Del yuzus loggfil for å feilsøke problemer + Ingen loggfil funnet + Installer spillinnhold + Installer spilloppdateringer eller DLC + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates Gaia er ikke ekte Kopiert til utklippstavlen @@ -94,6 +105,7 @@ Bidragsytere Laget med \u2764 fra yuzu-teamet https://github.com/yuzu-emu/yuzu/graphs/contributors + Prosjekter som gjør yuzu for Android mulig Bygg https://discord.gg/u77vRWY https://yuzu-emu.org/ @@ -114,41 +126,43 @@ Er du interessert? - Aktiver hastighetsbegrensning - Når aktivert, begrenses emuleringshastigheten til en angitt prosentandel av normal hastighet. + Begrense hastigheten + Begrenser emuleringshastigheten til en spesifisert prosentandel av normal hastighet. Hastighetsbegrensning i prosent - Angir prosentandelen som skal begrense emuleringshastigheten. Med standardverdien 100 % vil emuleringen være begrenset til normal hastighet. Høyere eller lavere verdier vil øke eller redusere hastighetsbegrensningen. + Angir prosentandelen som skal begrense emuleringshastigheten. 100 % er normal hastighet. Høyere eller lavere verdier vil øke eller redusere hastighetsgrensen. CPU-nøyaktighet - Dokket modus - Emulerer i dokket modus, noe som øker oppløsningen på bekostning av ytelsen. + Øker oppløsningen, men reduserer ytelsen. Håndholdt modus brukes når den er deaktivert, noe som reduserer oppløsningen og øker ytelsen. Emulert region Emulert språk Velg RTC-dato Velg RTC-tid - Aktiver egendefinert RTC - Med denne innstillingen kan du stille inn en egendefinert sanntidsklokke som er atskilt fra gjeldende systemtid. - Angi egendefinert RTC + Tilpasset Sannhetstidsklokke + Gjør det mulig å stille inn en egendefinert sanntidsklokke separat fra den gjeldende systemtiden. + Angi tilpasset RTC - API Nøyaktighetsnivå - Oppløsning + Oppløsning (håndholdt/dokket) VSync-modus Størrelsesforhold Filter for vindustilpasning - Anti-Aliasing-metode + Anti-aliasing-metode Tving fram maksimal klokkefrekvens (kun Adreno) Tvinger GPU-en til å kjøre med maksimal klokkefrekvens (termiske begrensninger vil fortsatt gjelde). Bruk asynkrone shaders - Kompilerer shaders asynkront, noe som reduserer hakkingen, men kan føre til feil. - Aktiver feilsøking av grafikk - Når dette er merket av, går grafikk-API-et inn i en langsommere feilsøkingsmodus. - Bruk disk shader-cache - Reduser hakking ved å lagre og laste inn genererte shaders på disken. - - + Kompilerer shaders asynkront, noe som reduserer hakking, men kan føre til feil. + Bruk reaktiv spyling + Forbedrer gjengivelsesnøyaktigheten i enkelte spill på bekostning av ytelsen. + Disk shader-hurtigbuffer + Reduserer hakking ved å lagre og laste inn genererte shaders lokalt. + + + CPU + API + Feilsøking av grafikk + Setter grafikk-API-et til en langsom feilsøkingsmodus. Volum Angir volumet på lydutgangen. @@ -164,14 +178,19 @@ Alle avanserte innstillinger tilbakestilles til standardkonfigurasjonen. Dette kan ikke angres. Tilbakestilling av innstillinger Lukk - Lær Mer - + Lær mer + Auto + Send inn + Null + Importer + Eksporter Velg GPU-driver Ønsker du å bytte ut din nåværende GPU-driver? Installer Standard Bruk av standard GPU-driver + Ugyldig driver valgt, bruker systemstandard! Systemets GPU-driver Installerer driver... @@ -182,10 +201,10 @@ Grafikk Lyd Tema og farge + Feilsøk ROM-en din er kryptert - spillkassetter eller installerte titler.]]> 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. @@ -196,25 +215,25 @@ Avslutt emulering Ferdig FPS-teller - Veksle kontroller - Relativt senter for stikken - DPad-skyveplate - Haptikk + Veksle mellom kontrollene + Relativt pinnesenter + D-pad-skyving + Berøringshaptikk Vis overlegg - Slå av alt + Veksle mellom alle Juster overlegg Skaler Gjennomsiktighet Tilbakestill overlegg Rediger overlegg - Pause Emulering - Opphev pausing av emulering - Alternativer for overlegg + Pause emulering + Ta emuleringen ut av pause + Overlay-alternativer Laster inn innstillinger... - Programvare Tastatur + Programvaretastatur Avbryt @@ -226,7 +245,6 @@ Fatal Feil Det oppstod en fatal feil. Sjekk loggen for mer informasjon.\nFortsatt emulering kan føre til krasj og feil. Hvis du slår av denne innstillingen, reduseres emuleringsytelsen betydelig! Vi anbefaler at du lar denne innstillingen være aktivert for å få den beste opplevelsen. - Japan USA @@ -236,8 +254,7 @@ Korea Taiwan - - + GB Vulkan Ingen @@ -274,12 +291,14 @@ FXAA SMAA + Auto + Standard (16:9) Tving 4:3 Tving 21:9 Tving 16:10 - Strekk til Vindu + Strekk til vindu Nøyaktig @@ -287,9 +306,9 @@ Paranoid (Langsom) - D-Pad - Venstre Pinne - Høyre Pinne + D-pad + Venstre spak + Høyre spak Hjem Skjermbilde @@ -298,7 +317,7 @@ Bygging av shaders - Endre appens tema + Endre app-tema Standard Material You @@ -309,7 +328,13 @@ Mørk - Bruk svart bakgrunn + Svart bakgrunn Bruk svart bakgrunn når du bruker det mørke temaet. - + Lydløs + Slå på lyden + + + Lisenser + Oppskalering av høy kvalitet fra AMD + diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index 899e233d0..f4d9920c2 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -1,5 +1,5 @@ - + To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.<br /><br />Zanim zaczniesz, wybierz plik kluczy prod.keys ]]> z katalogu w pamięci masowej.<br /><br />Dowiedz się więcej]]> Emulacja jest uruchomiona @@ -25,7 +25,6 @@ Wstecz Dodaj gry Wybierz folder zawierający Twoje gry - Gry Szukaj @@ -61,6 +60,8 @@ 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 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys Wybrany plik jest niepoprawny lub uszkodzony. Zrzuć ponownie swoje klucze. @@ -86,7 +87,17 @@ Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry. Importuj Eksportuj - + Zainstaluj firmware + Firmware musi być w postaci archiwum ZIP, niektóre gry wymagają go do uruchomienia/prawidłowego działania + Instaluję firmware + Zainstalowano pomyślnie + Błąd podczas instalacji firmware + Udostępnij logi debugowania + Podziel się logami yuzu, pomoże to twórcom w poprawie działania emulatora + Nie znaleziono plików logów + Zainstaluj zawartość gry + Zainstaluj aktualizację gry lub dodatek DLC + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates Gaia isn\'t real Skopiowano do schowka @@ -94,6 +105,7 @@ Współtwórcy Stworzone z \u2764 przez zespół yuzu https://github.com/yuzu-emu/yuzu/graphs/contributors + Projekty dzięki którym yuzu mógł zostać stworzony Wersja https://discord.gg/u77vRWY https://yuzu-emu.org/ @@ -114,27 +126,25 @@ Jesteś zainteresowany? - Włącz limit szybkości emulacji + Limit szybkość Włącz, aby ustawić procentowy limit szybkości emulacji Procentowy limit szybkości emulacji Określa limit szybkości emulacji gier. Domyślna wartość 100% oznacza normalną szybkość z jaką działa gra. Wartości niższe lub wyższe zmniejszą lub zwiększą limit szybkości. Dokładność procesora CPU - Tryb zadokowany - Emulacja w trybie stacji dokującej, zwiększa rozdzielczość kosztem wydajności. + Zwiększa rozdzielczość kosztem wydajności. Kiedy wyłączone, używany jest tryb Handheld, który obniża rozdzielczość i dzięki temu zwiększa wydajność. Region emulacji Język emulacji Ustaw datę RTC Ustaw czas RTC - Włącz niestandardowy zegar RTC + Niestandardowy RTC Ta opcja pozwala na wybranie własnych ustawień czasu używanych w czasie emulacji, innych niż czas systemu Android. Ustaw niestandardowy czas RTC - Interfejs graficzny Poziom precyzji emulacji - Rozdzielczość + Rozdzielczość (Handheld/Zadokowany) Synchronizacja pionowa VSync Proporcje ekranu Filtr adaptacji rozdzielczości @@ -143,12 +153,16 @@ Wymusza uruchomienie maksymalnego taktowania układu graficznego (zabezpieczenia termiczne będą dalej aktywne). Wyłącz synchronizację shaderów Kompiluj oświetlenie bez synchronizacji, poprawi wydajność ale może powodować błędy. - Włącz debugowanie grafiki - Kiedy włączone, interfejs graficzny korzysta z wolnego trybu debugowania błędów. - Użyj pamięci podręcznej shaderów na dysku + Użyj spłukiwania reaktywnego - reactive flushing + Poprawia jakość renderowania w kilku grach, kosztem wydajności. + Pamięć podręczna shaderów Zmniejsza przycięcia przez przechowywanie gotowych wygenerowanych plików oświetlenia w pamięci urządzenia. - + + CPU + Interfejs graficzny + Debugowanie grafiki + Kiedy włączone, interfejs graficzny korzysta z wolnego trybu debugowania błędów. Głośność Ustala poziom głośności wyjścia dźwięku. @@ -161,17 +175,21 @@ Przywrócić wartość tego ustawienia do wartości domyślnej? Przywróć ustawienia domyślne Przywrócić WSZYSTKIE ustawienia? - Wszystkie zaawansowane opcje zostaną przywrócone do wartości domyślnych. Czynności nie będzie można cofnąć. + Wszystkie zaawansowane opcje zostaną przywrócone do wartości domyślnych. Czynności nie będzie można cofnąć Reset ustawień Zamknij Dowiedz się więcej - + Automatyczny + Zatwierdź + Importuj + Eksportuj Wybierz sterownik GPU Chcesz zastąpić obecny sterownik układu graficznego? Zainstaluj Domyślne Aktywny domyślny sterownik GPU + Wybrano błędny sterownik, powrót do domyślnego. Systemowy sterownik GPU Instalowanie sterownika... @@ -182,10 +200,10 @@ Grafika Dźwięk Motyw i kolor + Debug Twój ROM jest zakodowany - kardridży lub zainstalowanych gier.]]> 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. @@ -198,23 +216,23 @@ Licznik FPS Wybierz przyciski Wycentruj gałki - Ruchomy DPad + Ruchomy D-pad Wibracje haptyczne Pokaż przyciski - Zaznacz wszystkie + Włącz wszystkie Dostosuj nakładkę Skala Przeźroczystość - Resetuj + Resetuj nakładkę Edytuj nakładkę Wstrzymaj emulację Wznów emulację Opcje nakładki - Wczytywanie ustawień... + Wczytuję ustawienia... - Klawiatura systemowa + Klawiatura programowa Przerwij @@ -226,7 +244,6 @@ Błąd krytyczny Wystąpił błąd krytyczny. Szczegóły znajdziesz w pliku log.\nKontynuowanie może spowodować błędy lub przerwanie emulacji. Wyłączenie tej opcji znacząco ograniczy wydajność! Dla najlepszego doświadczenia, zaleca się zostawienie tej opcji włączonej. - Japonia USA @@ -236,8 +253,7 @@ Korea Tajwan - - + GB Vulkan Żadny @@ -274,12 +290,14 @@ FXAA SMAA + Automatyczny + Domyślne (16:9) Wymuś 4:3 Wymuś 21:9 Wymuś 16:10 - Rozciągnij do Okna + Rozciągnij do okna Dokładny @@ -287,7 +305,7 @@ Paranoid (Wolny) - D-Pad + D-pad Lewa gałka Prawa gałka Home @@ -298,18 +316,21 @@ Budowanie shaderów - Zmień motyw aplikacji + Ustaw motyw aplikacji Domyślny Material You - Zmiana trybu motywu + Zmień tryb motywu Podążaj za systemowym Jasny Ciemny - Używaj czarnego tła + Czarne tła Kiedy używany ciemny motyw, tła zostają zastąpione czernią. - + + Licencje + Rozciąganie wysokiej jakości od AMD + diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index caa095364..8888fc750 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -1,30 +1,31 @@ - + - Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro no armazenamento do teu dispositivo.<br /><br /> + Este software executa jogos do console Nintendo Switch. Não estão inclusos nem jogos ou chaves. <br /><br />Antes de começar, por favor localize o arquivo no armazenamento de seu dispositivo.<br /><br /> Emulação está Ativa - Mostra uma notificação permanente enquanto a emulação está a correr. + Mostra uma notificação permanente enquanto a emulação estiver em andamento. Yuzu está em execução Notificações e erros - Mostra notificações quendo algo corre mal. - Permissões de notificação não permitidas + Mostra notificações quando algo dá errado. + Acesso às notificações não concedido! - Bemvindo! - Aprende como configurar <b>yuzu</b> e arranca a emulação. - Começa - Chaves - Seleciona o teu ficheiro <b>prod.keys</b> com o botão abaixo. - Seleciona as Chaves + Bem-vindo! + Aprenda como configurar o <b>yuzu</b> e mergulhe na emulação. + Primeiros passos + Keys + Selecione seu arquivo <b>prod.keys</b> com o botão abaixo. + Selecione as Keys Jogos - Seleciona a tua pasta <b>Games</b> com o botão abaixo. + Seleciona sua pasta <b>Jogos</b> com o botão abaixo. Feito - Tudo pronto.\nDisfruta dos teus jogos! + Tudo pronto.\nAproveite seus jogos! Continuar Próximo Voltar - Adiciona Jogos - Seleciona a tua pasta de Jogos + Adicionar Jogos + Selecione sua pasta de Jogos + Completo! Jogos @@ -37,7 +38,8 @@ Ignorar a seleção da pasta de jogos? Os jogos não serão exibidos na lista de jogos se uma pasta não estiver selecionada. https://yuzu-emu.org/help/quickstart/#dumping-games - Procurar Jogos + Procurar jogos + Procurar nas definições Pasta de Jogos selecionada Instala prod.keys Necessário para desencriptar jogos comerciais @@ -61,15 +63,18 @@ 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 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves. Instala driver para GPU Instala drivers alternativos para desempenho ou precisão potencialmente melhores Definições avançadas + Definições avançadas: %1$s Configura definições do emulador - Jogos recentes - Adicionados recentemente + Jogado recentemente + Adicionado recentemente Jogos comerciais Homebrew Abre a pasta Yuzu @@ -86,6 +91,33 @@ O nome da primeira sub pasta tem de ser a ID do jogo. Importar Exportar + Instalar firmware + O firmware deve estar em um arquivo ZIP e é necessário para iniciar alguns jogos. + Instalando firmware + Firmware instalado com sucesso. + Falha na instalação do firmware + Cofirma que os ficheiros firmware nca estão no root do finheiro zip e tenta de novo. + Compartilhe registros de debug. + Compartilhe o arquivo de registro do yuzu para obter ajuda com problemas + Arquivo de registro não encontrado + Instalar conteúdo de jogos + Instalar atualizações de jogos ou DLC + A instalar conteúdo... + Erro ao instalar ficheiro(s) para NAND + Por favor confitma que o conteúdo(s) é válido e que as prod.keys estão instaladas. + A instalação de jogos base não é permitida para evitar possíveis conflitos. + Sò conteúdos NSP e XCI são suportados. Por favor verifica que o conteúdo(s) do jogo são válidos. + %1$d erro(s) de instalação + Conteúdo(s) de jogo instalados com sucesso + %1$d instalado com sucesso + %1$d substituída com êxito + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + Drivers personalizados não suportados + Carrea«gamento de drivers personalizados não é suportado pr este dispositivo. \nCheck verifica esta opção de futuro para confirmar se o suporte foi adicionado! + Administrar dados yuzu + Importa/exporta firmware, chaves, dados do usuário e mais! + Partilha ficheiro duardado + Erro ao exportar dados guardados Gaia não é real @@ -94,7 +126,18 @@ Contribuidores Feito com \u2764 da equipa do Yuzu https://github.com/yuzu-emu/yuzu/graphs/contributors + Projetos que tornam o yuzu para Android possível Versão + Dado de utilizados + Importar/exportar todos dados da aplicação data.\n\n Ao importar dados do utilizados, todos os dados existentes do utilizados serão excluídos! + A exportar dados de utilizados... + A importar dados de utilizador... + Importar dados de utilizados... + Backup yuzu inválido + Dados de utilizados exportados com sucesso + Dados de utilizador importado com sucesso + Exportação cancelada + Verifiqua se as pastas de dados do utilizados estão na raiz da pasta zip e contêm um arquivo de configuração em config/config.ini e tenta novamente. https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu @@ -114,41 +157,53 @@ Estás interessado? - Ativar limite de velocidade - Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal. + Limite de velocidade + Limita a velocidade da emulação a uma porcentagem específica da velocidade normal. Percentagem do limite de velocidade - Especifica o limite da percentagem da velocidade da emulação. Com a velocidade por defeito a 100% a emulação será limitada à velocidade normal. Valores maiores ou menores aumentarão ou diminuirão o limite de velocidade. + Especifica a porcentagem para limitar a velocidade de emulação. 100% é o normal. Valores mais altos ou mais baixos irão aumentar ou diminuir o limite de velocidade. Precisão do CPU + %1$s%2$s - Modo ancorado - Emula em modo ancorado, que aumenta a resolução ás custas da performance. + Modo Ancorado + Aumenta a resolução, diminuindo o desempenho. O Modo Portátil é utilizado quando estiver desabilitado, diminuindo a resolução e melhorando o desempenho. Região da emulação Idioma da emulação - Seleciona a data RTC - Seleciona a hora RTC - Ativa RTC personalizado - Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema - Define RTC personalizado + Selecione a data do sistema + Selecione a hora do sistema + Data e hora personalizada + Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo. + Defina um relógio em tempo real personalizado - API Nível de precisão - Resolução + Resolução (Portátil/Ancorado) Modo VSync - Proporção do ecrã + Oriantação + Proporção da tela Filtro de Adaptação da Janela - Método de Anti-Aliasing + Método de Anti-Serrilhado Força velocidade máxima (Adreno only) Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas) Usa shaders assíncronos - Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas. + Compila os shaders de forma assíncrona, reduzindo travamentos, mas pode apresentar problemas. + Usar flushing reativo + Melhora a precisão da renderização em alguns jogos ao custo de desempenho. + Cache de shaders em disco + Reduz travamentos ao armazenar e carregar localmente os shaders. + + + CPU + Depuração da CPU + Coloca a CPU em um modo de depuração lento. + GPU + API Ativar depuração de gráficos Quando selecionado, a API gráfica entra num modo de depuração mais lento. - Usar cache de shaders em disco - Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento. + Fastmem + Motor de saída Volume Especifica o volume de saída. @@ -157,14 +212,24 @@ Definições guardadas Definições guardadas para %1$s Erro ao guardar %1$s.ini: %2$s + Menu não implementado A carregar... + A desligar... Queres reverter esta definição para os valores padrão? Reverter para padrão Redefinir todas as definições? - Todas as definições avançadas serão redefinidas para as definições padrão. Isto não pode ser revertido. + Todas as configurações avançadas retornarão ao padrão. Isto não pode ser desfeito. Redefinir definições Fechar Saiba mais + Automático + Enviar + Nenhum (desativado) + Importar + Exportar + Exportação falhada + IMportação falhada + A cancelar Seleciona a driver para o GPU @@ -172,6 +237,7 @@ Instalar Padrão Usar o driver padrão do GPU + Driver selecionado inválido, a usar o padrão do sistema! Driver do GPU padrão A instalar o Driver... @@ -182,10 +248,11 @@ Gráficos Áudio Cor e tema. + Depuração A tua ROM está encriptada - Cartidges de Jogo or Jogos Instalados.]]> + cartucho de jogo or títulos instalados.]]> 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. @@ -193,25 +260,25 @@ O ficheiro da ROM não existe - Sair da emulação + Parar emulação Feito Contador de FPS - Alterar Controlos - Centro do Analógico Relativo - Deslizar do DPad - Hápticos - Mostrar sobreposição - Alterar todos - Ajustar a sobreposição + Alterar controles + Centro Relativo de Analógico + Deslizamento dos Botões Direcionais + Vibração ao tocar + Mostrar overlay + Marcar/Desmarcar tudo + Ajustar overlay Escala Opacidade - Redefinir Sobreposição - Editar sobreposição - Pausa emulação + Restaurar overlay padrão + Editar overlay + Pausar emulação Retomar emulação - Opções de sobreposição + Opções de overlay - Configurações a carregar... + Carregando configurações... Teclado de software @@ -226,6 +293,9 @@ Erro fatal Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros. Desligar esta configuração irá reduzir a performance da emulação significantemente! Para a melhor experiência é recomendado que deixes esta configuração ativada. + RAM do dispositivo: %1$s\nRecommended: %2$s + %1$s %2$s + Nenhum jogo inicializável presente! Japão @@ -236,7 +306,14 @@ Coréia Taiwan - + + Byte + KB + MB + GB + TB + PB + EB Vulcano @@ -274,12 +351,17 @@ FXAA SMAA + + Landscape + Portrait + Automático + Padrão (16:9) Forçar 4:3 Forçar 21:9 Forçar 16:10 - Esticar para a janela + Esticar à janela Preciso @@ -287,7 +369,7 @@ Paranoid (Lento) - D-pad + Botões Direcionais Analógico esquerdo Analógico direito Botão Home @@ -298,18 +380,32 @@ A criar shaders - Muda o Tema da App + Mudar o tema do aplicativo Padrão Material You - Altera o Modo do Tema + Alterar o tema Igual ao Sistema Claro Escuro + + cubeb + - Usa Fundos Negros + Plano de fundo preto Quando usar tema escuro, aplicar fundos escuros - + + Picture in Picture + Minimizar a janela quando colocada em segundo plano + Pausa + Correr + Mudo + Unmute + + + Licenças + Upscaling de alta qualidade da AMD + diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index 0a1a47fbb..6afea9b03 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml @@ -1,5 +1,5 @@ - + Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro no armazenamento do teu dispositivo.<br /><br /> Emulação está Ativa @@ -25,6 +25,7 @@ Voltar Adiciona Jogos Seleciona a tua pasta de Jogos + Completo! Jogos @@ -37,7 +38,8 @@ Ignorar a seleção da pasta de jogos? Os jogos não serão exibidos na lista de jogos se uma pasta não estiver selecionada. https://yuzu-emu.org/help/quickstart/#dumping-games - Procurar Jogos + Procurar jogos + Procurar nas definições Pasta de Jogos selecionada Instala prod.keys Necessário para desencriptar jogos comerciais @@ -61,15 +63,18 @@ 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 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves. Instala driver para GPU Instala drivers alternativos para desempenho ou precisão potencialmente melhores Configurações avançadas + Definições avançadas: %1$s Configura configurações do emulador - Jogos recentes - Adicionados recentemente + Jogado recentemente + Adicionado recentemente Jogos comerciais Homebrew Abre a pasta Yuzu @@ -86,6 +91,33 @@ O nome da primeira sub pasta tem de ser a ID do jogo. Importar Exportar + Instalar firmware + O firmware deve estar em um arquivo ZIP e é necessário para iniciar alguns jogos. + Instalando firmware + Firmware instalado com sucesso. + Falha na instalação do firmware + Cofirma que os ficheiros firmware nca estão no root do finheiro zip e tenta de novo. + Compartilhe registros de debug. + Compartilhe o arquivo de registro do yuzu para obter ajuda com problemas + Arquivo de registro não encontrado + Instalar conteúdo adicional + Instale atualizações de jogos ou DLC + A instalar conteúdo... + Erro ao instalar ficheiro(s) para NAND + Por favor confitma que o conteúdo(s) é válido e que as prod.keys estão instaladas. + A instalação de jogos base não é permitida para evitar possíveis conflitos. + Sò conteúdos NSP e XCI são suportados. Por favor verifica que o conteúdo(s) do jogo são válidos. + %1$d erro(s) de instalação + Conteúdo(s) de jogo instalados com sucesso + %1$d instalado com sucesso + %1$d substituída com êxito + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + Drivers personalizados não suportados + Carrea«gamento de drivers personalizados não é suportado pr este dispositivo. \nCheck verifica esta opção de futuro para confirmar se o suporte foi adicionado! + Administrar dados yuzu + Importa/exporta firmware, chaves, dados do usuário e mais! + Partilha ficheiro duardado + Erro ao exportar dados guardados Gaia não é real @@ -94,7 +126,18 @@ Contribuidores Feito com \u2764 da equipa do Yuzu https://github.com/yuzu-emu/yuzu/graphs/contributors + Projetos que tornam o yuzu para Android possível Versão + Dado de utilizados + Importar/exportar todos dados da aplicação data.\n\n Ao importar dados do utilizados, todos os dados existentes do utilizados serão excluídos! + A exportar dados de utilizados... + A importar dados de utilizador... + Importar dados de utilizados... + Backup yuzu inválido + Dados de utilizados exportados com sucesso + Dados de utilizador importado com sucesso + Exportação cancelada + Verifiqua se as pastas de dados do utilizados estão na raiz da pasta zip e contêm um arquivo de configuração em config/config.ini e tenta novamente. https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu @@ -114,41 +157,53 @@ Estás interessado? - Ativar limite de velocidade - Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal. + Limite de velocidade + Limita a velocidade da emulação a uma porcentagem específica da velocidade normal. Percentagem do limite de velocidade - Especifica o limite da percentagem da velocidade da emulação. Com a velocidade por defeito a 100% a emulação será limitada à velocidade normal. Valores maiores ou menores aumentarão ou diminuirão o limite de velocidade. + Especifica a porcentagem para limitar a velocidade de emulação. 100% é o normal. Valores mais altos ou mais baixos irão aumentar ou diminuir o limite de velocidade. Precisão do CPU + %1$s%2$s - Modo ancorado - Emula em modo ancorado, que aumenta a resolução ás custas da performance. + Modo Ancorado + Aumenta a resolução, diminuindo o desempenho. O Modo Portátil é utilizado quando estiver desabilitado, diminuindo a resolução e melhorando o desempenho. Região da emulação Idioma da emulação - Seleciona a data RTC - Seleciona a hora RTC - Ativa RTC personalizado - Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema - Define RTC personalizado + Selecione a data do sistema + Selecione a hora do sistema + RTC personalizado + Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo. + Defina um relógio em tempo real personalizado - API Nível de precisão - Resolução + Resolução (Portátil/Ancorado) Modo VSync - Proporção do ecrã + Oriantação + Proporção da tela Filtro de Adaptação da Janela - Método de Anti-Aliasing + Método de Anti-Serrilhado Força velocidade máxima (Adreno only) Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas) Usa shaders assíncronos - Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas. + Compila os shaders de forma assíncrona, reduzindo travamentos, mas pode apresentar problemas. + Usar flushing reativo + Melhora a precisão da renderização em alguns jogos ao custo de desempenho. + Cache de shaders em disco + Reduz travamentos ao armazenar e carregar localmente os shaders. + + + CPU + Depuração da CPU + Coloca a CPU em um modo de depuração lento. + GPU + API Ativar depuração de gráficos Quando selecionado, a API gráfica entra num modo de depuração mais lento. - Usar cache do disk shader - Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento. + Fastmem + Motor de saída Volume Especifica o volume de saída. @@ -157,14 +212,24 @@ Configurações guardadas Configurações guardadas para %1$s Erro ao guardar %1$s.ini: %2$s + Menu não implementado A carregar... + A desligar... Queres reverter esta definição para os valores padrão? Reverter para padrão Redefinir todas as configurações? - Todas as configurações avançadas serão redefinidas para as definições padrão. Isto não pode ser revertido. + Todas as configurações avançadas retornarão ao padrão. Isto não pode ser desfeito. Redefinir configurações Fechar - Saber Mais + Saber mais + Automático + Enviar + Nenhum (desativado) + Importar + Exportar + Exportação falhada + IMportação falhada + A cancelar Seleciona a driver para o GPU @@ -172,6 +237,7 @@ Instalar Padrão Usar o driver padrão do GPU + Driver selecionado inválido, a usar o padrão do sistema! Driver do GPU padrão A instalar o Driver... @@ -182,10 +248,11 @@ Gráficos Audio Cor e tema. + Depurar A tua ROM está encriptada - Cartidges de Jogo or Jogos Instalados.]]> + cartucho de jogo or títulos instalados.]]> 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. @@ -193,28 +260,28 @@ O ficheiro da ROM não existe - Sair da emulação + Parar emulação Feito Contador de FPS - Alterar Controlos - Centro do Analógico Relativo - Deslizar do DPad - Hápticos - Mostrar sobreposição - Alterar todos - Ajustar a sobreposição + Alterar controles + Centro Relativo de Analógico + Deslizamento dos Botões Direcionais + Vibração ao tocar + Mostrar overlay + Marcar/Desmarcar tudo + Ajustar overlay Escala Opacidade - Redefinir Sobreposição - Editar sobreposição - Pausa emulação - Retomar emulação - Opções de sobreposição + Restaurar overlay padrão + Editar overlay + Pausar emulação + Despausar emulação + Opções de overlay - Configurações a carregar... + Carregando configurações... - Teclado de Software + Teclado de software Abortar @@ -226,6 +293,9 @@ Erro fatal Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros. Desligar esta configuração irá reduzir a performance da emulação significantemente! Para a melhor experiência é recomendado que deixes esta configuração ativada. + RAM do dispositivo: %1$s\nRecommended: %2$s + %1$s %2$s + Nenhum jogo inicializável presente! Japão @@ -236,7 +306,14 @@ Coreia Taiwan - + + Byte + KB + MB + GB + TB + PB + EB Vulcano @@ -274,12 +351,17 @@ FXAA SMAA + + Landscape + Portrait + Automático + Padrão (16:9) Forçar 4:3 Forçar 21:9 Forçar 16:10 - Esticar à Janela + Esticar à janela Preciso @@ -287,9 +369,9 @@ Paranoid (Lento) - D-Pad - Analógico Esquerdo - Analógico Direito + Botões Direcionais + Analógico esquerdo + Analógico direito Home Captura de ecrã @@ -298,18 +380,32 @@ A criar shaders - Muda o Tema da App + Mudar o tema do aplicativo Padrão Material You - Altera o Modo do Tema + Alterar o tema Igual ao Sistema Claro Escuro + + cubeb + - Usa Fundos Escuros + Plano de fundo preto Quando usar tema escuro, aplicar fundos escuros - + + Picture in Picture + Minimizar a janela quando colocada em segundo plano + Pausa + Correr + Mute + Unmute + + + Licenças + Upscaling de alta qualidade da AMD + diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 0bef035d6..c614257a8 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -1,5 +1,5 @@ - + Это программное обеспечение позволяет запускать игры для игровой консоли Nintendo Switch. Мы не предоставляем сами игры или ключи.<br /><br />Перед началом работы найдите файл prod.keys ]]> в хранилище устройства..<br /><br />Узнать больше]]> Эмуляция активна @@ -7,7 +7,7 @@ yuzu запущен Уведомления и ошибки Показывать уведомления, когда что-то пошло не так - Вы не предоставили разрешение уведомлений! + Вы не предоставили разрешение на уведомления! Добро пожаловать! @@ -25,6 +25,7 @@ Назад Добавить игры Выберите папку с играми + Выполнено! Игры @@ -38,6 +39,7 @@ Игры не будут отображаться в списке Игры, если папка не выбрана. https://yuzu-emu.org/help/quickstart/#dumping-games Найти игры + Настройки поиска Выбрана папка с играми Установить prod.keys Требуется для расшифровки розничных игр @@ -61,14 +63,17 @@ Выбран неверный файл ключей Ключи успешно установлены Ошибка при чтении ключей шифрования + Убедитесь, что файл ключей имеет расширение .keys, и повторите попытку. + Убедитесь, что файл ключей имеет расширение .bin, и повторите попытку. Неверные ключи шифрования https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys Выбранный файл неверен или поврежден. Пожалуйста, пере-дампите ваши ключи. Установить драйвер ГП Установите альтернативные драйверы для потенциально лучшей производительности и/или точности Расширенные настройки + Расширенные настройки: %1$s Настройка параметров эмулятора - Недавно сыграно + Недавно сыгранные Недавно добавлено Розничные Homebrew @@ -86,6 +91,34 @@ Название первой вложенной папки должно быть идентификатором игры. Импорт Экспорт + Установить прошивку + Прошивка должна находиться в ZIP-архиве и необходима для загрузки некоторых игр + Установка прошивки + Прошивка успешно установлена + Не удалось установить прошивку + Убедитесь что файлы прошивки nca находятся в корне zip-архива и повторите попытку. + Поделиться журналом отладки + Поделиться журналом отладки yuzu для устранения проблем + Файл журнала не найден + Установить игровой контент + Установить обновления игры или дополнений + Установка контента... + Ошибка установки файл(ов) в NAND. + Убедитесь что содержимое допустимо и что файл prod.keys установлен. + Установка базовых игр запрещена во избежание возможных конфликтов. + Поддерживается только контент NSP и XCI. Пожалуйста убедитесь что игровой контент действителен. + %1$d ошибка установки + Игровой контент успешно установлен + %1$d Успешно установлено + %1$d Успешно перезаписано + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + Пользовательские драйверы не поддерживаются + Загрузка пользовательского драйвера в настоящее время не поддерживается для этого устройства.\nПроверьте этот параметр еще раз в будущем чтобы узнать была ли добавлена ​​поддержка! +  + Управление данными yuzu + Импортируйте/экспортируйте прошивку, ключи, пользовательские данные и многое другое! + Поделиться файлом сохранения + Не удалось экспортировать сохранение Gaia не существует @@ -94,7 +127,18 @@ Контрибьюторы Сделано с \u2764 от команды yuzu https://github.com/yuzu-emu/yuzu/graphs/contributors + Проекты, которые сделали yuzu для Android возможным Сборка + Данные пользователя + Импортируйте/экспортируйте все данные приложения.\n\nПри импорте пользовательских данных все существующие пользовательские данные будут удалены! + Экспорт пользовательских данных… + Импорт пользовательских данных… + Импортировать пользовательские данные + Неверная резервная копия yuzu + Пользовательские данные успешно экспортированы + Пользовательские данные успешно импортированы + Экспорт отменен + Убедитесь что папки пользовательских данных находятся в корне zip-папки и содержат файл конфигурации config/config.ini и повторите попытку. https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu @@ -114,41 +158,51 @@ Вы заинтересованы? - Включить ограничение скорости - Если эта функция включена, скорость эмуляции будет ограничена указанным процентом от нормальной скорости. + Ограничить скорость + Ограничивает скорость эмуляции указанным процентом от нормальной скорости. Ограничение процента cкорости - Указывает процент для ограничения скорости эмуляции. При значении по умолчанию 100% эмуляция будет ограничена нормальной скоростью. Значения выше или ниже будут увеличивать или уменьшать ограничение скорости. + Указывает процент ограничения скорости эмуляции. 100% - это нормальная скорость. Значения больше или меньше увеличивают или уменьшают ограничение скорости. Точность ЦП + %1$s%2$s Режим док-станции - Эмуляция режима док-станции, что увеличивает разрешение за счет снижения производительности. - Эмулируемый регион - Эмулируемый язык + Увеличивает разрешение, снижая производительность. Портативный режим используется при отключении, снижая разрешение и повышая производительность. + Регион консоли + Язык консоли Выберите дату RTC Выберите время RTC - Включить пользовательский RTC - Этот параметр позволяет установить пользовательские часы реального времени отдельно от текущего системного времени + Пользовательский RTC + Позволяет установить пользовательские часы реального времени отдельно от текущего системного времени. Установить пользовательский RTC - API Уровень точности - Разрешение + Разрешение (портативное/в док-станции) Режим верт. синхронизации + Ориентация Соотношение сторон Фильтр адаптации окна Метод сглаживания Принудительно заставить максимальную тактовую частоту (только для Adreno) Заставляет ГП работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться). Использовать асинхронные шейдеры - Компилирует шейдеры асинхронно, что уменьшает зависания, но может взамен предоставить визуальные баги. - Включить отладку графики - Если включено, графический API переходит в более медленный режим отладки - Использовать кэш шейдеров на диске - Уменьшение зависаний за счет хранения и загрузки сгенерированных шейдеров на хранилище. + Компиляция шейдеров происходит асинхронно, что уменьшает зависания, но может привести к появлению багов. + Реактивная очистка + Повышение точности рендеринга в некоторых играх за счет снижения производительности. + Кэш шейдеров на диске + Уменьшение зависаний за счет хранения и загрузки сгенерированных шейдеров. + + + ЦП + Отладка ЦП + Переводит ЦП в режим медленной отладки. + графический процессор + API + Отладка графики + Переводит графический API в режим медленной отладки. + Fastmem - Громкость Задает громкость аудиовыхода. @@ -157,7 +211,9 @@ Сохраненные настройки Настройки сохранены для %1$s Ошибка сохранения %1$s.ini: %2$s + Нереализованное меню Загрузка... + Выключение… Хотите ли вы вернуть этот параметр к значению по умолчанию? Сброс к настройкам по умолчанию Сбросить все настройки? @@ -165,6 +221,14 @@ Настройки сброшены Закрыть Узнать больше + Авто + Отправить + Null + Импорт + Экспорт + Ошибка экспорта + Ошибка импортирования + Отменяю Выбрать драйвер ГП @@ -172,6 +236,7 @@ Установить По умолчанию Используется стандартный драйвер ГП + Выбран неверный драйвер, используется стандартный системный! Системный драйвер ГП Установка драйвера... @@ -182,10 +247,11 @@ Графика Аудио Тема и цвет + Отладка Ваш ROM зашифрованный - игровые картриджи или установленные игры.]]> + или установленные игры.]]> prod.keys установлен, чтобы игры можно было расшифровать.]]> Произошла ошибка при инициализации видеоядра. Обычно это вызвано несовместимым драйвером ГП. Установка пользовательского драйвера ГП может решить эту проблему. @@ -199,17 +265,17 @@ Переключение управления Относительный центр стика Слайд крестовиной - Тактильная обратная связь + Обратная связь от нажатий Показать оверлей Переключить всё - Настроить оверлей + Регулировка оверлея Масштаб Непрозрачность Сбросить оверлей - Изменить оверлей + Редактировать оверлей Пауза эмуляции - Возобновление эмуляции - Настройки оверлея + Возобновить эмуляцию + Настройка оверлея Загрузка настроек... @@ -226,6 +292,9 @@ Фатальная ошибка Произошла фатальная ошибка. Проверьте журнал для получения подробной информации.\nПродолжение эмуляции может привести к сбоям и ошибкам. Отключение этой настройки значительно снизит производительность эмуляции! Для достижения наилучших результатов рекомендуется оставить эту настройку включенной. + Оперативная память устройства: %1$s\nРекомендовано: %2$s + %1$s%2$s + Загрузочной игры нету! Япония @@ -236,7 +305,14 @@ Корея Тайвань - + + Байт + КБ + МБ + GB + ТБ + ПБ + ЕВ Vulkan @@ -274,6 +350,11 @@ FXAA SMAA + + Пейзаж + Портрет + Авто + Стандартное (16:9) Заставить 4:3 @@ -288,8 +369,8 @@ Крестовина - Левый мини-джойстик - Правый мини-джойстик + Левый стик + Правый стик Home Скриншот @@ -298,18 +379,32 @@ Постройка шейдеров - Изменить тему приложения + Сменить тему По умолчанию Material You - Изменить режим темы + Сменить режим темы Системная Светлая Темная + + cubeb + - Использовать черный фон + Чёрный фон При использовании темной темы применяйте черный фон. - + + Картинка в картинке + Свернуть окно при размещении в фоновом режиме + Пауза + Играть + Выключить звук + Включить звук + + + Лицензии + Высококачественное масштабирование от AMD + diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index 5b789ee98..34809dbb8 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -1,5 +1,5 @@ - + Це програмне забезпечення дозволяє запускати ігри для ігрової консолі Nintendo Switch. Ми не надаємо самі ігри або ключі.<br /><br />Перед початком роботи знайдіть ваш файл prod.keys ]]> у сховищі пристрою.<br /><br />Дізнатися більше]]> Емуляція активна @@ -25,7 +25,6 @@ Назад Додати ігри Виберіть папку з іграми - Ігри Пошук @@ -61,6 +60,7 @@ Вибрано неправильний файл ключів Ключі успішно встановлено Помилка під час зчитування ключів шифрування + Переконайтеся, що файл ключів має розширення .keys, і повторіть спробу. Невірні ключі шифрування https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys Обраний файл невірний або пошкоджений. Будь ласка, пере-дампіть ваші ключі. @@ -68,8 +68,6 @@ Встановіть альтернативні драйвери для потенційно кращої продуктивності та/або точності Розширені налаштування Налаштування параметрів емулятора - Нещодавно зіграно - Нещодавно додано Роздрібні Homebrew Відкрити папку yuzu @@ -86,7 +84,6 @@ Назва першої вкладеної папки має бути ідентифікатором гри. Імпорт Експорт - Gaia не існує Скопійовано в буфер обміну @@ -113,42 +110,20 @@ Наша нескінченна вдячність Ви зацікавлені? - - Увімкнути обмеження швидкості - Якщо цю функцію ввімкнено, швидкість емуляції буде обмежена зазначеним відсотком від нормальної швидкості. Обмеження відсотка швидкості - Вказує відсоток для обмеження швидкості емуляції. При значенні за замовчуванням 100% емуляція буде обмежена нормальною швидкістю. Значення вище або нижче збільшуватимуть або зменшуватимуть обмеження швидкості. Точність ЦП - - - Режим док-станції - Емуляція режиму док-станції, що збільшує роздільну здатність за рахунок зниження продуктивності. Емульований регіон Емульована мова - Оберіть дату RTC - Оберіть час RTC - Увімкнути користувацький RTC - Цей параметр дає змогу встановити користувацький годинник реального часу окремо від поточного системного часу - Встановити користувацький RTC - + Користувацький RTC - API Рівень точності - Роздільна здатність Режим верт. синхронізації - Співвідношення сторін - Фільтр адаптації вікна - Метод згладжування Примусово змусити максимальну тактову частоту (тільки для Adreno) Змушує ГП працювати на максимально можливих тактових частотах (теплові обмеження все одно будуть застосовуватися). Використовувати асинхронні шейдери - Компілює шейдери асинхронно, що зменшує зависання, але може натомість надати візуальні баги. - Увімкнути налагодження графіки - Якщо увімкнено, графічний API переходить у повільніший режим налагодження - Використовувати кеш шейдерів на диску - Зменшення зависань завдяки зберіганню та завантаженню згенерованих шейдерів на сховище. - - + + ЦП + API Гучність Вказує гучність аудіовиходу. @@ -161,17 +136,20 @@ Чи хочете ви повернути цей параметр до значення за замовчуванням? Скидання до налаштувань за замовчуванням Скинути всі налаштування - Усі додаткові налаштування буде скинуто до налаштування за замовчуванням. Це неможливо скасувати. Налаштування скинуто Закрити Дізнатися більше - + Авто + Null + Імпорт + Експорт Вибрати драйвер ГП Хочете замінити поточний драйвер ГП? Встановити За замовчуванням Використовується стандартний драйвер ГП + Обрано неправильний драйвер, використовується стандартний системний! Системний драйвер ГП Встановлення драйвера... @@ -182,40 +160,19 @@ Графіка Аудіо Тема і колір + Налагодження Ваш ROM зашифрований - ігрові картриджі або встановлені ігри.]]> prod.keys встановлено, щоб ігри можна було розшифрувати.]]> Сталася помилка під час ініціалізації відеоядра. Зазвичай це спричинено несумісним драйвером ГП. Встановлення користувацького драйвера ГП може вирішити цю проблему. Не вдалося запустити ROM Файл ROM не існує - - Вихід з емуляції Готово - Лічильник FPS - Перемикання керування - Відносний центр стіка - Слайд хрестовиною - Тактильний зворотний зв\'язок - Показати оверлей - Перемкнути все - Налаштувати оверлей Масштаб Непрозорість - Скинути оверлей - Змінити оверлей - Пауза емуляції - Відновлення емуляції - Налаштування оверлея - - Завантаження налаштувань... - - - Віртуальна клавіатура - Перервати Продовжити @@ -226,7 +183,6 @@ Фатальна помилка Сталася фатальна помилка. Перевірте журнал для отримання докладної інформації.\nПродовження емуляції може призвести до збоїв і помилок. Вимкнення цього налаштування значно знизить продуктивність емуляції! Для досягнення найкращих результатів рекомендується залишити це налаштування увімкненим. - Японія США @@ -236,8 +192,7 @@ Корея Тайвань - - + GB Vulkan Вимкнено @@ -274,22 +229,18 @@ FXAA SMAA + Авто + За замовчуванням (16:9) Змусити 4:3 Змусити 21:9 Змусити 16:10 - Розтягнути до вікна - Точно Небезпечно Параноїк (повільно) - - Кнопки напрямків - Лівий міні-джойстик - Правий міні-джойстик Home Знімок екрану @@ -297,19 +248,16 @@ Підготовка шейдерів Побудова шейдерів - - Змінити тему застосунку За замовчуванням Material You - - Змінити режим теми Системна Світла Темна - - Використовувати чорне тло У разі використання темної теми застосовуйте чорне тло. - + Вимкнути звук + Увімкнути звук + + diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml new file mode 100644 index 000000000..f977db3a2 --- /dev/null +++ b/src/android/app/src/main/res/values-vi/strings.xml @@ -0,0 +1,340 @@ + + + + Phần mềm này sẽ chạy các game cho máy chơi game Nintendo Switch. Không có title games hoặc keys được bao gồm.<br /><br />Trước khi bạn bắt đầu, hãy tìm tập tin prod.keys ]]> trên bộ nhớ thiết bị của bạn.<br /><br />Tìm hiểu thêm]]> + Giả lập đang chạy + Hiển thị thông báo liên tục khi giả lập đang chạy. + yuzu đang chạy + Thông báo và lỗi + Hiển thị thông báo khi có sự cố xảy ra. + Ứng dụng không được cấp quyền thông báo! + + + Chào mừng! + Tìm hiểu cách cài đặt <b>yuzu</b> và bắt đầu giả lập. + Bắt đầu + Keys + Chọn tệp <b>prod.keys</b> của bạn bằng nút bên dưới. + Chọn Keys + Game + Chọn thư mục <b>Game</b> của bạn bằng nút bên dưới. + Hoàn thành + Tất cả đã hoàn tất.\nHãy tận hưởng các game của bạn! + Tiếp tục + Tiếp theo + Trở lại + Thêm Game + Chọn thư mục game của bạn + + Game + Tìm kiếm + Cài đặt + Không tìm thấy tập tin hoặc chưa có thư mục game nào được chọn. + Tìm và lọc game + Chọn thư mục game + Cho phép yuzu thêm vào danh sách game + Bỏ qua việc lựa chọn thư mục game? + Game sẽ không hiển thị trong danh sách nếu một thư mục không được chọn. + https://yuzu-emu.org/help/quickstart/#dumping-games + Tìm kiếm game + Thư mục game đã được chọn + Cài đặt prod.keys + Yêu cầu để giải mã các game bán lẻ + Bỏ qua việc thêm keys? + Cần có keys hợp lệ để giả lập các game bán lẻ. Chỉ có các ứng dụng homebrew có thể vận hành nếu bạn tiếp tục. + https://yuzu-emu.org/help/quickstart/#guide-introduction + Thông báo + Cấp quyền thông báo bằng nút bên dưới. + Cấp quyền + Bỏ qua việc cấp quyền thông báo? + yuzu sẽ không thể gửi những thông báo quan trọng đến bạn. + Đã từ chối cấp quyền + Bạn từ chối cấp quyền này quá nhiều lần và giờ bạn phải cấp quyền thủ công trong cài đặt máy. + Thông tin + Phiên bản, đóng góp và những thứ khác + Trợ giúp + Bỏ qua + Hủy bỏ + Cài đặt keys Amiibo + Cần thiết để dùng Amiibo trong game + Tệp keys không hợp lệ đã được chọn + Cài đặt keys 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ệ + https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys + Tệp đã chọn sai hoặc hỏng. Vui lòng trích xuất lại keys của bạn. + 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 + Cấu hình cài đặt giả lập + Đã chơi gần đây + Đã thêm gần đây + Bán lẻ + Homebrew + Mở thư mục yuzu + Quản lý tệp nội bộ của yuzu + Thay đổi giao diện ứng dụng + Không tìm thấy trình quản lý tập tin + Không thể mở thư mục yuzu + Vui lòng xác định thư mục người dùng với bảng điều khiển bên của trình quản lý tệp thủ công. + Quản lý dữ liệu save + Đã tìm thấy dữ liệu save. Vui lòng chọn một tuỳ chọn bên dưới. + Nhập hoặc xuất tệp save + Nhập thành công + Cấu trúc thư mục save không hợp lệ + Tên thư mục con đầu tiên phải là ID title của game. + Nhập + Xuất + Cài đặt firmware + Firmware phải được đặt trong một tập tin nén ZIP và cần thiết để khởi chạy một số game + Đang cài đặt firmware + Cài đặt firmware thành công + Cài đặt firmware thất bại + Chia sẻ nhật ký gỡ lỗi + Chia sẻ tập tin nhật ký của yuzu để gỡ lỗi vấn đề + Không tìm thấy tập tin nhật ký + Cài đặt nội dung game + Cài đặt cập nhật game hoặc DLC + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + + Gaia không có thật + Đã sao chép vào bộ nhớ tạm + Một giả lập Switch mã nguồn mở + Người đóng góp + Được làm với \u2764 từ nhóm yuzu + https://github.com/yuzu-emu/yuzu/graphs/contributors + Các dự án làm cho yuzu trên Android trở thành điều có thể + Dựng + https://discord.gg/u77vRWY + https://yuzu-emu.org/ + https://github.com/yuzu-emu + + + Early Access + Tải Early Access + https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea + Các tính năng tiên tiến, truy cập sớm các bản cập nhật và nhiều hơn nữa + Lợi ích của Early Access + Tính năng tiên tiến + Truy cập sớm các bản cập nhật + Không có cài đặt thủ công + Ưu tiên hỗ trợ + Hỗ trợ bảo tồn game + Sự biết ơn vô hạn của chúng tôi + Bạn có thấy hứng thú không? + + + Giới hạn tốc độ + Giới hạn tốc độ giả lập ở một phần trăm cụ thể của tốc độ bình thường. + Giới hạn phần trăm tốc độ + Xác định phần trăm để giới hạn tốc độ giả lập. 100% là tốc độ bình thường. Giá trị cao hơn hoặc thấp hơn sẽ tăng hoặc giảm giới hạn tốc độ. + Độ chính xác CPU + + Chế độ docked + Tăng độ phân giải, giảm hiệu suất. Chế độ handheld được sử dụng khi tắt, giảm độ phân giải và tăng hiệu suất. + Khu vực giả lập + Ngôn ngữ giả lập + Chọn ngày RTC + Chọn giờ RTC + RTC tuỳ chỉnh + Cho phép bạn thiết lập một đồng hồ thời gian thực tùy chỉnh riêng biệt so với thời gian hệ thống hiện tại. + Thiết lập RTC tùy chỉnh + + + Mức độ chính xác + Độ phân giải (Handheld/Docked) + Chế độ VSync + Tỉ lệ khung hình + Bộ lọc điều chỉnh cửa sổ + Phương pháp khử răng cưa + Buộc chạy ở xung nhịp tối đa (chỉ cho Adreno) + Buộc GPU hoạt động ở xung nhịp tối đa có thể (ràng buộc nhiệt độ vẫn sẽ được áp dụng). + Dùng các shader bất đồng bộ + Biên dịch các shader bất đồng bộ, giảm tình trạng giật lag nhưng có thể gây ra các lỗi. + Dùng xả tương ứng + Cải thiện độ chính xác kết xuất trong một số game nhưng đồng thời giảm hiệu suất. + Lưu bộ nhớ đệm shader trên ổ cứng + Giảm tình trạng giật lag bằng cách lưu trữ và tải các shader được tạo ra nội bộ. + + + CPU + API + Gỡ lỗi đồ hoạ + Đặt API đồ họa vào chế độ gỡ lỗi chậm. + Âm lượng + Xác định âm lượng của đầu ra âm thanh. + + + Mặc định + Cài đặt đã lưu + Cài đặt đã lưu cho %1$s + Lỗi khi lưu %1$s.ini: %2$s + Đang tải... + Bạn có muốn đặt lại cài đặt này về giá trị mặc định không? + Đặt lại về mặc định + Bạn có muốn đặt lại tất cả các cài đặt về giá trị mặc định không? + Tất cả các cài đặt nâng cao sẽ được đặt lại về cấu hình mặc định. Điều này không thể hoàn tác. + Cài đặt đã được đặt lại + Đóng + Tìm hiểu thêm + Tự động + Gửi + Null + Nhập + Xuất + + Chọn driver GPU + Bạn có muốn thay thế driver GPU hiện tại không? + Cài đặt + Mặc định + Dùng driver GPU mặc định + Driver không hợp lệ đã được chọn, dùng mặc định hệ thống! + Driver GPU hệ thống + Đang cài đặt driver... + + + Cài đặt + Chung + Hệ thống + Đồ hoạ + Âm thanh + Chủ đề và màu sắc + Gỡ lỗi + + + ROM của bạn đã bị mã hoá + prod.keys đã được cài đặt để các 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ể nạp ROM + Tệp ROM không tồn tại + + + Thoát giả lập + Hoàn thành + Bộ đếm FPS + Chuyển đổi điều khiển + Trung tâm nút cần xoay tương đối + Trượt D-pad + Chạm haptics + Hiện lớp phủ + Chuyển đổi tất cả + Điều chỉnh lớp phủ + Tỉ lệ thu phóng + Độ mờ + Đặt lại lớp phủ + Chỉnh sửa lớp phủ + Tạm đừng giả lập + Tiếp tục giả lập + Tuỳ chọn lớp phủ + + Đang tải cài đặt... + + + Bàn phím mềm + + + Hủy bỏ + Tiếp tục + Không tìm thấy bản lưu trữ của hệ thống + %s bị thiếu. Vui lòng trích xuất các bản lưu trữ hệ thống của bạn.\nNếu chạy tiếp giả lập có thể bị crash và lỗi. + Một bản lưu trữ của hệ thống + Lỗi Lưu/Tải + Lỗi nghiêm trọng + Đã xảy ra lỗi nghiêm trọng. Kiểm tra nhật ký để biết thêm chi tiết.\nNếu chạy tiếp giả lập có thể bị crash và lỗi. + Tắt cài đặt này sẽ làm giảm đáng kể hiệu suất giả lập! Để có trải nghiệm tốt nhất, bạn nên bật cài đặt này. + + Nhật Bản + Hoa Kỳ + Châu Âu + Úc + Trung Quốc + Hàn Quốc + Đài Loan + + GB + + Vulkan + Không có + + + Bình thường + Cao + Cực đại (Chậm) + + + 0.5X (360p/540p) + 0.75X (540p/810p) + 1X (720p/1080p) + 2X (1440p/2160p) (Chậm) + 3X (2160p/3240p) (Chậm) + 4X (2880p/4320p) (Chậm) + + + Immediate (Tắt) + Mailbox + FIFO (Bật) + FIFO Relaxed + + + Nearest Neighbor + Bilinear + Bicubic + Gaussian + ScaleForce + AMD FidelityFX™ Super Resolution + + + Không có + FXAA + SMAA + + Tự động + + + Mặc định (16:9) + Dùng 4:3 + Dùng 21:9 + Dùng 16:10 + Mở rộng đến cửa sổ + + + Chính xác + Không an toàn + Paranoid (Chậm) + + + D-pad + Cần trái + Cần phải + Home + Ảnh chụp màn hình + + + Đang chuẩn bị shader + Đang đựng shader + + + Thay đổi chủ đề ứng dụng + Mặc định + Material You + + + Thay đổi chủ đề + Theo hệ thống + Sáng + Tối + + + Nền đen + Khi sử dụng chủ đề tối, hãy áp dụng nền đen. + + Tắt tiếng + Bật tiếng + + + Giấy phép + Upscaling chất lượng cao từ AMD + diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index c0e885751..13455564f 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -1,5 +1,5 @@ - + 此软件可以运行 Nintendo Switch 游戏,但不包含任何游戏和密钥文件。<br /><br />在开始前,请找到放置于设备存储中的 prod.keys ]]> 文件。<br /><br />了解更多]]> 正在进行模拟 @@ -17,7 +17,7 @@ 使用下方的按钮来选择你的 <b>prod.keys</b> 文件。 选择密钥文件 游戏 - 使用下方的按钮选择你的 <b>游戏</b> 文件夹。 + 使用下方的按钮选择你的<b>游戏</b>文件夹。 完成 你完成了全部设置。\n玩的开心! 继续 @@ -25,6 +25,7 @@ 上一步 添加游戏 选择你的游戏文件夹 + 完成! 游戏 @@ -38,6 +39,7 @@ 如果未选择游戏文件夹,游戏将不会显示在游戏列表中。 https://yuzu-emu.org/help/quickstart/#dumping-games 搜索游戏 + 搜索设置 已选择游戏文件夹 安装 prod.keys 文件 需要密钥文件来解密游戏 @@ -61,12 +63,15 @@ 选择的密钥文件无效 密钥文件已成功安装 读取加密密钥时出错 + 请确保您的密钥文件扩展名为 .keys 并重试。 + 请确保您的密钥文件扩展名为 .bin 并重试。 无效的加密密钥 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys 选择的密钥文件不正确或已损坏。请重新转储密钥文件。 安装 GPU 驱动 安装替代的驱动程序以获得更好的性能和精度 高级选项 + 高级选项: %1$s 更改模拟器设置 最近游玩 最近添加 @@ -86,6 +91,33 @@ 第一个子文件夹名称必须为当前游戏的 ID。 导入 导出 + 安装固件 + 固件文件必须为 zip 格式,启动某些游戏时必需 + 正在安装固件 + 固件已成功安装 + 固件安装失败 + 请确保固件 nca 文件位于 zip 压缩包的根目录,然后重试。 + 分享调试日志 + 分享 yuzu 日志文件以便调试 + 未找到日志文件 + 安装游戏附加内容 + 安装游戏更新及 DLC + 安装中... + 向 NAND 安装文件时失败 + 请确保附加内容的有效性,并且 prod.keys 密钥文件已安装。 + 为避免产生冲突,此功能不能用于安装游戏本体。 + 只有 NSP 或 XCI 格式的附加内容可以安装。请确保您的游戏附加内容是有效的。 + %1$d 安装出错 + 游戏附加内容已成功安装 + %1$d 安装成功 + %1$d 覆盖安装成功 + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + 不支持自定义驱动 + 此设备不支持自定义驱动。\n请之后再访问此项,查看是否已为此设备添加支持。 + 管理 yuzu 数据 + 导入/导出固件、密钥、用户数据及其他。 + 分享存档文件 + 导出存档文件失败 Gaia 不真实 @@ -94,14 +126,25 @@ 贡献者 使用来自 yuzu 团队的 \u2764 制作 https://github.com/yuzu-emu/yuzu/graphs/contributors + Android 版 yuzu 离不开这些项目的支持 构建版本 + 用户数据 + 导入/导出应用程序所有数据。\n\n导入用户数据时,将删除当前所有的用户数据! + 正在导出用户数据... + 正在导入用户数据... + 导入用户数据 + 无效的 yuzu 备份 + 导出用户数据成功 + 导入用户数据成功 + 已取消导出数据 + 请确保用户数据文件夹位于 zip 压缩包的根目录,并在 config/config.ini 路径中包含配置文件,然后重试。 https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu 抢先体验 - 取得抢先体验 + 获取抢先体验! https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea 最新的功能、抢先更新、以及更多 抢先体验的权益 @@ -109,33 +152,34 @@ 抢先更新 无需手动安装 优先支持 - 帮助保留游戏 + 帮助保留游玩历史 我们真诚的感激 您对此感兴趣吗? - 启用运行速度限制 - 启用后,模拟速度将限制在正常运行速度的指定百分比。 + 运行速度限制 + 将运行速度限制为正常速度的指定百分比。 限制速度百分比 - 指定限制模拟速度的百分比。预设为 100%,此时模拟速度将被限制为标准速度。更高或更低的值将增加或降低速度限制上限。 + 指定限制运行速度的百分比。100% 为正常速度。更高或更低的值将增加或降低速度限制上限。 CPU 精度 + %1$s%2$s 主机模式 - 以主机模式进行模拟,牺牲性能并提高画面分辨率。 + 提高分辨率,但降低性能。禁用此项时使用掌机模式,降低分辨率并提高性能。 模拟区域 模拟语言 选择日期 选择时间 - 启用自定义系统时钟 - 此选项允许您设置与目前系统时间相独立的自定义系统时钟 - 设置自定义系统时钟 + 自定义系统时间 + 此选项允许您设置与目前系统时间相独立的自定义系统时钟。 + 设置自定义系统时间 - API 精度等级 - 分辨率 + 分辨率 (掌机模式/主机模式) 垂直同步模式 + 屏幕方向 屏幕纵横比 窗口滤镜 抗锯齿方式 @@ -143,12 +187,23 @@ 强制 GPU 以最大时钟运行 (仍被温控限制)。 使用异步着色器 异步编译着色器,减少卡顿,但可能引入故障。 - 启用图形调试 - 启用时,图形 API 将进入较慢的调试模式。 - 使用磁盘着色器缓存 - 将生成的着色器缓存于磁盘中并进行读取以减少卡顿。 + 启用反应性刷新 + 牺牲性能,提高某些游戏的渲染精度。 + 磁盘着色器缓存 + 将生成的着色器缓存于磁盘中并进行读取,以减少卡顿。 + + + CPU + CPU 调试 + 将 CPU 设置为较慢的调试模式。 + GPU + API + 图形调试 + 将图形 API 设置为较慢的调试模式。 + Fastmem + 输出引擎 音量 指定输出的音量。 @@ -157,7 +212,9 @@ 已保存设置 已保存 %1$s 的设置 保存 %1$s.ini 时出错: %2$s + 未生效菜单 加载中… + 正在关闭… 您要将此设定重设为默认值吗? 恢复默认 重置所有设置项? @@ -165,6 +222,14 @@ 重设设置项 关闭 了解更多 + 自动 + 提交 + + 导入 + 导出 + 导出失败 + 导入失败 + 取消中 选择 GPU 驱动程序 @@ -172,6 +237,7 @@ 安装 系统默认 使用默认 GPU 驱动程序 + 选择的驱动程序无效,将使用系统默认的驱动程序! 系统 GPU 驱动程序 正在安装驱动程序… @@ -182,10 +248,11 @@ 图形 声音 主题和色彩 + 调试 您的 ROM 已加密 - 游戏卡带或已安装的游戏。]]> + 游戏卡带或已安装的游戏。]]> prod.keys 文件已安装,使得游戏可以被解密。]]> 初始化视频核心时发生错误 这通常由不兼容的 GPU 驱动程序造成,安装自定义 GPU 驱动程序可能解决此问题。 @@ -226,6 +293,9 @@ 致命错误 发生致命错误,请查阅日志获取详细信息。\n继续模拟可能会造成崩溃和错误。 关闭此项会显著降低模拟性能!建议您将此项保持为启用状态。 + 设备 RAM: %1$s\n推荐 RAM: %2$s + %1$s%2$s + 当前没有可启动的游戏! 日本 @@ -236,7 +306,14 @@ 韩国 中国台湾 - + + Byte + KB + MB + GB + TB + PB + EB Vulkan @@ -274,6 +351,11 @@ 快速近似抗锯齿 子像素形态学抗锯齿 + + 横向大屏 + 纵向屏幕 + 自动 + 默认 (16:9) 强制 4:3 @@ -303,13 +385,27 @@ Material You - 主题模式 + 更改主题模式 跟随系统 浅色 深色 + + cubeb + 使用黑色背景 使用深色主题时,套用黑色背景。 - + + 画中画 + 模拟器位于后台时最小化窗口 + 暂停 + 开始 + 静音 + 取消静音 + + + 许可证 + 来自 AMD 的高品质画质升级 + diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index 4a21bf893..b8f468c68 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -1,5 +1,5 @@ - + 此軟體可以執行 Nintendo Switch 主機遊戲,但不包含任何遊戲和金鑰。<br /><br />在您開始前,請找到放置於您的裝置儲存空間的 prod.keys ]]> 檔案。<br /><br />深入瞭解]]> 模擬進行中 @@ -25,6 +25,7 @@ 上一步 新增遊戲 選取您的遊戲資料夾 + 完成! 遊戲 @@ -33,11 +34,12 @@ 找不到檔案,或者尚未選取遊戲目錄。 搜尋並篩選遊戲 選取遊戲資料夾 - 一律允許 yuzu 填入遊戲清單 + 允許 yuzu 填入遊戲清單 跳過選取遊戲資料夾? 如果資料夾未選取,遊戲將不會顯示在遊戲清單。 https://yuzu-emu.org/help/quickstart/#dumping-games 搜尋遊戲 + 搜索设置 遊戲目錄已選取 安裝 prod.keys 需要解密零售遊戲 @@ -60,13 +62,16 @@ 需要在遊戲中使用 Amiibo 無效的金鑰檔案已選取 金鑰已成功安裝 - 讀取加密金鑰時出現錯誤 + 讀取加密金鑰時發生錯誤 + 驗證您的金鑰檔案是否具有 .keys 副檔名並再試一次。 + 驗證您的金鑰檔案是否具有 .bin 副檔名並再試一次。 無效的加密金鑰 https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys 選取的檔案不正確或已損毀,請重新傾印您的金鑰。 安裝 GPU 驅動程式 安裝替代驅動程式以取得潛在的更佳效能或準確度 進階設定 + 高级选项: %1$s 進行模擬器設定 最近遊玩 最近新增 @@ -86,6 +91,33 @@ 首個子資料夾名稱必須為遊戲標題 ID。 匯入 匯出 + 安裝韌體 + 韌體必須為 ZIP 封存檔,將會用於部分遊戲的啟動 + 正在安裝韌體 + 韌體已成功安裝 + 韌體安裝失敗 + 请确保固件 nca 文件位于 zip 压缩包的根目录,然后重试。 + 分享偵錯記錄 + 分享 yuzu 的記錄檔以便對相關問題進行偵錯 + 找不到記錄檔 + 安裝遊戲內容 + 安裝遊戲更新或 DLC + 安装中... + 向 NAND 安装文件时失败 + 请确保附加内容的有效性,并且 prod.keys 密钥文件已安装。 + 为避免产生冲突,此功能不能用于安装游戏本体。 + 只有 NSP 或 XCI 格式的附加内容可以安装。请确保您的游戏附加内容是有效的。 + %1$d 安装出错 + 游戏附加内容已成功安装 + %1$d 安装成功 + %1$d 覆盖安装成功 + https://yuzu-emu.org/help/quickstart/#dumping-installed-updates + 不支持自定义驱动 + 此设备不支持自定义驱动。\n请之后再访问此项,查看是否已为此设备添加支持。 + 管理 yuzu 数据 + 导入/导出固件、密钥、用户数据及其他。 + 分享存档文件 + 导出存档文件失败 Gaia 不真實 @@ -94,7 +126,18 @@ 參與者 使用來自 yuzu 團隊的 \u2764 製作 https://github.com/yuzu-emu/yuzu/graphs/contributors + 這些專案使 yuzu Android 版成為可能 組建 + 用户数据 + 导入/导出应用程序所有数据。\n\n导入用户数据时,将删除当前所有的用户数据! + 正在导出用户数据... + 正在导入用户数据... + 导入用户数据 + 无效的 yuzu 备份 + 导出用户数据成功 + 导入用户数据成功 + 已取消导出数据 + 请确保用户数据文件夹位于 zip 压缩包的根目录,并在 config/config.ini 路径中包含配置文件,然后重试。 https://discord.gg/u77vRWY https://yuzu-emu.org/ https://github.com/yuzu-emu @@ -114,28 +157,29 @@ 您仍感興趣嗎? - 啟用限制速度 - 若啟用,模擬速度將會限制在標準速度的指定百分比。 + 限制速度 + 將模擬速度限制在標準速度的指定百分比。 限制速度百分比 - 指定限制模擬速度的百分比。預設為 100%,模擬速度將被限制為標準速度。更高或更低的值將會增加或減少速度限制。 + 指定限制模擬速度的百分比。100% 為標準速度,更高或更低的值將會增加或減少速度限制。 CPU 準確度 + %1$s%2$s 底座模式 - 以底座模式模擬,以犧牲效能的代價提高解析度。 + 提高解析度,降低效能。停用後將會使用手提模式,會降低解析度並提高效能。 模擬區域 模擬語言 選取 RTC 日期 選取 RTC 時間 - 啟用自訂 RTC - 此設定允許您設定與您的目前系統時間相互獨立的自訂即時時鐘 + 自訂 RTC + 允許您設定與您的目前系統時間相互獨立的自訂即時時鐘。 設定自訂 RTC - API 準確度層級 - 解析度 + 解析度 (手提/底座) VSync 模式 + 屏幕方向 長寬比 視窗適應過濾器 消除鋸齒方法 @@ -143,12 +187,23 @@ 強制 GPU 以最大可能時脈執行 (熱溫限制仍被套用)。 使用非同步著色器 非同步編譯著色器,將會減少間斷,但可能會引入故障。 - 啟用圖形偵錯 - 核取時,圖形 API 將會進入慢速偵錯模式。 - 使用磁碟著色器快取 + 使用重新啟用排清 + 犧牲效能,以改善部分遊戲的轉譯準確度。 + 磁碟著色器快取 透過將產生的著色器儲存並載入至磁碟,減少中斷。 + + CPU + CPU 调试 + 将 CPU 设置为较慢的调试模式。 + GPU + API + 圖形偵錯 + 將圖形 API 設為慢速偵錯模式。 + Fastmem + + 输出引擎 音量 指定音訊輸出音量。 @@ -157,7 +212,9 @@ 已儲存設定 已儲存 %1$s 設定 儲存 %1$s 時發生錯誤 ini: %2$s + 未生效菜单 正在載入… + 正在关闭… 要將此設定重設回預設值嗎? 重設為預設值 重設所有設定? @@ -165,6 +222,14 @@ 設定已重設 關閉 深入瞭解 + 自動 + 提交 + + 匯入 + 匯出 + 导出失败 + 导入失败 + 取消中 選取 GPU 驅動程式 @@ -172,6 +237,7 @@ 安裝 預設 使用預設 GPU 驅動程式 + 選取的驅動程式無效,將使用系統預設驅動程式! 系統 GPU 驅動程式 正在安裝驅動程式… @@ -182,10 +248,11 @@ 圖形 音訊 主題和色彩 + 偵錯 您的 ROM 已加密 - 遊戲卡匣或安裝標題。]]> + 游戏卡带或已安装的游戏。]]> prod.keys 檔案已安裝,讓遊戲可以解密。]]> 初始化視訊核心時發生錯誤 這經常由不相容的 GPU 驅動程式造成,安裝自訂 GPU 驅動程式可能會解決此問題。 @@ -219,13 +286,16 @@ 中止 繼續 - 找不到系統檔案 + 找不到系統封存 %s 遺失,請傾印您的系統封存。\n繼續模擬可能會造成當機和錯誤。 系統封存 儲存/載入發生錯誤 嚴重錯誤 發生嚴重錯誤,檢查記錄以取得詳細資訊。\n繼續模擬可能會造成當機和錯誤。 關閉此設定會顯著降低模擬效能!如需最佳體驗,建議您將此設定保持為啟用狀態。 + 设备 RAM: %1$s\n推荐 RAM: %2$s + %1$s%2$s + 当前没有可启动的游戏! 日本 @@ -236,7 +306,14 @@ 南韓 台灣 - + + Byte + KB + MB + 英國 + TB + PB + EB Vulkan @@ -274,14 +351,20 @@ FXAA SMAA + + 横向大屏 + 纵向屏幕 + 自動 + 預設 (16:9) 強制 4:3 強制 21:9 強制 16:10 - 延伸視窗 + 延展視窗 + 高精度 低精度 不合理 (慢) @@ -307,8 +390,22 @@ 淺色 深色 + + cubeb + - 使用黑色背景 + 黑色背景 使用深色主題時,套用黑色背景。 - + + 画中画 + 模拟器位于后台时最小化窗口 + 暂停 + 开始 + 靜音 + 取消靜音 + + + 授權 + 來自 AMD 的升級圖像品質 + -- cgit v1.2.3 From 97b4ca1d01fef5fb350125d103e6aff89cd92108 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Tue, 31 Oct 2023 20:29:16 -0400 Subject: android: Auto-generate locale config --- src/android/app/build.gradle.kts | 4 ++++ src/android/app/src/main/AndroidManifest.xml | 1 - src/android/app/src/main/res/resources.properties | 1 + src/android/app/src/main/res/xml/locales_config.xml | 17 ----------------- 4 files changed, 5 insertions(+), 18 deletions(-) create mode 100644 src/android/app/src/main/res/resources.properties delete mode 100644 src/android/app/src/main/res/xml/locales_config.xml (limited to 'src') diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index ac43d84b7..021b070e0 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -47,6 +47,10 @@ android { jniLibs.useLegacyPackaging = true } + androidResources { + generateLocaleConfig = true + } + defaultConfig { // TODO If this is ever modified, change application_id in strings.xml applicationId = "org.yuzu.yuzu_emu" diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index a67351727..f10131b24 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -26,7 +26,6 @@ SPDX-License-Identifier: GPL-3.0-or-later android:supportsRtl="true" android:isGame="true" android:appCategory="game" - android:localeConfig="@xml/locales_config" android:banner="@drawable/tv_banner" android:fullBackupContent="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules_api_31" diff --git a/src/android/app/src/main/res/resources.properties b/src/android/app/src/main/res/resources.properties new file mode 100644 index 000000000..467b3efec --- /dev/null +++ b/src/android/app/src/main/res/resources.properties @@ -0,0 +1 @@ +unqualifiedResLocale=en-US diff --git a/src/android/app/src/main/res/xml/locales_config.xml b/src/android/app/src/main/res/xml/locales_config.xml deleted file mode 100644 index 51b88d9dc..000000000 --- a/src/android/app/src/main/res/xml/locales_config.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - -- cgit v1.2.3 From b0c6bf497a1eabec14c116b710dcc757e77455bf Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 31 Oct 2023 20:11:14 -0400 Subject: romfs: fix extraction of single-directory root --- src/core/file_sys/romfs.cpp | 44 ++++++++-------------- src/core/file_sys/romfs.h | 9 +---- .../hle/service/am/applets/applet_web_browser.cpp | 3 +- src/yuzu/main.cpp | 2 +- 4 files changed, 18 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 1c580de57..1eb1f439a 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp @@ -35,13 +35,14 @@ struct RomFSHeader { static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); struct DirectoryEntry { + u32_le parent; u32_le sibling; u32_le child_dir; u32_le child_file; u32_le hash; u32_le name_length; }; -static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size."); +static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size."); struct FileEntry { u32_le parent; @@ -64,25 +65,22 @@ std::pair GetEntry(const VirtualFile& file, std::size_t offs return {entry, string}; } -void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset, - u32 this_file_offset, std::shared_ptr parent) { - while (true) { +void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset, + u32 this_file_offset, std::shared_ptr& parent) { + while (this_file_offset != ROMFS_ENTRY_EMPTY) { auto entry = GetEntry(file, file_offset + this_file_offset); parent->AddFile(std::make_shared( file, entry.first.size, entry.first.offset + data_offset, entry.second)); - if (entry.first.sibling == ROMFS_ENTRY_EMPTY) - break; - this_file_offset = entry.first.sibling; } } -void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset, +void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size_t file_offset, std::size_t data_offset, u32 this_dir_offset, - std::shared_ptr parent) { - while (true) { + std::shared_ptr& parent) { + while (this_dir_offset != ROMFS_ENTRY_EMPTY) { auto entry = GetEntry(file, dir_offset + this_dir_offset); auto current = std::make_shared( std::vector{}, std::vector{}, entry.second); @@ -97,14 +95,12 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file } parent->AddDirectory(current); - if (entry.first.sibling == ROMFS_ENTRY_EMPTY) - break; this_dir_offset = entry.first.sibling; } } } // Anonymous namespace -VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { +VirtualDir ExtractRomFS(VirtualFile file) { RomFSHeader header{}; if (file->ReadObject(&header) != sizeof(RomFSHeader)) return nullptr; @@ -113,27 +109,17 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { return nullptr; const u64 file_offset = header.file_meta.offset; - const u64 dir_offset = header.directory_meta.offset + 4; - - auto root = - std::make_shared(std::vector{}, std::vector{}, - file->GetName(), file->GetContainingDirectory()); - - ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root); + const u64 dir_offset = header.directory_meta.offset; - VirtualDir out = std::move(root); + auto root_container = std::make_shared(); - if (type == RomFSExtractionType::SingleDiscard) - return out->GetSubdirectories().front(); + ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container); - while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) { - if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" && - type == RomFSExtractionType::Truncated) - break; - out = out->GetSubdirectories().front(); + if (auto root = root_container->GetSubdirectory(""); root) { + return std::make_shared(std::move(root)); } - return std::make_shared(std::move(out)); + return nullptr; } VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index 5d7f0c2a8..b75ff1aad 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h @@ -7,16 +7,9 @@ namespace FileSys { -enum class RomFSExtractionType { - Full, // Includes data directory - Truncated, // Traverses into data directory - SingleDiscard, // Traverses into the first subdirectory of root -}; - // Converts a RomFS binary blob to VFS Filesystem // Returns nullptr on failure -VirtualDir ExtractRomFS(VirtualFile file, - RomFSExtractionType type = RomFSExtractionType::Truncated); +VirtualDir ExtractRomFS(VirtualFile file); // Converts a VFS filesystem into a RomFS binary // Returns nullptr on failure diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index 1c9a1dc29..b0ea2b381 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -330,8 +330,7 @@ void WebBrowser::ExtractOfflineRomFS() { LOG_DEBUG(Service_AM, "Extracting RomFS to {}", Common::FS::PathToUTF8String(offline_cache_dir)); - const auto extracted_romfs_dir = - FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); + const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs); const auto temp_dir = system.GetFilesystem()->CreateDirectory( Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 0df163029..db9da6dc8 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2737,7 +2737,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); + const auto extracted = FileSys::ExtractRomFS(romfs); if (extracted == nullptr) { failed(); return; -- cgit v1.2.3 From 2b6edd3efd1c3a88ed47249b77e7b12951d8a13d Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Wed, 1 Nov 2023 00:17:38 -0400 Subject: android: Reorganize settings tab --- .../yuzu_emu/fragments/HomeSettingsFragment.kt | 66 +++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) (limited to 'src') 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 6e19fc6c0..ed2a5cb55 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 @@ -84,28 +84,6 @@ class HomeSettingsFragment : Fragment() { } ) ) - add( - HomeSetting( - R.string.open_user_folder, - R.string.open_user_folder_description, - R.drawable.ic_folder_open, - { openFileManager() } - ) - ) - add( - HomeSetting( - R.string.preferences_theme, - R.string.theme_and_color_description, - R.drawable.ic_palette, - { - val action = HomeNavigationDirections.actionGlobalSettingsActivity( - null, - Settings.MenuTag.SECTION_THEME - ) - binding.root.findNavController().navigate(action) - } - ) - ) add( HomeSetting( R.string.gpu_driver_manager, @@ -121,17 +99,6 @@ class HomeSettingsFragment : Fragment() { driverViewModel.selectedDriverMetadata ) ) - add( - HomeSetting( - R.string.manage_yuzu_data, - R.string.manage_yuzu_data_description, - R.drawable.ic_install, - { - binding.root.findNavController() - .navigate(R.id.action_homeSettingsFragment_to_installableFragment) - } - ) - ) add( HomeSetting( R.string.applets, @@ -146,6 +113,17 @@ class HomeSettingsFragment : Fragment() { R.string.applets_error_description ) ) + add( + HomeSetting( + R.string.manage_yuzu_data, + R.string.manage_yuzu_data_description, + R.drawable.ic_install, + { + binding.root.findNavController() + .navigate(R.id.action_homeSettingsFragment_to_installableFragment) + } + ) + ) add( HomeSetting( R.string.select_games_folder, @@ -170,6 +148,28 @@ class HomeSettingsFragment : Fragment() { { shareLog() } ) ) + add( + HomeSetting( + R.string.open_user_folder, + R.string.open_user_folder_description, + R.drawable.ic_folder_open, + { openFileManager() } + ) + ) + add( + HomeSetting( + R.string.preferences_theme, + R.string.theme_and_color_description, + R.drawable.ic_palette, + { + val action = HomeNavigationDirections.actionGlobalSettingsActivity( + null, + Settings.MenuTag.SECTION_THEME + ) + binding.root.findNavController().navigate(action) + } + ) + ) add( HomeSetting( R.string.about, -- cgit v1.2.3 From 5872c7d420064a2831520aa3b31a4070c7a17484 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Wed, 1 Nov 2023 00:18:20 -0400 Subject: android: Adjust driver manager source string --- src/android/app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index b92978140..c551a6106 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -72,7 +72,7 @@ Invalid encryption keys https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys The selected file is incorrect or corrupt. Please redump your keys. - GPU Driver Manager + GPU driver manager Install GPU driver Install alternative drivers for potentially better performance or accuracy Advanced settings -- cgit v1.2.3 From 344162db75bffd0de9b43d18d2d789c1fd564e57 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Wed, 1 Nov 2023 13:10:51 -0400 Subject: android: Default to player number 0 if we get an input from an unrecognized controller --- src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt index fc6a8b5cb..47bde5081 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt @@ -68,7 +68,7 @@ object InputHandler { private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int { var deviceIndex = index if (deviceId != -1) { - deviceIndex = controllerIds[deviceId]!! + deviceIndex = controllerIds[deviceId] ?: 0 } // TODO: Joycons are handled as different controllers. Find a way to merge them. -- cgit v1.2.3 From 92418e909f7bb3d2c199b9392304f4bd1dea65bc Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Wed, 1 Nov 2023 00:45:13 -0400 Subject: android: Use yuzu logging system Now anything that's logged in the frontend will be printed into the log file --- .../main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | 4 +-- .../src/main/java/org/yuzu/yuzu_emu/utils/Log.kt | 34 ++++------------------ src/android/app/src/main/jni/CMakeLists.txt | 1 + src/android/app/src/main/jni/native_log.cpp | 31 ++++++++++++++++++++ 4 files changed, 39 insertions(+), 31 deletions(-) create mode 100644 src/android/app/src/main/jni/native_log.cpp (limited to 'src') 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 07f1b4842..ed8fe6c3f 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 @@ -462,12 +462,12 @@ object NativeLibrary { } fun setEmulationActivity(emulationActivity: EmulationActivity?) { - Log.verbose("[NativeLibrary] Registering EmulationActivity.") + Log.debug("[NativeLibrary] Registering EmulationActivity.") sEmulationActivity = WeakReference(emulationActivity) } fun clearEmulationActivity() { - Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") + Log.debug("[NativeLibrary] Unregistering EmulationActivity.") sEmulationActivity.clear() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt index a193e82a4..1d3c7dce3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt @@ -3,38 +3,14 @@ package org.yuzu.yuzu_emu.utils -import android.util.Log -import org.yuzu.yuzu_emu.BuildConfig - -/** - * Contains methods that call through to [android.util.Log], but - * with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log - * levels in release builds. - */ object Log { - private const val TAG = "Yuzu Frontend" - - fun verbose(message: String) { - if (BuildConfig.DEBUG) { - Log.v(TAG, message) - } - } + external fun debug(message: String) - fun debug(message: String) { - if (BuildConfig.DEBUG) { - Log.d(TAG, message) - } - } + external fun warning(message: String) - fun info(message: String) { - Log.i(TAG, message) - } + external fun info(message: String) - fun warning(message: String) { - Log.w(TAG, message) - } + external fun error(message: String) - fun error(message: String) { - Log.e(TAG, message) - } + external fun critical(message: String) } diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 1c36661f5..88a570f68 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(yuzu-android SHARED native_config.cpp uisettings.cpp game_metadata.cpp + native_log.cpp ) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) diff --git a/src/android/app/src/main/jni/native_log.cpp b/src/android/app/src/main/jni/native_log.cpp new file mode 100644 index 000000000..33d691dc8 --- /dev/null +++ b/src/android/app/src/main/jni/native_log.cpp @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "android_common/android_common.h" + +extern "C" { + +void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_WARNING(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_INFO(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_ERROR(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage)); +} + +} // extern "C" -- cgit v1.2.3 From 398e8814288cbf71e2620ff22648d3002f7908b6 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Wed, 1 Nov 2023 01:26:10 -0400 Subject: android: Adjust log lifecycle Now logging will start when the frontend starts like qt does. This also adjusts the share log button to follow where we share the current log if we just returned from a game or return the old log if we haven't started a game yet. --- .../yuzu/yuzu_emu/activities/EmulationActivity.kt | 2 ++ .../yuzu_emu/fragments/HomeSettingsFragment.kt | 26 +++++++++++++++++----- .../src/main/java/org/yuzu/yuzu_emu/utils/Log.kt | 3 +++ src/android/app/src/main/jni/native.cpp | 9 ++++---- 4 files changed, 30 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index f37875ffe..da98d4ef5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -47,6 +47,7 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.utils.ForegroundService import org.yuzu.yuzu_emu.utils.InputHandler +import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.MemoryUtil import org.yuzu.yuzu_emu.utils.NfcReader import org.yuzu.yuzu_emu.utils.ThemeHelper @@ -80,6 +81,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { } override fun onCreate(savedInstanceState: Bundle?) { + Log.gameLaunched = true ThemeHelper.setTheme(this) super.onCreate(savedInstanceState) 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 6e19fc6c0..8ed4b482e 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 @@ -42,6 +42,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.GpuDriverHelper +import org.yuzu.yuzu_emu.utils.Log class HomeSettingsFragment : Fragment() { private var _binding: FragmentHomeSettingsBinding? = null @@ -312,19 +313,32 @@ class HomeSettingsFragment : Fragment() { } } + // Share the current log if we just returned from a game but share the old log + // if we just started the app and the old log exists. private fun shareLog() { - val file = DocumentFile.fromSingleUri( + val currentLog = DocumentFile.fromSingleUri( mainActivity, DocumentsContract.buildDocumentUri( DocumentProvider.AUTHORITY, "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt" ) )!! - if (file.exists()) { - val intent = Intent(Intent.ACTION_SEND) - .setDataAndType(file.uri, FileUtil.TEXT_PLAIN) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .putExtra(Intent.EXTRA_STREAM, file.uri) + val oldLog = DocumentFile.fromSingleUri( + mainActivity, + DocumentsContract.buildDocumentUri( + DocumentProvider.AUTHORITY, + "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt.old.txt" + ) + )!! + + val intent = Intent(Intent.ACTION_SEND) + .setDataAndType(currentLog.uri, FileUtil.TEXT_PLAIN) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + if (!Log.gameLaunched && oldLog.exists()) { + intent.putExtra(Intent.EXTRA_STREAM, oldLog.uri) + startActivity(Intent.createChooser(intent, getText(R.string.share_log))) + } else if (currentLog.exists()) { + intent.putExtra(Intent.EXTRA_STREAM, currentLog.uri) startActivity(Intent.createChooser(intent, getText(R.string.share_log))) } else { Toast.makeText( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt index 1d3c7dce3..fb682c344 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt @@ -4,6 +4,9 @@ package org.yuzu.yuzu_emu.utils object Log { + // Tracks whether we should share the old log or the current log + var gameLaunched = false + external fun debug(message: String) external fun warning(message: String) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 0e458df38..294e41045 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -248,6 +248,11 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) } void EmulationSession::InitializeSystem() { + // Initialize logging system + Common::Log::Initialize(); + Common::Log::SetColorConsoleBackendEnabled(true); + Common::Log::Start(); + // Initialize filesystem. m_system.SetFilesystem(m_vfs); m_system.GetUserChannel().clear(); @@ -462,10 +467,6 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { } static Core::SystemResultStatus RunEmulation(const std::string& filepath) { - Common::Log::Initialize(); - Common::Log::SetColorConsoleBackendEnabled(true); - Common::Log::Start(); - MicroProfileOnThreadCreate("EmuThread"); SCOPE_EXIT({ MicroProfileShutdown(); }); -- cgit v1.2.3 From 41701052d3ebbd2ed746beef342e1bdeaa9374e6 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 1 Nov 2023 20:47:08 -0400 Subject: renderer_vulkan: minimize transform feedback support log --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 059b7cb40..3983b2eb7 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -923,9 +923,13 @@ void RasterizerVulkan::UpdateDynamicStates() { } void RasterizerVulkan::HandleTransformFeedback() { + static std::once_flag warn_unsupported; + const auto& regs = maxwell3d->regs; if (!device.IsExtTransformFeedbackSupported()) { - LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); + std::call_once(warn_unsupported, [&] { + LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); + }); return; } query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, -- cgit v1.2.3 From 57cf830862bac1d68d660c120014e7d5021b72e8 Mon Sep 17 00:00:00 2001 From: german77 Date: Thu, 2 Nov 2023 17:42:47 -0600 Subject: core: hid: Fix wrong battery values --- src/core/hid/emulated_controller.cpp | 14 +++++++------- src/core/hid/hid_types.h | 15 ++++++++++----- src/core/hle/service/hid/controllers/npad.cpp | 6 +++--- 3 files changed, 20 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 2af3f06fc..8e2894449 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -1091,30 +1091,30 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac bool is_charging = false; bool is_powered = false; - NpadBatteryLevel battery_level = 0; + NpadBatteryLevel battery_level = NpadBatteryLevel::Empty; switch (controller.battery_values[index]) { case Common::Input::BatteryLevel::Charging: is_charging = true; is_powered = true; - battery_level = 6; + battery_level = NpadBatteryLevel::Full; break; case Common::Input::BatteryLevel::Medium: - battery_level = 6; + battery_level = NpadBatteryLevel::High; break; case Common::Input::BatteryLevel::Low: - battery_level = 4; + battery_level = NpadBatteryLevel::Low; break; case Common::Input::BatteryLevel::Critical: - battery_level = 2; + battery_level = NpadBatteryLevel::Critical; break; case Common::Input::BatteryLevel::Empty: - battery_level = 0; + battery_level = NpadBatteryLevel::Empty; break; case Common::Input::BatteryLevel::None: case Common::Input::BatteryLevel::Full: default: is_powered = true; - battery_level = 8; + battery_level = NpadBatteryLevel::Full; break; } diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 00beb40dd..7ba75a50c 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h @@ -302,6 +302,15 @@ enum class TouchScreenModeForNx : u8 { Heat2, }; +// This is nn::hid::system::NpadBatteryLevel +enum class NpadBatteryLevel : u32 { + Empty, + Critical, + Low, + High, + Full, +}; + // This is nn::hid::NpadStyleTag struct NpadStyleTag { union { @@ -385,16 +394,12 @@ struct NpadGcTriggerState { }; static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); -// This is nn::hid::system::NpadBatteryLevel -using NpadBatteryLevel = u32; -static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size"); - // This is nn::hid::system::NpadPowerInfo struct NpadPowerInfo { bool is_powered{}; bool is_charging{}; INSERT_PADDING_BYTES(0x6); - NpadBatteryLevel battery_level{8}; + NpadBatteryLevel battery_level{NpadBatteryLevel::Full}; }; static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index bc822f19e..21695bda2 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -1108,9 +1108,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { shared_memory->sixaxis_dual_right_properties.raw = 0; shared_memory->sixaxis_left_properties.raw = 0; shared_memory->sixaxis_right_properties.raw = 0; - shared_memory->battery_level_dual = 0; - shared_memory->battery_level_left = 0; - shared_memory->battery_level_right = 0; + shared_memory->battery_level_dual = Core::HID::NpadBatteryLevel::Empty; + shared_memory->battery_level_left = Core::HID::NpadBatteryLevel::Empty; + shared_memory->battery_level_right = Core::HID::NpadBatteryLevel::Empty; shared_memory->fullkey_color = { .attribute = ColorAttribute::NoController, .fullkey = {}, -- cgit v1.2.3 From b36fec486e063fc16c50346179bd8e1cb26ee177 Mon Sep 17 00:00:00 2001 From: german77 Date: Thu, 2 Nov 2023 19:14:05 -0600 Subject: service: hid: Ensure GetNextEntryIndex can't fail --- src/core/hle/service/hid/ring_lifo.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h index 65eb7ea02..0816784e0 100644 --- a/src/core/hle/service/hid/ring_lifo.h +++ b/src/core/hle/service/hid/ring_lifo.h @@ -32,15 +32,15 @@ struct Lifo { } std::size_t GetPreviousEntryIndex() const { - return static_cast((buffer_tail + total_buffer_count - 1) % total_buffer_count); + return static_cast((buffer_tail + max_buffer_size - 1) % max_buffer_size); } std::size_t GetNextEntryIndex() const { - return static_cast((buffer_tail + 1) % total_buffer_count); + return static_cast((buffer_tail + 1) % max_buffer_size); } void WriteNextEntry(const State& new_state) { - if (buffer_count < total_buffer_count - 1) { + if (buffer_count < static_cast(max_buffer_size) - 1) { buffer_count++; } buffer_tail = GetNextEntryIndex(); -- cgit v1.2.3 From a294beb116026ba15ed90299308ea47e4775c1e5 Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Fri, 3 Nov 2023 11:16:38 -0400 Subject: Allow 0 stereo count --- src/audio_core/adsp/apps/opus/opus_decoder.cpp | 4 ++-- src/audio_core/opus/decoder_manager.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.cpp b/src/audio_core/adsp/apps/opus/opus_decoder.cpp index 2084de128..75f0fb9ad 100644 --- a/src/audio_core/adsp/apps/opus/opus_decoder.cpp +++ b/src/audio_core/adsp/apps/opus/opus_decoder.cpp @@ -30,9 +30,9 @@ bool IsValidMultiStreamChannelCount(u32 channel_count) { return channel_count <= OpusStreamCountMax; } -bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) { +bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_count) { return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && - sterero_stream_count > 0 && sterero_stream_count <= total_stream_count; + stereo_stream_count >= 0 && stereo_stream_count <= total_stream_count; } } // namespace diff --git a/src/audio_core/opus/decoder_manager.cpp b/src/audio_core/opus/decoder_manager.cpp index 4a5382973..fdeccdf50 100644 --- a/src/audio_core/opus/decoder_manager.cpp +++ b/src/audio_core/opus/decoder_manager.cpp @@ -24,7 +24,7 @@ bool IsValidSampleRate(u32 sample_rate) { } bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { - return total_stream_count > 0 && stereo_stream_count > 0 && + return total_stream_count > 0 && static_cast(stereo_stream_count) >= 0 && stereo_stream_count <= total_stream_count && total_stream_count + stereo_stream_count <= channel_count; } -- cgit v1.2.3 From b3a1f793c3792dce9fb38c764d0ee9ee38d66783 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Fri, 3 Nov 2023 13:31:06 -0400 Subject: android: Update surface parameters on emulation start This adds a quick update that notifies the render surface if there was a change between surface creation and emulation starting. --- .../main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 07bd78bf7..c456c0592 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -312,6 +312,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { ViewUtils.showView(binding.surfaceInputOverlay) ViewUtils.hideView(binding.loadingIndicator) + emulationState.updateSurface() + // Setup overlay updateShowFpsOverlay() } @@ -804,6 +806,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } + @Synchronized + fun updateSurface() { + if (surface != null) { + NativeLibrary.surfaceChanged(surface) + } + } + @Synchronized fun clearSurface() { if (surface == null) { -- cgit v1.2.3 From 9bb8ac7cb6ac87fe5c0b82cd563ba62483761b4e Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Fri, 3 Nov 2023 15:51:17 -0400 Subject: android: Fix fetching system memory size from MemoryUtil We weren't rounding up the value at a unit before (GB, MB, etc) we were rounding up the total bytes and that would do nothing. This fixes that, and the check for total system memory during first emulation start where we tried to check the required system memory against 1 gigabyte. --- .../yuzu/yuzu_emu/activities/EmulationActivity.kt | 2 +- .../java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt | 34 ++++++++++------------ 2 files changed, 17 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index da98d4ef5..054e4b755 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -107,7 +107,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { - if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) { + if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) { Toast.makeText( this, getString( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt index aa4a5539a..9076a86c4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt @@ -27,7 +27,7 @@ object MemoryUtil { const val Pb = Tb * 1024 const val Eb = Pb * 1024 - private fun bytesToSizeUnit(size: Float): String = + private fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String = when { size < Kb -> { context.getString( @@ -39,63 +39,59 @@ object MemoryUtil { size < Mb -> { context.getString( R.string.memory_formatted, - (size / Kb).hundredths, + if (roundUp) ceil(size / Kb) else (size / Kb).hundredths, context.getString(R.string.memory_kilobyte) ) } size < Gb -> { context.getString( R.string.memory_formatted, - (size / Mb).hundredths, + if (roundUp) ceil(size / Mb) else (size / Mb).hundredths, context.getString(R.string.memory_megabyte) ) } size < Tb -> { context.getString( R.string.memory_formatted, - (size / Gb).hundredths, + if (roundUp) ceil(size / Gb) else (size / Gb).hundredths, context.getString(R.string.memory_gigabyte) ) } size < Pb -> { context.getString( R.string.memory_formatted, - (size / Tb).hundredths, + if (roundUp) ceil(size / Tb) else (size / Tb).hundredths, context.getString(R.string.memory_terabyte) ) } size < Eb -> { context.getString( R.string.memory_formatted, - (size / Pb).hundredths, + if (roundUp) ceil(size / Pb) else (size / Pb).hundredths, context.getString(R.string.memory_petabyte) ) } else -> { context.getString( R.string.memory_formatted, - (size / Eb).hundredths, + if (roundUp) ceil(size / Eb) else (size / Eb).hundredths, context.getString(R.string.memory_exabyte) ) } } - // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for - // the potential error created by memInfo.totalMem - private val totalMemory: Float + val totalMemory: Float get() { val memInfo = ActivityManager.MemoryInfo() with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { getMemoryInfo(memInfo) } - return ceil( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - memInfo.advertisedMem.toFloat() - } else { - memInfo.totalMem.toFloat() - } - ) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + memInfo.advertisedMem.toFloat() + } else { + memInfo.totalMem.toFloat() + } } fun isLessThan(minimum: Int, size: Float): Boolean = @@ -109,5 +105,7 @@ object MemoryUtil { else -> totalMemory < Kb && totalMemory < minimum } - fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory) + // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for + // the potential error created by memInfo.totalMem + fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true) } -- cgit v1.2.3 From 0a83047368b2e0e72535c0239db806d6c229d7e9 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Fri, 3 Nov 2023 15:52:01 -0400 Subject: android: Log more system information during startup Logs device manufacturer/model, SoC manufacturer/model where available, and the total system memory --- .../app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt | 2 ++ src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt | 12 ++++++++++++ 2 files changed, 14 insertions(+) (limited to 'src') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index 8c053670c..d114bd53d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt @@ -11,6 +11,7 @@ import java.io.File import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.GpuDriverHelper +import org.yuzu.yuzu_emu.utils.Log fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir @@ -49,6 +50,7 @@ class YuzuApplication : Application() { DirectoryInitialization.start() GpuDriverHelper.initializeDriverParameters() NativeLibrary.logDeviceInfo() + Log.logDeviceInfo() createNotificationChannels() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt index fb682c344..aebe84b0f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt @@ -3,6 +3,8 @@ package org.yuzu.yuzu_emu.utils +import android.os.Build + object Log { // Tracks whether we should share the old log or the current log var gameLaunched = false @@ -16,4 +18,14 @@ object Log { external fun error(message: String) external fun critical(message: String) + + fun logDeviceInfo() { + info("Device Manufacturer - ${Build.MANUFACTURER}") + info("Device Model - ${Build.MODEL}") + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { + info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}") + info("SoC Model - ${Build.SOC_MODEL}") + } + info("Total System Memory - ${MemoryUtil.getDeviceRAM()}") + } } -- cgit v1.2.3 From 4b321c003caed15a23d6f221da871980ceed72c2 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Fri, 3 Nov 2023 16:21:54 -0400 Subject: arm: NativeClock: Special handling for bad system counter clock frequency reporting On some devices, checking the system counter clock frequency will return 0. Substitute in the correct values to prevent issues. --- src/common/arm64/native_clock.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/common/arm64/native_clock.cpp b/src/common/arm64/native_clock.cpp index 88fdba527..f437d7187 100644 --- a/src/common/arm64/native_clock.cpp +++ b/src/common/arm64/native_clock.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#ifdef ANDROID +#include +#endif #include "common/arm64/native_clock.h" namespace Common::Arm64 { @@ -65,7 +68,23 @@ bool NativeClock::IsNative() const { u64 NativeClock::GetHostCNTFRQ() { u64 cntfrq_el0 = 0; - asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); + std::string_view board{""}; +#ifdef ANDROID + char buffer[PROP_VALUE_MAX]; + int len{__system_property_get("ro.product.board", buffer)}; + board = std::string_view(buffer, static_cast(len)); +#endif + if (board == "s5e9925") { // Exynos 2200 + cntfrq_el0 = 25600000; + } else if (board == "exynos2100") { // Exynos 2100 + cntfrq_el0 = 26000000; + } else if (board == "exynos9810") { // Exynos 9810 + cntfrq_el0 = 26000000; + } else if (board == "s5e8825") { // Exynos 1280 + cntfrq_el0 = 26000000; + } else { + asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); + } return cntfrq_el0; } -- cgit v1.2.3 From 036d2686af47c9c52e121c96266cfa47fb865742 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Fri, 3 Nov 2023 22:49:31 -0400 Subject: android: Don't reload log/system after loading firmware/backup --- .../main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | 2 +- .../java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 4 ++-- .../yuzu/yuzu_emu/utils/DirectoryInitialization.kt | 2 +- src/android/app/src/main/jni/native.cpp | 21 +++++++++++++-------- src/android/app/src/main/jni/native.h | 2 +- 5 files changed, 18 insertions(+), 13 deletions(-) (limited to 'src') 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 ed8fe6c3f..9ebd6c732 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -252,7 +252,7 @@ object NativeLibrary { external fun reloadKeys(): Boolean - external fun initializeSystem() + external fun initializeSystem(reload: Boolean) external fun defaultCPUCore(): Int 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 ba1177426..211b7cf69 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -403,7 +403,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } else { firmwarePath.deleteRecursively() cacheFirmwareDir.copyRecursively(firmwarePath, true) - NativeLibrary.initializeSystem() + NativeLibrary.initializeSystem(true) getString(R.string.save_file_imported_success) } } catch (e: Exception) { @@ -649,7 +649,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } // Reinitialize relevant data - NativeLibrary.initializeSystem() + NativeLibrary.initializeSystem(true) gamesViewModel.reloadGames(false) return@newInstance getString(R.string.user_data_import_success) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt index 79a07f7ef..5e9a1176a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt @@ -15,7 +15,7 @@ object DirectoryInitialization { fun start() { if (!areDirectoriesReady) { initializeInternalStorage() - NativeLibrary.initializeSystem() + NativeLibrary.initializeSystem(false) areDirectoriesReady = true } } diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 294e41045..46438906e 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -247,11 +247,13 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) } } -void EmulationSession::InitializeSystem() { - // Initialize logging system - Common::Log::Initialize(); - Common::Log::SetColorConsoleBackendEnabled(true); - Common::Log::Start(); +void EmulationSession::InitializeSystem(bool reload) { + if (!reload) { + // Initialize logging system + Common::Log::Initialize(); + Common::Log::SetColorConsoleBackendEnabled(true); + Common::Log::Start(); + } // Initialize filesystem. m_system.SetFilesystem(m_vfs); @@ -667,12 +669,15 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c } } -void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz) { +void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, + jboolean reload) { // Create the default config.ini. Config{}; // Initialize the emulated system. - EmulationSession::GetInstance().System().Initialize(); - EmulationSession::GetInstance().InitializeSystem(); + if (!reload) { + EmulationSession::GetInstance().System().Initialize(); + } + EmulationSession::GetInstance().InitializeSystem(reload); } jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) { diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index 0aa2b085b..3b9596459 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h @@ -43,7 +43,7 @@ public: const Core::PerfStatsResults& PerfStats() const; void ConfigureFilesystemProvider(const std::string& filepath); - void InitializeSystem(); + void InitializeSystem(bool reload); Core::SystemResultStatus InitializeEmulation(const std::string& filepath); bool IsHandheldOnly(); -- cgit v1.2.3 From bf8d7bc0da2a2e49f883c9360908ec8901d7e01c Mon Sep 17 00:00:00 2001 From: german77 Date: Fri, 3 Nov 2023 23:22:28 -0600 Subject: service: hid: Silence EnableUnintendedHomeButtonInputProtection --- src/core/hle/service/hid/hid.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 929dd5f03..1d4101be9 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1353,7 +1353,7 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) { void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - bool unintended_home_button_input_protection; + bool is_enabled; INSERT_PADDING_BYTES_NOINIT(3); Core::HID::NpadIdType npad_id; u64 applet_resource_user_id; @@ -1364,13 +1364,11 @@ void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { auto& controller = GetAppletResource()->GetController(HidController::NPad); const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( - parameters.unintended_home_button_input_protection, parameters.npad_id); + parameters.is_enabled, parameters.npad_id); - LOG_WARNING(Service_HID, - "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," - "applet_resource_user_id={}", - parameters.unintended_home_button_input_protection, parameters.npad_id, - parameters.applet_resource_user_id); + LOG_DEBUG(Service_HID, + "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}", + parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); -- cgit v1.2.3