From 877b31b33e63c8a9df2649daedfb26a24d2e4515 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 22 Nov 2018 20:51:53 -0500 Subject: [PATCH 01/74] software_keyboard: Signal state changed event upon construction Previously, ILibraryAppletAccessor would signal upon creation of any applet, but this is incorrect. A flag inside of the applet code determines whether or not creation should signal state change and swkbd happens to be one of these applets. --- src/core/hle/service/am/applets/software_keyboard.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 981bdec51f..40984ffa96 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -38,7 +38,12 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( return params; } -SoftwareKeyboard::SoftwareKeyboard() = default; +SoftwareKeyboard::SoftwareKeyboard() { + // Some applets require this to be signalled on applet creation, some do not. Internally, this + // is done by a flag in the applet module, but for simplicity SoftwareKeyboard is one of the + // applets with this flag. + broker.SignalStateChanged(); +} SoftwareKeyboard::~SoftwareKeyboard() = default; From d17f38494b8d64c16ac718c660a57cd89ab48b6f Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 22 Nov 2018 20:58:51 -0500 Subject: [PATCH 02/74] frontend: Add frontend applet for ProfileSelect Responsible for selecting a profile and firing callback upon completion. --- src/core/CMakeLists.txt | 2 ++ src/core/frontend/applets/profile_select.cpp | 19 ++++++++++++++ src/core/frontend/applets/profile_select.h | 27 ++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 src/core/frontend/applets/profile_select.cpp create mode 100644 src/core/frontend/applets/profile_select.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 73aec8ab0b..4b51943ab5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -79,6 +79,8 @@ add_library(core STATIC file_sys/vfs_vector.h file_sys/xts_archive.cpp file_sys/xts_archive.h + frontend/applets/profile_select.cpp + frontend/applets/profile_select.h frontend/applets/software_keyboard.cpp frontend/applets/software_keyboard.h frontend/emu_window.cpp diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp new file mode 100644 index 0000000000..fbf5f2a9e5 --- /dev/null +++ b/src/core/frontend/applets/profile_select.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/frontend/applets/profile_select.h" +#include "core/settings.h" + +namespace Core::Frontend { + +ProfileSelectApplet::~ProfileSelectApplet() = default; + +void DefaultProfileSelectApplet::SelectProfile( + std::function)> callback) const { + Service::Account::ProfileManager manager; + callback(manager.GetUser(Settings::values.current_user).value_or(Service::Account::UUID{})); + LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); +} + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h new file mode 100644 index 0000000000..fc8f7ae947 --- /dev/null +++ b/src/core/frontend/applets/profile_select.h @@ -0,0 +1,27 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "core/hle/service/acc/profile_manager.h" + +namespace Core::Frontend { + +class ProfileSelectApplet { +public: + virtual ~ProfileSelectApplet(); + + virtual void SelectProfile( + std::function)> callback) const = 0; +}; + +class DefaultProfileSelectApplet final : public ProfileSelectApplet { +public: + void SelectProfile( + std::function)> callback) const override; +}; + +} // namespace Core::Frontend From 58fd0a1c50912f28457eae974dfe928463ceb0c2 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 22 Nov 2018 21:00:04 -0500 Subject: [PATCH 03/74] core: Add getter/setter for ProfileSelector in System --- src/core/core.cpp | 11 +++++++++++ src/core/core.h | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/src/core/core.cpp b/src/core/core.cpp index 795fabc652..19b4b97c53 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -97,6 +97,8 @@ struct System::Impl { virtual_filesystem = std::make_shared(); /// Create default implementations of applets if one is not provided. + if (profile_selector == nullptr) + profile_selector = std::make_unique(); if (software_keyboard == nullptr) software_keyboard = std::make_unique(); @@ -227,6 +229,7 @@ struct System::Impl { bool is_powered_on = false; /// Frontend applets + std::unique_ptr profile_selector; std::unique_ptr software_keyboard; /// Service manager @@ -422,6 +425,14 @@ std::shared_ptr System::GetFilesystem() const { return impl->virtual_filesystem; } +void System::SetProfileSelector(std::unique_ptr applet) { + impl->profile_selector = std::move(applet); +} + +const Core::Frontend::ProfileSelectApplet& System::GetProfileSelector() const { + return *impl->profile_selector; +} + void System::SetSoftwareKeyboard(std::unique_ptr applet) { impl->software_keyboard = std::move(applet); } diff --git a/src/core/core.h b/src/core/core.h index be71bd4378..21dea051ba 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -10,6 +10,7 @@ #include "common/common_types.h" #include "core/hle/kernel/object.h" +#include "frontend/applets/profile_select.h" namespace Core::Frontend { class EmuWindow; @@ -237,6 +238,10 @@ public: std::shared_ptr GetFilesystem() const; + void SetProfileSelector(std::unique_ptr applet); + + const Core::Frontend::ProfileSelectApplet& GetProfileSelector() const; + void SetSoftwareKeyboard(std::unique_ptr applet); const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const; From 6deccc7e6b7a242c94e4e54cc805c5ee390a5284 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 22 Nov 2018 21:00:42 -0500 Subject: [PATCH 04/74] qt: Register to use Qt ProfileSelector instead of default --- src/yuzu/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 22c207a3af..b39bf1903e 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -8,6 +8,7 @@ #include // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. +#include "applets/profile_select.h" #include "applets/software_keyboard.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" @@ -571,6 +572,7 @@ bool GMainWindow::LoadROM(const QString& filename) { system.SetGPUDebugContext(debug_context); + system.SetProfileSelector(std::make_unique(*this)); system.SetSoftwareKeyboard(std::make_unique(*this)); const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; From 4fb59fdfe1e5fe2d0b9313c395e0d7c880e00c6f Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 22 Nov 2018 21:02:36 -0500 Subject: [PATCH 05/74] applets: Implement ProfileSelect applet Allows the player to select an emulated profile. --- .../hle/service/am/applets/profile_select.cpp | 76 +++++++++++++++++++ .../hle/service/am/applets/profile_select.h | 54 +++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 src/core/hle/service/am/applets/profile_select.cpp create mode 100644 src/core/hle/service/am/applets/profile_select.h diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp new file mode 100644 index 0000000000..750f0fdebe --- /dev/null +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -0,0 +1,76 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "common/assert.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/profile_select.h" + +namespace Service::AM::Applets { + +constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; + +ProfileSelect::ProfileSelect() = default; +ProfileSelect::~ProfileSelect() = default; + +void ProfileSelect::Initialize() { + complete = false; + status = RESULT_SUCCESS; + final_data.clear(); + + Applet::Initialize(); + + const auto user_config_storage = broker.PopNormalDataToApplet(); + ASSERT(user_config_storage != nullptr); + const auto& user_config = user_config_storage->GetData(); + + ASSERT(user_config.size() >= sizeof(UserSelectionConfig)); + std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig)); +} + +bool ProfileSelect::TransactionComplete() const { + return complete; +} + +ResultCode ProfileSelect::GetStatus() const { + return status; +} + +void ProfileSelect::ExecuteInteractive() { + UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); +} + +void ProfileSelect::Execute() { + if (complete) { + broker.PushNormalDataFromApplet(IStorage{final_data}); + return; + } + + const auto& frontend{Core::System::GetInstance().GetProfileSelector()}; + + frontend.SelectProfile([this](std::optional uuid) { SelectionComplete(uuid); }); +} + +void ProfileSelect::SelectionComplete(std::optional uuid) { + UserSelectionOutput output{}; + + if (uuid.has_value() && uuid->uuid != Account::INVALID_UUID) { + output.result = 0; + output.uuid_selected = uuid->uuid; + } else { + status = ERR_USER_CANCELLED_SELECTION; + output.result = ERR_USER_CANCELLED_SELECTION.raw; + output.uuid_selected = Account::INVALID_UUID; + } + + final_data = std::vector(sizeof(UserSelectionOutput)); + std::memcpy(final_data.data(), &output, final_data.size()); + broker.PushNormalDataFromApplet(IStorage{final_data}); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h new file mode 100644 index 0000000000..c383527f4d --- /dev/null +++ b/src/core/hle/service/am/applets/profile_select.h @@ -0,0 +1,54 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "common/common_funcs.h" +#include "common/swap.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Service::AM::Applets { + +struct UserSelectionConfig { + // TODO(DarkLordZach): RE this structure + // It seems to be flags and the like that determine the UI of the applet on the switch... from + // my research this is safe to ignore for now. + INSERT_PADDING_BYTES(0xA0); +}; +static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size."); + +struct UserSelectionOutput { + u64 result; + u128 uuid_selected; +}; +static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size."); + +class ProfileSelect final : public Applet { +public: + ProfileSelect(); + ~ProfileSelect() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void SelectionComplete(std::optional uuid); + +private: + UserSelectionConfig config; + bool complete = false; + ResultCode status = RESULT_SUCCESS; + std::vector final_data; +}; + +} // namespace Service::AM::Applets From 60b59d554d20c4f02036f254604835c62a7f282f Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 22 Nov 2018 21:02:57 -0500 Subject: [PATCH 06/74] am: Use ProfileSelect applet --- src/core/hle/service/am/am.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3a7b6da846..1a15d85cb0 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -19,6 +19,7 @@ #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/am/applets/profile_select.h" #include "core/hle/service/am/applets/software_keyboard.h" #include "core/hle/service/am/applets/stub_applet.h" #include "core/hle/service/am/idle.h" @@ -39,6 +40,7 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2}; constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; enum class AppletId : u32 { + ProfileSelect = 0x10, SoftwareKeyboard = 0x11, }; @@ -773,6 +775,8 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default; static std::shared_ptr GetAppletFromId(AppletId id) { switch (id) { + case AppletId::ProfileSelect: + return std::make_shared(); case AppletId::SoftwareKeyboard: return std::make_shared(); default: From bf90f2402dae06ebf4292e59bf8703490596f6ba Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 22 Nov 2018 21:03:33 -0500 Subject: [PATCH 07/74] qt: Implement GUI dialog frontend for ProfileSelector Presents profiles in a list, similar to switch. --- src/core/CMakeLists.txt | 2 + src/yuzu/CMakeLists.txt | 2 + src/yuzu/applets/profile_select.cpp | 168 ++++++++++++++++++++++++++++ src/yuzu/applets/profile_select.h | 73 ++++++++++++ src/yuzu/main.cpp | 22 ++++ src/yuzu/main.h | 2 + 6 files changed, 269 insertions(+) create mode 100644 src/yuzu/applets/profile_select.cpp create mode 100644 src/yuzu/applets/profile_select.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4b51943ab5..4f0ea5d679 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -160,6 +160,8 @@ add_library(core STATIC hle/service/am/applet_oe.h hle/service/am/applets/applets.cpp hle/service/am/applets/applets.h + hle/service/am/applets/profile_select.cpp + hle/service/am/applets/profile_select.h hle/service/am/applets/software_keyboard.cpp hle/service/am/applets/software_keyboard.h hle/service/am/applets/stub_applet.cpp diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cfca8f4a8d..61562b9b25 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -7,6 +7,8 @@ add_executable(yuzu Info.plist about_dialog.cpp about_dialog.h + applets/profile_select.cpp + applets/profile_select.h applets/software_keyboard.cpp applets/software_keyboard.h bootmanager.cpp diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp new file mode 100644 index 0000000000..5c1b65a2cd --- /dev/null +++ b/src/yuzu/applets/profile_select.cpp @@ -0,0 +1,168 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include +#include "common/file_util.h" +#include "common/string_util.h" +#include "core/hle/lock.h" +#include "yuzu/applets/profile_select.h" +#include "yuzu/main.h" + +// Same backup JPEG used by acc IProfile::GetImage if no jpeg found +constexpr std::array backup_jpeg{ + 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, + 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, + 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, + 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, + 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, + 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, +}; + +QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) { + return QtProfileSelectionDialog::tr( + "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " + "00112233-4455-6677-8899-AABBCCDDEEFF))") + .arg(username, QString::fromStdString(uuid.FormatSwitch())); +} + +QString GetImagePath(Service::Account::UUID uuid) { + const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; + return QString::fromStdString(path); +} + +QPixmap GetIcon(Service::Account::UUID uuid) { + QPixmap icon{GetImagePath(uuid)}; + + if (!icon) { + icon.fill(Qt::black); + icon.loadFromData(backup_jpeg.data(), static_cast(backup_jpeg.size())); + } + + return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); +} + +QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) + : QDialog(parent), profile_manager(std::make_unique()) { + outer_layout = new QVBoxLayout; + + instruction_label = new QLabel(tr("Select a user:")); + + scroll_area = new QScrollArea; + + buttons = new QDialogButtonBox; + buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); + buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole); + + connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject); + + outer_layout->addWidget(instruction_label); + outer_layout->addWidget(scroll_area); + outer_layout->addWidget(buttons); + + layout = new QVBoxLayout; + tree_view = new QTreeView; + item_model = new QStandardItemModel(tree_view); + tree_view->setModel(item_model); + + tree_view->setAlternatingRowColors(true); + tree_view->setSelectionMode(QHeaderView::SingleSelection); + tree_view->setSelectionBehavior(QHeaderView::SelectRows); + tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); + tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); + tree_view->setSortingEnabled(true); + tree_view->setEditTriggers(QHeaderView::NoEditTriggers); + tree_view->setUniformRowHeights(true); + tree_view->setIconSize({64, 64}); + tree_view->setContextMenuPolicy(Qt::NoContextMenu); + + item_model->insertColumns(0, 1); + item_model->setHeaderData(0, Qt::Horizontal, "Users"); + + // We must register all custom types with the Qt Automoc system so that we are able to use it + // with signals/slots. In this case, QList falls under the umbrells of custom types. + qRegisterMetaType>("QList"); + + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addWidget(tree_view); + + scroll_area->setLayout(layout); + + connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser); + + const auto& profiles = profile_manager->GetAllUsers(); + for (const auto& user : profiles) { + Service::Account::ProfileBase profile; + if (!profile_manager->GetProfileBase(user, profile)) + continue; + + const auto username = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast(profile.username.data()), profile.username.size()); + + list_items.push_back(QList{new QStandardItem{ + GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}}); + } + + for (const auto& item : list_items) + item_model->appendRow(item); + + setLayout(outer_layout); + setWindowTitle(tr("Profile Selector")); + resize(550, 400); +} + +QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; + +void QtProfileSelectionDialog::accept() { + ok = true; + QDialog::accept(); +} + +void QtProfileSelectionDialog::reject() { + ok = false; + user_index = 0; + QDialog::reject(); +} + +bool QtProfileSelectionDialog::GetStatus() const { + return ok; +} + +u32 QtProfileSelectionDialog::GetIndex() const { + return user_index; +} + +void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) { + user_index = index.row(); +} + +QtProfileSelector::QtProfileSelector(GMainWindow& parent) { + connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent, + &GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection); + connect(&parent, &GMainWindow::ProfileSelectorFinishedSelection, this, + &QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection); +} + +QtProfileSelector::~QtProfileSelector() = default; + +void QtProfileSelector::SelectProfile( + std::function)> callback) const { + this->callback = std::move(callback); + emit MainWindowSelectProfile(); +} + +void QtProfileSelector::MainWindowFinishedSelection(std::optional uuid) { + // Acquire the HLE mutex + std::lock_guard lock(HLE::g_hle_lock); + callback(uuid); +} diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h new file mode 100644 index 0000000000..9acb092f3d --- /dev/null +++ b/src/yuzu/applets/profile_select.h @@ -0,0 +1,73 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include "core/frontend/applets/profile_select.h" + +class GMainWindow; +class QDialogButtonBox; +class QGraphicsScene; +class QLabel; +class QScrollArea; +class QStandardItem; +class QStandardItemModel; +class QTreeView; +class QVBoxLayout; + +class QtProfileSelectionDialog final : public QDialog { + Q_OBJECT + +public: + QtProfileSelectionDialog(QWidget* parent); + ~QtProfileSelectionDialog() override; + + void accept() override; + void reject() override; + + bool GetStatus() const; + u32 GetIndex() const; + +private: + bool ok = false; + u32 user_index = 0; + + void SelectUser(const QModelIndex& index); + + QVBoxLayout* layout; + QTreeView* tree_view; + QStandardItemModel* item_model; + QGraphicsScene* scene; + + std::vector> list_items; + + QVBoxLayout* outer_layout; + QLabel* instruction_label; + QScrollArea* scroll_area; + QDialogButtonBox* buttons; + + std::unique_ptr profile_manager; +}; + +class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet { + Q_OBJECT + +public: + explicit QtProfileSelector(GMainWindow& parent); + ~QtProfileSelector() override; + + void SelectProfile( + std::function)> callback) const override; + +signals: + void MainWindowSelectProfile() const; + +private: + void MainWindowFinishedSelection(std::optional uuid); + + mutable std::function)> callback; +}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b39bf1903e..085cab74b6 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -208,6 +208,28 @@ GMainWindow::~GMainWindow() { delete render_window; } +void GMainWindow::ProfileSelectorSelectProfile() { + QtProfileSelectionDialog dialog(this); + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | + Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); + dialog.setWindowModality(Qt::WindowModal); + dialog.exec(); + + if (!dialog.GetStatus()) { + emit ProfileSelectorFinishedSelection(std::nullopt); + return; + } + + Service::Account::ProfileManager manager; + const auto uuid = manager.GetUser(dialog.GetIndex()); + if (!uuid.has_value()) { + emit ProfileSelectorFinishedSelection(std::nullopt); + return; + } + + emit ProfileSelectorFinishedSelection(uuid); +} + void GMainWindow::SoftwareKeyboardGetText( const Core::Frontend::SoftwareKeyboardParameters& parameters) { QtSoftwareKeyboardDialog dialog(this, parameters); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 674e734125..7c5eaf16ba 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -99,10 +99,12 @@ signals: // Signal that tells widgets to update icons to use the current theme void UpdateThemedIcons(); + void ProfileSelectorFinishedSelection(std::optional uuid); void SoftwareKeyboardFinishedText(std::optional text); void SoftwareKeyboardFinishedCheckDialog(); public slots: + void ProfileSelectorSelectProfile(); void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); From e11e65b3d63fabd7c953cca07971364984021c3e Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 23 Nov 2018 11:00:33 -0500 Subject: [PATCH 08/74] applets: Correct event ResetTypes from OneShot to Sticky Fixes bugs relating to signalling in software keyboard. --- src/core/hle/service/am/applets/applets.cpp | 6 +++--- src/core/hle/service/am/applets/profile_select.cpp | 1 + src/core/hle/service/am/applets/profile_select.h | 4 ---- src/core/hle/service/am/applets/software_keyboard.cpp | 7 +------ src/yuzu/applets/profile_select.h | 2 +- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 47da355370..7698ca8195 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -16,11 +16,11 @@ namespace Service::AM::Applets { AppletDataBroker::AppletDataBroker() { auto& kernel = Core::System::GetInstance().Kernel(); state_changed_event = Kernel::WritableEvent::CreateEventPair( - kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:StateChangedEvent"); + kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent"); pop_out_data_event = Kernel::WritableEvent::CreateEventPair( - kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopDataOutEvent"); + kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent"); pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair( - kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); + kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); } AppletDataBroker::~AppletDataBroker() = default; diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp index 750f0fdebe..4c7b45454d 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include + #include "common/assert.h" #include "common/string_util.h" #include "core/core.h" diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h index c383527f4d..787485f22a 100644 --- a/src/core/hle/service/am/applets/profile_select.h +++ b/src/core/hle/service/am/applets/profile_select.h @@ -4,14 +4,10 @@ #pragma once -#include -#include #include #include "common/common_funcs.h" -#include "common/swap.h" #include "core/hle/service/acc/profile_manager.h" -#include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 40984ffa96..981bdec51f 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -38,12 +38,7 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( return params; } -SoftwareKeyboard::SoftwareKeyboard() { - // Some applets require this to be signalled on applet creation, some do not. Internally, this - // is done by a flag in the applet module, but for simplicity SoftwareKeyboard is one of the - // applets with this flag. - broker.SignalStateChanged(); -} +SoftwareKeyboard::SoftwareKeyboard() = default; SoftwareKeyboard::~SoftwareKeyboard() = default; diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h index 9acb092f3d..868573324f 100644 --- a/src/yuzu/applets/profile_select.h +++ b/src/yuzu/applets/profile_select.h @@ -23,7 +23,7 @@ class QtProfileSelectionDialog final : public QDialog { Q_OBJECT public: - QtProfileSelectionDialog(QWidget* parent); + explicit QtProfileSelectionDialog(QWidget* parent); ~QtProfileSelectionDialog() override; void accept() override; From 3e75175d0242090902e4b383086e3f5ac6cf3f73 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Mon, 3 Dec 2018 12:25:27 -0500 Subject: [PATCH 09/74] svc: Implement SetThreadActivity (thread suspension) --- src/core/hle/kernel/errors.h | 2 +- src/core/hle/kernel/svc.cpp | 38 +++++++++++++++++++++++++++++---- src/core/hle/kernel/thread.cpp | 24 ++++++++++++++++++++- src/core/hle/kernel/thread.h | 14 ++++++++++++ src/yuzu/debugger/wait_tree.cpp | 4 ++++ 5 files changed, 76 insertions(+), 6 deletions(-) diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 8b58d701d6..d8240ec6d0 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -27,7 +27,7 @@ constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118}; constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119}; constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120}; constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; -constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122}; +constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122}; constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132}; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 948989b318..72da3eb3dc 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -851,8 +851,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) } /// Sets the thread activity -static ResultCode SetThreadActivity(Handle handle, u32 unknown) { - LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown); +static ResultCode SetThreadActivity(Handle handle, u32 activity) { + LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); + if (activity > static_cast(ThreadActivity::Paused)) { + return ERR_INVALID_ENUM_VALUE; + } + + const auto* current_process = Core::CurrentProcess(); + const SharedPtr thread = current_process->GetHandleTable().Get(handle); + if (!thread) { + LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); + return ERR_INVALID_HANDLE; + } + + if (thread->GetOwnerProcess() != current_process) { + LOG_ERROR(Kernel_SVC, + "The current process does not own the current thread, thread_handle={:08X} " + "thread_pid={}, " + "current_process_pid={}", + handle, thread->GetOwnerProcess()->GetProcessID(), + current_process->GetProcessID()); + return ERR_INVALID_HANDLE; + } + + if (thread == GetCurrentThread()) { + LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); + return ERR_BUSY; + } + + thread->SetActivity(static_cast(activity)); return RESULT_SUCCESS; } @@ -879,7 +906,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { if (thread == GetCurrentThread()) { LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); - return ERR_ALREADY_REGISTERED; + return ERR_BUSY; } Core::ARM_Interface::ThreadContext ctx = thread->GetContext(); @@ -1157,7 +1184,10 @@ static ResultCode StartThread(Handle thread_handle) { ASSERT(thread->GetStatus() == ThreadStatus::Dormant); thread->ResumeFromWait(); - Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); + + if (thread->GetStatus() == ThreadStatus::Ready) { + Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); + } return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 4ffb768183..748c45dd76 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -50,7 +50,7 @@ void Thread::Stop() { // Clean up thread from ready queue // This is only needed when the thread is terminated forcefully (SVC TerminateProcess) - if (status == ThreadStatus::Ready) { + if (status == ThreadStatus::Ready || status == ThreadStatus::Paused) { scheduler->UnscheduleThread(this, current_priority); } @@ -140,6 +140,11 @@ void Thread::ResumeFromWait() { wakeup_callback = nullptr; + if (activity == ThreadActivity::Paused) { + status = ThreadStatus::Paused; + return; + } + status = ThreadStatus::Ready; ChangeScheduler(); @@ -388,6 +393,23 @@ bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr t return wakeup_callback(reason, std::move(thread), std::move(object), index); } +void Thread::SetActivity(ThreadActivity value) { + activity = value; + + if (value == ThreadActivity::Paused) { + // Set status if not waiting + if (status == ThreadStatus::Ready) { + status = ThreadStatus::Paused; + } else if (status == ThreadStatus::Running) { + status = ThreadStatus::Paused; + Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); + } + } else if (status == ThreadStatus::Paused) { + // Ready to reschedule + ResumeFromWait(); + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////// /** diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index d384d50dbc..268bf845bb 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -44,6 +44,7 @@ enum ThreadProcessorId : s32 { enum class ThreadStatus { Running, ///< Currently running Ready, ///< Ready to run + Paused, ///< Paused by SetThreadActivity or debug WaitHLEEvent, ///< Waiting for hle event to finish WaitSleep, ///< Waiting due to a SleepThread SVC WaitIPC, ///< Waiting for the reply from an IPC request @@ -60,6 +61,11 @@ enum class ThreadWakeupReason { Timeout // The thread was woken up due to a wait timeout. }; +enum class ThreadActivity : u32 { + Normal = 0, + Paused = 1, +}; + class Thread final : public WaitObject { public: using TLSMemory = std::vector; @@ -370,6 +376,12 @@ public: return affinity_mask; } + ThreadActivity GetActivity() const { + return activity; + } + + void SetActivity(ThreadActivity value); + private: explicit Thread(KernelCore& kernel); ~Thread() override; @@ -438,6 +450,8 @@ private: TLSMemoryPtr tls_memory = std::make_shared(); std::string name; + + ThreadActivity activity = ThreadActivity::Normal; }; /** diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 96c57fe976..293351c759 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -221,6 +221,9 @@ QString WaitTreeThread::GetText() const { case Kernel::ThreadStatus::Ready: status = tr("ready"); break; + case Kernel::ThreadStatus::Paused: + status = tr("paused"); + break; case Kernel::ThreadStatus::WaitHLEEvent: status = tr("waiting for HLE return"); break; @@ -261,6 +264,7 @@ QColor WaitTreeThread::GetColor() const { case Kernel::ThreadStatus::Running: return QColor(Qt::GlobalColor::darkGreen); case Kernel::ThreadStatus::Ready: + case Kernel::ThreadStatus::Paused: return QColor(Qt::GlobalColor::darkBlue); case Kernel::ThreadStatus::WaitHLEEvent: case Kernel::ThreadStatus::WaitIPC: From a3d78b77f8734cba6e73d3b57b0d6af68e7ca110 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 4 Dec 2018 02:25:34 -0500 Subject: [PATCH 10/74] debugger: Set paused thread color --- src/yuzu/debugger/wait_tree.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 293351c759..fd4ddbd446 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -264,8 +264,9 @@ QColor WaitTreeThread::GetColor() const { case Kernel::ThreadStatus::Running: return QColor(Qt::GlobalColor::darkGreen); case Kernel::ThreadStatus::Ready: - case Kernel::ThreadStatus::Paused: return QColor(Qt::GlobalColor::darkBlue); + case Kernel::ThreadStatus::Paused: + return QColor(Qt::GlobalColor::lightGray); case Kernel::ThreadStatus::WaitHLEEvent: case Kernel::ThreadStatus::WaitIPC: return QColor(Qt::GlobalColor::darkRed); From 281b64daf46ca5a27c29a14cf31f1b8b6f985d46 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 22 Nov 2018 15:25:12 -0500 Subject: [PATCH 11/74] ui_settings: Add UI setting for input profile index --- src/yuzu/configuration/config.cpp | 2 ++ src/yuzu/ui_settings.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 83ebbd1fe7..ef028cca34 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -506,6 +506,7 @@ void Config::ReadValues() { UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt(); UISettings::values.show_console = qt_config->value("showConsole", false).toBool(); + UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt(); qt_config->endGroup(); } @@ -695,6 +696,7 @@ void Config::SaveValues() { qt_config->setValue("firstStart", UISettings::values.first_start); qt_config->setValue("calloutFlags", UISettings::values.callout_flags); qt_config->setValue("showConsole", UISettings::values.show_console); + qt_config->setValue("profileIndex", UISettings::values.profile_index); qt_config->endGroup(); } diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index e80aebc0a2..035192aeb5 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h @@ -58,6 +58,9 @@ struct Values { // logging bool show_console; + // Controllers + uint32_t profile_index; + // Game List bool show_unknown; bool show_add_ons; From 20dffc22a2f706d4a7420457f22137c794efeecc Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 22 Nov 2018 15:26:29 -0500 Subject: [PATCH 12/74] configure: Use ConfigureInputSimple for Input tab --- src/yuzu/configuration/configure.ui | 52 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index 9b297df283..8706b80d21 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -7,7 +7,7 @@ 0 0 461 - 500 + 659 @@ -24,17 +24,17 @@ General - - - Game List - - + + + Game List + + System - + Input @@ -54,11 +54,11 @@ Debug - - - Web - - + + + Web + + @@ -77,12 +77,12 @@
configuration/configure_general.h
1 - - ConfigureGameList - QWidget -
configuration/configure_gamelist.h
- 1 -
+ + ConfigureGameList + QWidget +
configuration/configure_gamelist.h
+ 1 +
ConfigureSystem QWidget @@ -102,9 +102,9 @@ 1 - ConfigureInput + ConfigureInputSimple QWidget -
configuration/configure_input.h
+
configuration/configure_input_simple.h
1
@@ -113,12 +113,12 @@
configuration/configure_graphics.h
1
- - ConfigureWeb - QWidget -
configuration/configure_web.h
- 1 -
+ + ConfigureWeb + QWidget +
configuration/configure_web.h
+ 1 +
From 59ca8d458d36f6780b17e7d29e97c6084c23b542 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 22 Nov 2018 15:27:07 -0500 Subject: [PATCH 13/74] configure_input: Convert into QDialog --- src/yuzu/configuration/configure_input.cpp | 2 +- src/yuzu/configuration/configure_input.h | 4 +- src/yuzu/configuration/configure_input.ui | 48 ++++++++++++++++++++-- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 830d261153..3e78802ce3 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -34,7 +34,7 @@ void CallConfigureDialog(ConfigureInput& parent, Args&&... args) { } // Anonymous namespace ConfigureInput::ConfigureInput(QWidget* parent) - : QWidget(parent), ui(std::make_unique()) { + : QDialog(parent), ui(std::make_unique()) { ui->setupUi(this); players_controller = { diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index 1649e4c0b0..b5005e3ea6 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -7,8 +7,8 @@ #include #include +#include #include -#include #include "ui_configure_input.h" @@ -20,7 +20,7 @@ namespace Ui { class ConfigureInput; } -class ConfigureInput : public QWidget { +class ConfigureInput : public QDialog { Q_OBJECT public: diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui index dae8277bcd..0a2d9f0246 100644 --- a/src/yuzu/configuration/configure_input.ui +++ b/src/yuzu/configuration/configure_input.ui @@ -1,13 +1,13 @@ ConfigureInput - + 0 0 - 473 - 685 + 384 + 576 @@ -478,6 +478,13 @@
+ + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + @@ -485,5 +492,38 @@ - + + + buttonBox + accepted() + ConfigureInput + accept() + + + 294 + 553 + + + 191 + 287 + + + + + buttonBox + rejected() + ConfigureInput + reject() + + + 294 + 553 + + + 191 + 287 + + + + From 233a80419633db70f7b9e26be69fe62d6040cecf Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 1 Dec 2018 11:11:11 -0500 Subject: [PATCH 14/74] configure_input: Add ConfigureInputSimple as default input UI config Greatly simplifies the current input UI, while still allowing power users to tweak advanced settings. Adds 'input profiles', which are easy autoconfigurations to make getting started easy and fast. Also has a custom option which brings up the current, full UI. --- src/yuzu/CMakeLists.txt | 3 + src/yuzu/configuration/config.cpp | 10 ++ src/yuzu/configuration/config.h | 1 + .../configuration/configure_input_player.cpp | 1 + .../configuration/configure_input_simple.cpp | 140 ++++++++++++++++++ .../configuration/configure_input_simple.h | 40 +++++ .../configuration/configure_input_simple.ui | 97 ++++++++++++ src/yuzu/ui_settings.h | 2 +- 8 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 src/yuzu/configuration/configure_input_simple.cpp create mode 100644 src/yuzu/configuration/configure_input_simple.h create mode 100644 src/yuzu/configuration/configure_input_simple.ui diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cfca8f4a8d..68448e5649 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -31,6 +31,8 @@ add_executable(yuzu configuration/configure_input.h configuration/configure_input_player.cpp configuration/configure_input_player.h + configuration/configure_input_simple.cpp + configuration/configure_input_simple.h configuration/configure_mouse_advanced.cpp configuration/configure_mouse_advanced.h configuration/configure_system.cpp @@ -85,6 +87,7 @@ set(UIS configuration/configure_graphics.ui configuration/configure_input.ui configuration/configure_input_player.ui + configuration/configure_input_simple.ui configuration/configure_mouse_advanced.ui configuration/configure_system.ui configuration/configure_touchscreen_advanced.ui diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index ef028cca34..5e0d149cd9 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -4,6 +4,7 @@ #include #include "common/file_util.h" +#include "configure_input_simple.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/hid/controllers/npad.h" #include "input_common/main.h" @@ -342,6 +343,13 @@ void Config::ReadTouchscreenValues() { qt_config->endGroup(); } +void Config::ApplyDefaultProfileIfInputInvalid() { + if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(), + [](const Settings::PlayerInput& in) { return in.connected; })) { + ApplyInputProfileConfiguration(UISettings::values.profile_index); + } +} + void Config::ReadValues() { qt_config->beginGroup("Controls"); @@ -508,6 +516,8 @@ void Config::ReadValues() { UISettings::values.show_console = qt_config->value("showConsole", false).toBool(); UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt(); + ApplyDefaultProfileIfInputInvalid(); + qt_config->endGroup(); } diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index a1c27bbf9c..e73ad19bbc 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -34,6 +34,7 @@ private: void ReadKeyboardValues(); void ReadMouseValues(); void ReadTouchscreenValues(); + void ApplyDefaultProfileIfInputInvalid(); void SaveValues(); void SavePlayerValues(); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 7dadd83c1a..ba2b32c4f2 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp new file mode 100644 index 0000000000..10c804388f --- /dev/null +++ b/src/yuzu/configuration/configure_input_simple.cpp @@ -0,0 +1,140 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include + +#include + +#include "ui_configure_input_simple.h" +#include "yuzu/configuration/configure_input.h" +#include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/configure_input_simple.h" +#include "yuzu/ui_settings.h" + +namespace { + +template +void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) { + caller->applyConfiguration(); + Dialog dialog(caller, std::forward(args)...); + + const auto res = dialog.exec(); + if (res == QDialog::Accepted) { + dialog.applyConfiguration(); + } +} + +// OnProfileSelect functions should (when applicable): +// - Set controller types +// - Set controller enabled +// - Set docked mode +// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch) +// +// OnProfileSelect function should NOT however: +// - Reset any button mappings +// - Open any dialogs +// - Block in any way + +constexpr std::size_t HANDHELD_INDEX = 8; + +void HandheldOnProfileSelect() { + Settings::values.players[HANDHELD_INDEX].connected = true; + Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon; + + for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) { + Settings::values.players[player].connected = false; + } + + Settings::values.use_docked_mode = false; + Settings::values.keyboard_enabled = false; + Settings::values.mouse_enabled = false; + Settings::values.debug_pad_enabled = false; + Settings::values.touchscreen.enabled = true; +} + +void DualJoyconsDockedOnProfileSelect() { + Settings::values.players[0].connected = true; + Settings::values.players[0].type = Settings::ControllerType::DualJoycon; + + for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) { + Settings::values.players[player].connected = false; + } + + Settings::values.use_docked_mode = true; + Settings::values.keyboard_enabled = false; + Settings::values.mouse_enabled = false; + Settings::values.debug_pad_enabled = false; + Settings::values.touchscreen.enabled = false; +} + +// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure +// is clicked) +using InputProfile = + std::tuple, std::function>; + +const std::array INPUT_PROFILES{{ + {ConfigureInputSimple::tr("Single Player - Handheld - Undocked"), HandheldOnProfileSelect, + [](ConfigureInputSimple* caller) { + CallConfigureDialog(caller, HANDHELD_INDEX, false); + }}, + {ConfigureInputSimple::tr("Single Player - Dual Joycons - Docked"), + DualJoyconsDockedOnProfileSelect, + [](ConfigureInputSimple* caller) { + CallConfigureDialog(caller, 1, false); + }}, + {ConfigureInputSimple::tr("Custom"), [] {}, CallConfigureDialog}, +}}; + +} // namespace + +void ApplyInputProfileConfiguration(int profile_index) { + std::get<1>( + INPUT_PROFILES.at(std::min(profile_index, static_cast(INPUT_PROFILES.size() - 1))))(); +} + +ConfigureInputSimple::ConfigureInputSimple(QWidget* parent) + : QWidget(parent), ui(std::make_unique()) { + ui->setupUi(this); + + for (const auto& profile : INPUT_PROFILES) { + ui->profile_combobox->addItem(std::get<0>(profile), std::get<0>(profile)); + } + + connect(ui->profile_combobox, QOverload::of(&QComboBox::currentIndexChanged), this, + &ConfigureInputSimple::OnSelectProfile); + connect(ui->profile_configure, &QPushButton::pressed, this, &ConfigureInputSimple::OnConfigure); + + this->loadConfiguration(); +} + +ConfigureInputSimple::~ConfigureInputSimple() = default; + +void ConfigureInputSimple::applyConfiguration() { + auto index = ui->profile_combobox->currentIndex(); + // Make the stored index for "Custom" very large so that if new profiles are added it + // doesn't change. + if (index >= static_cast(INPUT_PROFILES.size() - 1)) + index = std::numeric_limits::max(); + + UISettings::values.profile_index = index; +} + +void ConfigureInputSimple::loadConfiguration() { + const auto index = UISettings::values.profile_index; + if (index >= static_cast(INPUT_PROFILES.size()) || index < 0) + ui->profile_combobox->setCurrentIndex(static_cast(INPUT_PROFILES.size() - 1)); + else + ui->profile_combobox->setCurrentIndex(index); +} + +void ConfigureInputSimple::OnSelectProfile(int index) { + ApplyInputProfileConfiguration(index); +} + +void ConfigureInputSimple::OnConfigure() { + std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this); +} diff --git a/src/yuzu/configuration/configure_input_simple.h b/src/yuzu/configuration/configure_input_simple.h new file mode 100644 index 0000000000..5b6b699948 --- /dev/null +++ b/src/yuzu/configuration/configure_input_simple.h @@ -0,0 +1,40 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include + +class QPushButton; +class QString; +class QTimer; + +namespace Ui { +class ConfigureInputSimple; +} + +// Used by configuration loader to apply a profile if the input is invalid. +void ApplyInputProfileConfiguration(int profile_index); + +class ConfigureInputSimple : public QWidget { + Q_OBJECT + +public: + explicit ConfigureInputSimple(QWidget* parent = nullptr); + ~ConfigureInputSimple() override; + + /// Save all button configurations to settings file + void applyConfiguration(); + +private: + /// Load configuration settings. + void loadConfiguration(); + + void OnSelectProfile(int index); + void OnConfigure(); + + std::unique_ptr ui; +}; diff --git a/src/yuzu/configuration/configure_input_simple.ui b/src/yuzu/configuration/configure_input_simple.ui new file mode 100644 index 0000000000..c4889caa99 --- /dev/null +++ b/src/yuzu/configuration/configure_input_simple.ui @@ -0,0 +1,97 @@ + + + ConfigureInputSimple + + + + 0 + 0 + 473 + 685 + + + + ConfigureInputSimple + + + + + + + + Profile + + + + + + Configure + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 250 + 0 + + + + + + + + Choose a controller configuration: + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index 035192aeb5..2f434eab0e 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h @@ -59,7 +59,7 @@ struct Values { bool show_console; // Controllers - uint32_t profile_index; + int profile_index; // Game List bool show_unknown; From c07059e7fdead17b47acea02ab1a6ac13c8f4d9c Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 4 Dec 2018 22:01:01 -0500 Subject: [PATCH 15/74] configure_input_simple: Properly signal docked mode change --- src/yuzu/configuration/configure_input.cpp | 58 +++++++++---------- src/yuzu/configuration/configure_input.h | 4 +- .../configuration/configure_input_simple.cpp | 2 + 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 3e78802ce3..f39d57998a 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -20,6 +20,33 @@ #include "yuzu/configuration/configure_input_player.h" #include "yuzu/configuration/configure_mouse_advanced.h" +void OnDockedModeChanged(bool last_state, bool new_state) { + if (last_state == new_state) { + return; + } + + Core::System& system{Core::System::GetInstance()}; + if (!system.IsPoweredOn()) { + return; + } + Service::SM::ServiceManager& sm = system.ServiceManager(); + + // Message queue is shared between these services, we just need to signal an operation + // change to one and it will handle both automatically + auto applet_oe = sm.GetService("appletOE"); + auto applet_ae = sm.GetService("appletAE"); + bool has_signalled = false; + + if (applet_oe != nullptr) { + applet_oe->GetMessageQueue()->OperationModeChanged(); + has_signalled = true; + } + + if (applet_ae != nullptr && !has_signalled) { + applet_ae->GetMessageQueue()->OperationModeChanged(); + } +} + namespace { template void CallConfigureDialog(ConfigureInput& parent, Args&&... args) { @@ -90,37 +117,6 @@ ConfigureInput::ConfigureInput(QWidget* parent) ConfigureInput::~ConfigureInput() = default; -void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) { - if (ui->use_docked_mode->isChecked() && ui->handheld_connected->isChecked()) { - ui->handheld_connected->setChecked(false); - } - - if (last_state == new_state) { - return; - } - - Core::System& system{Core::System::GetInstance()}; - if (!system.IsPoweredOn()) { - return; - } - Service::SM::ServiceManager& sm = system.ServiceManager(); - - // Message queue is shared between these services, we just need to signal an operation - // change to one and it will handle both automatically - auto applet_oe = sm.GetService("appletOE"); - auto applet_ae = sm.GetService("appletAE"); - bool has_signalled = false; - - if (applet_oe != nullptr) { - applet_oe->GetMessageQueue()->OperationModeChanged(); - has_signalled = true; - } - - if (applet_ae != nullptr && !has_signalled) { - applet_ae->GetMessageQueue()->OperationModeChanged(); - } -} - void ConfigureInput::applyConfiguration() { for (std::size_t i = 0; i < players_controller.size(); ++i) { const auto controller_type_index = players_controller[i]->currentIndex(); diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index b5005e3ea6..b8e62cc2b6 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -20,6 +20,8 @@ namespace Ui { class ConfigureInput; } +void OnDockedModeChanged(bool last_state, bool new_state); + class ConfigureInput : public QDialog { Q_OBJECT @@ -33,8 +35,6 @@ public: private: void updateUIEnabled(); - void OnDockedModeChanged(bool last_state, bool new_state); - /// Load configuration settings. void loadConfiguration(); /// Restore all buttons to their default values. diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp index 10c804388f..b4f3724bd6 100644 --- a/src/yuzu/configuration/configure_input_simple.cpp +++ b/src/yuzu/configuration/configure_input_simple.cpp @@ -132,7 +132,9 @@ void ConfigureInputSimple::loadConfiguration() { } void ConfigureInputSimple::OnSelectProfile(int index) { + const auto old_docked = Settings::values.use_docked_mode; ApplyInputProfileConfiguration(index); + OnDockedModeChanged(old_docked, Settings::values.use_docked_mode); } void ConfigureInputSimple::OnConfigure() { From 0d2ba2ca4c43aeadf5f65dfbc978f20ff3bf0a2c Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 9 Dec 2018 19:46:15 -0500 Subject: [PATCH 16/74] applets: Correct usage of SignalStateChanged event This was causing some games (most notably Pokemon Quest) to softlock due to an event being fired when not supposed to. This also removes a hack wherein we were firing the state changed event when the game retrieves it, which is incorrect. --- src/core/hle/service/am/am.cpp | 1 - src/core/hle/service/am/applets/applets.cpp | 6 +++--- src/core/hle/service/am/applets/software_keyboard.cpp | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3a7b6da846..f83730cd60 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -565,7 +565,6 @@ private: void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); - applet->GetBroker().SignalStateChanged(); const auto event = applet->GetBroker().GetStateChangedEvent(); IPC::ResponseBuilder rb{ctx, 2, 1}; diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 47da355370..7698ca8195 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -16,11 +16,11 @@ namespace Service::AM::Applets { AppletDataBroker::AppletDataBroker() { auto& kernel = Core::System::GetInstance().Kernel(); state_changed_event = Kernel::WritableEvent::CreateEventPair( - kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:StateChangedEvent"); + kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent"); pop_out_data_event = Kernel::WritableEvent::CreateEventPair( - kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopDataOutEvent"); + kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent"); pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair( - kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); + kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); } AppletDataBroker::~AppletDataBroker() = default; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 981bdec51f..f255f74b53 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -146,11 +146,10 @@ void SoftwareKeyboard::WriteText(std::optional text) { if (complete) { broker.PushNormalDataFromApplet(IStorage{output_main}); + broker.SignalStateChanged(); } else { broker.PushInteractiveDataFromApplet(IStorage{output_sub}); } - - broker.SignalStateChanged(); } else { output_main[0] = 1; complete = true; From 34b24a47e9515a6a80d1961fc2e83bcfc84b7889 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 14 Dec 2018 18:19:12 -0500 Subject: [PATCH 17/74] vm_manager: Add backing functionality for memory attributes Adds the barebones enumeration constants and functions in place to handle memory attributes, while also essentially leaving the attribute itself non-functional. --- src/core/hle/kernel/vm_manager.cpp | 3 +- src/core/hle/kernel/vm_manager.h | 83 ++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index d3b55a51ec..ea3f59935b 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -38,7 +38,7 @@ static const char* GetMemoryStateName(MemoryState state) { bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { ASSERT(base + size == next.base); if (permissions != next.permissions || meminfo_state != next.meminfo_state || - type != next.type) { + attribute != next.attribute || type != next.type) { return false; } if (type == VMAType::AllocatedMemoryBlock && @@ -308,6 +308,7 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const { if (IsValidHandle(vma)) { memory_info.base_address = vma->second.base; + memory_info.attributes = ToSvcMemoryAttribute(vma->second.attribute); memory_info.permission = static_cast(vma->second.permissions); memory_info.size = vma->second.size; memory_info.state = ToSvcMemoryState(vma->second.meminfo_state); diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 10bacac3e4..99eeeffaaa 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -43,6 +43,88 @@ enum class VMAPermission : u8 { ReadWriteExecute = Read | Write | Execute, }; +constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { + return static_cast(u32(lhs) | u32(rhs)); +} + +constexpr VMAPermission operator&(VMAPermission lhs, VMAPermission rhs) { + return static_cast(u32(lhs) & u32(rhs)); +} + +constexpr VMAPermission operator^(VMAPermission lhs, VMAPermission rhs) { + return static_cast(u32(lhs) ^ u32(rhs)); +} + +constexpr VMAPermission operator~(VMAPermission permission) { + return static_cast(~u32(permission)); +} + +constexpr VMAPermission& operator|=(VMAPermission& lhs, VMAPermission rhs) { + lhs = lhs | rhs; + return lhs; +} + +constexpr VMAPermission& operator&=(VMAPermission& lhs, VMAPermission rhs) { + lhs = lhs & rhs; + return lhs; +} + +constexpr VMAPermission& operator^=(VMAPermission& lhs, VMAPermission rhs) { + lhs = lhs ^ rhs; + return lhs; +} + +/// Attribute flags that can be applied to a VMA +enum class MemoryAttribute : u32 { + Mask = 0xFF, + + /// No particular qualities + None = 0, + /// Memory locked/borrowed for use. e.g. This would be used by transfer memory. + Locked = 1, + /// Memory locked for use by IPC-related internals. + LockedForIPC = 2, + /// Mapped as part of the device address space. + DeviceMapped = 4, + /// Uncached memory + Uncached = 8, +}; + +constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) { + return static_cast(u32(lhs) | u32(rhs)); +} + +constexpr MemoryAttribute operator&(MemoryAttribute lhs, MemoryAttribute rhs) { + return static_cast(u32(lhs) & u32(rhs)); +} + +constexpr MemoryAttribute operator^(MemoryAttribute lhs, MemoryAttribute rhs) { + return static_cast(u32(lhs) ^ u32(rhs)); +} + +constexpr MemoryAttribute operator~(MemoryAttribute attribute) { + return static_cast(~u32(attribute)); +} + +constexpr MemoryAttribute& operator|=(MemoryAttribute& lhs, MemoryAttribute rhs) { + lhs = lhs | rhs; + return lhs; +} + +constexpr MemoryAttribute& operator&=(MemoryAttribute& lhs, MemoryAttribute rhs) { + lhs = lhs & rhs; + return lhs; +} + +constexpr MemoryAttribute& operator^=(MemoryAttribute& lhs, MemoryAttribute rhs) { + lhs = lhs ^ rhs; + return lhs; +} + +constexpr u32 ToSvcMemoryAttribute(MemoryAttribute attribute) { + return static_cast(attribute & MemoryAttribute::Mask); +} + // clang-format off /// Represents memory states and any relevant flags, as used by the kernel. /// svcQueryMemory interprets these by masking away all but the first eight @@ -183,6 +265,7 @@ struct VirtualMemoryArea { VMAPermission permissions = VMAPermission::None; /// Tag returned by svcQueryMemory. Not otherwise used. MemoryState meminfo_state = MemoryState::Unmapped; + MemoryAttribute attribute = MemoryAttribute::None; // Settings for type = AllocatedMemoryBlock /// Memory block backing this VMA. From 4dc8a7da3f8ee53ba8a812141c03bec7b27b67bc Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 14 Dec 2018 20:59:08 -0500 Subject: [PATCH 18/74] vm_manager: Rename meminfo_state to state This is shorter and more concise. This also removes the now-innaccurate comment, as it's not returned wholesale to svcQueryMemory anymore. --- src/core/hle/kernel/vm_manager.cpp | 16 ++++++++-------- src/core/hle/kernel/vm_manager.h | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index ea3f59935b..252f92df2e 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -37,8 +37,8 @@ static const char* GetMemoryStateName(MemoryState state) { bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { ASSERT(base + size == next.base); - if (permissions != next.permissions || meminfo_state != next.meminfo_state || - attribute != next.attribute || type != next.type) { + if (permissions != next.permissions || state != next.state || attribute != next.attribute || + type != next.type) { return false; } if (type == VMAType::AllocatedMemoryBlock && @@ -115,7 +115,7 @@ ResultVal VMManager::MapMemoryBlock(VAddr target, final_vma.type = VMAType::AllocatedMemoryBlock; final_vma.permissions = VMAPermission::ReadWrite; - final_vma.meminfo_state = state; + final_vma.state = state; final_vma.backing_block = std::move(block); final_vma.offset = offset; UpdatePageTableForVMA(final_vma); @@ -140,7 +140,7 @@ ResultVal VMManager::MapBackingMemory(VAddr target, u8* me final_vma.type = VMAType::BackingMemory; final_vma.permissions = VMAPermission::ReadWrite; - final_vma.meminfo_state = state; + final_vma.state = state; final_vma.backing_memory = memory; UpdatePageTableForVMA(final_vma); @@ -177,7 +177,7 @@ ResultVal VMManager::MapMMIO(VAddr target, PAddr paddr, u6 final_vma.type = VMAType::MMIO; final_vma.permissions = VMAPermission::ReadWrite; - final_vma.meminfo_state = state; + final_vma.state = state; final_vma.paddr = paddr; final_vma.mmio_handler = std::move(mmio_handler); UpdatePageTableForVMA(final_vma); @@ -189,7 +189,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { VirtualMemoryArea& vma = vma_handle->second; vma.type = VMAType::Free; vma.permissions = VMAPermission::None; - vma.meminfo_state = MemoryState::Unmapped; + vma.state = MemoryState::Unmapped; vma.backing_block = nullptr; vma.offset = 0; @@ -311,7 +311,7 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const { memory_info.attributes = ToSvcMemoryAttribute(vma->second.attribute); memory_info.permission = static_cast(vma->second.permissions); memory_info.size = vma->second.size; - memory_info.state = ToSvcMemoryState(vma->second.meminfo_state); + memory_info.state = ToSvcMemoryState(vma->second.state); } else { memory_info.base_address = address_space_end; memory_info.permission = static_cast(VMAPermission::None); @@ -365,7 +365,7 @@ void VMManager::LogLayout() const { (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-', - GetMemoryStateName(vma.meminfo_state)); + GetMemoryStateName(vma.state)); } } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 99eeeffaaa..e2614cd4c9 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -263,8 +263,7 @@ struct VirtualMemoryArea { VMAType type = VMAType::Free; VMAPermission permissions = VMAPermission::None; - /// Tag returned by svcQueryMemory. Not otherwise used. - MemoryState meminfo_state = MemoryState::Unmapped; + MemoryState state = MemoryState::Unmapped; MemoryAttribute attribute = MemoryAttribute::None; // Settings for type = AllocatedMemoryBlock From a6daed74f58a37bfddd32bc1e0503013cd32df13 Mon Sep 17 00:00:00 2001 From: heapo Date: Sun, 16 Dec 2018 09:34:24 -0800 Subject: [PATCH 19/74] Fix arrayed shadow sampler array slice/depth comparison ordering, as well as invalid GLSL LOD selection. --- .../renderer_opengl/gl_shader_decompiler.cpp | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index a5cfa0070b..708c7b4e9d 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -1591,23 +1591,21 @@ private: process_mode == Tegra::Shader::TextureProcessMode::LL || process_mode == Tegra::Shader::TextureProcessMode::LLA; + // LOD selection (either via bias or explicit textureLod) not supported in GL for + // sampler2DArrayShadow and samplerCubeArrayShadow. const bool gl_lod_supported = !( (texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) || - (texture_type == Tegra::Shader::TextureType::TextureCube && !is_array && - depth_compare)); + (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && depth_compare)); const std::string read_method = lod_needed && gl_lod_supported ? "textureLod(" : "texture("; std::string texture = read_method + sampler + ", coord"; - if (process_mode != Tegra::Shader::TextureProcessMode::None) { + UNIMPLEMENTED_IF(process_mode != Tegra::Shader::TextureProcessMode::None && + !gl_lod_supported); + + if (process_mode != Tegra::Shader::TextureProcessMode::None && gl_lod_supported) { if (process_mode == Tegra::Shader::TextureProcessMode::LZ) { - if (gl_lod_supported) { - texture += ", 0"; - } else { - // Lod 0 is emulated by a big negative bias - // in scenarios that are not supported by glsl - texture += ", -1000"; - } + texture += ", 0.0"; } else { // If present, lod or bias are always stored in the register indexed by the // gpr20 @@ -1645,15 +1643,15 @@ private: if (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) { coord += ",0.0"; } + if (is_array) { + coord += ',' + regs.GetRegisterAsInteger(array_register); + } if (depth_compare) { // Depth is always stored in the register signaled by gpr20 // or in the next register if lod or bias are used const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0); coord += ',' + regs.GetRegisterAsFloat(depth_register); } - if (is_array) { - coord += ',' + regs.GetRegisterAsInteger(array_register); - } coord += ");"; return std::make_pair( coord, GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, 0)); @@ -1686,15 +1684,15 @@ private: } } + if (is_array) { + coord += ',' + regs.GetRegisterAsInteger(array_register); + } if (depth_compare) { // Depth is always stored in the register signaled by gpr20 // or in the next register if lod or bias are used const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0); coord += ',' + regs.GetRegisterAsFloat(depth_register); } - if (is_array) { - coord += ',' + regs.GetRegisterAsInteger(array_register); - } coord += ");"; return std::make_pair(coord, From 72599cc667046cbd588fe1c4e3e3c7b55b272e29 Mon Sep 17 00:00:00 2001 From: heapo Date: Fri, 14 Dec 2018 17:10:14 -0800 Subject: [PATCH 20/74] Implement postfactor multiplication/division for fmul instructions --- src/video_core/engines/shader_bytecode.h | 2 +- .../renderer_opengl/gl_shader_decompiler.cpp | 24 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 5ea094e646..5198cd268a 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -575,7 +575,7 @@ union Instruction { union { BitField<39, 2, u64> tab5cb8_2; - BitField<41, 3, u64> tab5c68_1; + BitField<41, 3, u64> postfactor; BitField<44, 2, u64> tab5c68_0; BitField<48, 1, u64> negate_b; } fmul; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index a5cfa0070b..bd61af463c 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -1867,9 +1867,6 @@ private: UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0, "FMUL tab5cb8_2({}) is not implemented", instr.fmul.tab5cb8_2.Value()); - UNIMPLEMENTED_IF_MSG(instr.fmul.tab5c68_1 != 0, - "FMUL tab5cb8_1({}) is not implemented", - instr.fmul.tab5c68_1.Value()); UNIMPLEMENTED_IF_MSG( instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented", instr.fmul.tab5c68_0 @@ -1879,7 +1876,26 @@ private: op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); - regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, + std::string postfactor_op; + if (instr.fmul.postfactor != 0) { + s8 postfactor = static_cast(instr.fmul.postfactor); + + // postfactor encoded as 3-bit 1's complement in instruction, + // interpreted with below logic. + if (postfactor >= 4) { + postfactor = 7 - postfactor; + } else { + postfactor = 0 - postfactor; + } + + if (postfactor > 0) { + postfactor_op = " * " + std::to_string(1 << postfactor); + } else { + postfactor_op = " / " + std::to_string(1 << -postfactor); + } + } + + regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + postfactor_op, 1, 1, instr.alu.saturate_d, 0, true); break; } From dd272298aa881de546a714b80505a095f6949258 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 17 Dec 2018 21:01:06 -0500 Subject: [PATCH 21/74] service/am: Unstub GetAppletResourceUserId This is supposed to return the current process' ID. (0 indicates an invalid ID for both process IDs and ARU IDs). --- src/core/hle/service/am/am.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3a7b6da846..cfd4ed3dea 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -71,10 +71,13 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") { IWindowController::~IWindowController() = default; void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); + const u64 process_id = Core::System::GetInstance().Kernel().CurrentProcess()->GetProcessID(); + + LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(0); + rb.Push(process_id); } void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) { From ef061481c5038aa9103764c351c041c5afa62f33 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 18 Dec 2018 04:28:50 -0300 Subject: [PATCH 22/74] shader_bytecode: Fixup half float's operator B encoding --- src/video_core/engines/shader_bytecode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 5198cd268a..2efeb6e1a5 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -609,7 +609,7 @@ union Instruction { BitField<31, 1, u64> negate_b; BitField<30, 1, u64> abs_b; - BitField<47, 2, HalfType> type_b; + BitField<28, 2, HalfType> type_b; BitField<35, 2, HalfType> type_c; } alu_half; From fd2c42bfcdf0e4f04059ba2f425ed2e83a52aa9f Mon Sep 17 00:00:00 2001 From: MerryMage Date: Tue, 18 Dec 2018 10:18:00 +0000 Subject: [PATCH 23/74] arm_dynarmic: Set CNTFRQ value --- src/core/arm/dynarmic/arm_dynarmic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 4d2491870c..afbda8d8b7 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -151,6 +151,7 @@ std::unique_ptr ARM_Dynarmic::MakeJit() const { config.tpidr_el0 = &cb->tpidr_el0; config.dczid_el0 = 4; config.ctr_el0 = 0x8444c004; + config.cntfrq_el0 = 19200000; // Value from fusee. // Unpredictable instructions config.define_unpredictable_behaviour = true; From eef6ce79a9c06f89be2760d7156eb44c063bab63 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Mon, 17 Dec 2018 23:04:02 +0000 Subject: [PATCH 24/74] kernel/thread: Set default fpcr --- src/core/hle/kernel/thread.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 4ffb768183..63f8923fd9 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -158,6 +158,9 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd context.cpu_registers[0] = arg; context.pc = entry_point; context.sp = stack_top; + // TODO(merry): Perform a hardware test to determine the below value. + // AHP = 0, DN = 1, FTZ = 1, RMode = Round towards zero + context.fpcr = 0x03C00000; } ResultVal> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, From 37280cf5559b8a6b2744360cdbbee177a783fd1f Mon Sep 17 00:00:00 2001 From: heapo Date: Tue, 18 Dec 2018 11:34:51 -0800 Subject: [PATCH 25/74] Texture format fixes: Flag RGBA16UI as GL_RGBA_INTEGER format, and interpret R16U as Z16 when depth_compare is enabled. --- .../renderer_opengl/gl_rasterizer_cache.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 5f4cdd1190..7ea07631ab 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -101,8 +101,18 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, params.srgb_conversion = config.tic.IsSrgbConversionEnabled(); params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), params.srgb_conversion); + + if (params.pixel_format == PixelFormat::R16U && config.tsc.depth_compare_enabled) { + // Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled, + // then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also + // causes GetFormatType to properly return 'Depth' below). + params.pixel_format = PixelFormat::Z16; + } + params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); params.type = GetFormatType(params.pixel_format); + UNIMPLEMENTED_IF(params.type == SurfaceType::ColorTexture && config.tsc.depth_compare_enabled); + params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); params.unaligned_height = config.tic.Height(); @@ -257,7 +267,7 @@ static constexpr std::array tex {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RGBA16U - {GL_RGBA16UI, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI + {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float, false}, // R11FG11FB10F {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI From a2be49305d8c5c66cfa2ec2060688013cf3729b9 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Fri, 31 Aug 2018 14:16:16 +0800 Subject: [PATCH 26/74] yuzu, video_core: Screenshot functionality Allows capturing screenshot at the current internal resolution (native for software renderer), but a setting is available to capture it in other resolutions. The screenshot is saved to a single PNG in the current layout. --- src/core/frontend/framebuffer_layout.cpp | 15 ++++ src/core/frontend/framebuffer_layout.h | 7 ++ src/video_core/renderer_base.cpp | 12 +++ src/video_core/renderer_base.h | 27 ++++++ .../renderer_opengl/renderer_opengl.cpp | 41 ++++++++- .../renderer_opengl/renderer_opengl.h | 9 +- src/video_core/video_core.cpp | 8 ++ src/video_core/video_core.h | 2 + src/yuzu/bootmanager.cpp | 18 ++++ src/yuzu/bootmanager.h | 6 ++ src/yuzu/configuration/config.cpp | 5 ++ src/yuzu/main.cpp | 28 ++++++ src/yuzu/main.h | 1 + src/yuzu/main.ui | 90 ++++++++++--------- src/yuzu/ui_settings.h | 4 + 15 files changed, 229 insertions(+), 44 deletions(-) diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 8f07aedc96..f8662d1931 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -6,6 +6,7 @@ #include "common/assert.h" #include "core/frontend/framebuffer_layout.h" +#include "core/settings.h" namespace Layout { @@ -42,4 +43,18 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) { return res; } +FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale) { + int width, height; + + if (Settings::values.use_docked_mode) { + width = ScreenDocked::WidthDocked * res_scale; + height = ScreenDocked::HeightDocked * res_scale; + } else { + width = ScreenUndocked::Width * res_scale; + height = ScreenUndocked::Height * res_scale; + } + + return DefaultFrameLayout(width, height); +} + } // namespace Layout diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index f3fddfa947..e06647794f 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -9,6 +9,7 @@ namespace Layout { enum ScreenUndocked : unsigned { Width = 1280, Height = 720 }; +enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 }; /// Describes the layout of the window framebuffer struct FramebufferLayout { @@ -34,4 +35,10 @@ struct FramebufferLayout { */ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height); +/** + * Convenience method to get frame layout by resolution scale + * @param res_scale resolution scale factor + */ +FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale); + } // namespace Layout diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 1482cdb409..94223f45f9 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -27,4 +27,16 @@ void RendererBase::UpdateCurrentFramebufferLayout() { render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height); } +void RendererBase::RequestScreenshot(void* data, std::function callback, + const Layout::FramebufferLayout& layout) { + if (renderer_settings.screenshot_requested) { + LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); + return; + } + renderer_settings.screenshot_bits = data; + renderer_settings.screenshot_complete_callback = std::move(callback); + renderer_settings.screenshot_framebuffer_layout = layout; + renderer_settings.screenshot_requested = true; +} + } // namespace VideoCore diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 669e26e15c..1d54c3723f 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -9,6 +9,7 @@ #include #include "common/common_types.h" +#include "core/frontend/emu_window.h" #include "video_core/gpu.h" #include "video_core/rasterizer_interface.h" @@ -21,6 +22,12 @@ namespace VideoCore { struct RendererSettings { std::atomic_bool use_framelimiter{false}; std::atomic_bool set_background_color{false}; + + // Screenshot + std::atomic screenshot_requested{false}; + void* screenshot_bits; + std::function screenshot_complete_callback; + Layout::FramebufferLayout screenshot_framebuffer_layout; }; class RendererBase : NonCopyable { @@ -57,9 +64,29 @@ public: return *rasterizer; } + Core::Frontend::EmuWindow& GetRenderWindow() { + return render_window; + } + + const Core::Frontend::EmuWindow& GetRenderWindow() const { + return render_window; + } + + RendererSettings& Settings() { + return renderer_settings; + } + + const RendererSettings& Settings() const { + return renderer_settings; + } + /// Refreshes the settings common to all renderers void RefreshBaseSettings(); + /// Request a screenshot of the next frame + void RequestScreenshot(void* data, std::function callback, + const Layout::FramebufferLayout& layout); + protected: Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle. std::unique_ptr rasterizer; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 4fd0d66c55..49a1989e49 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -138,7 +138,12 @@ void RendererOpenGL::SwapBuffers( // Load the framebuffer from memory, draw it to the screen, and swap buffers LoadFBToScreenInfo(*framebuffer); - DrawScreen(); + + if (renderer_settings.screenshot_requested) + CaptureScreenshot(); + + DrawScreen(render_window.GetFramebufferLayout()); + render_window.SwapBuffers(); } @@ -383,14 +388,13 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, /** * Draws the emulated screens to the emulator window. */ -void RendererOpenGL::DrawScreen() { +void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { if (renderer_settings.set_background_color) { // Update background color before drawing glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); } - const auto& layout = render_window.GetFramebufferLayout(); const auto& screen = layout.screen; glViewport(0, 0, layout.width, layout.height); @@ -414,6 +418,37 @@ void RendererOpenGL::DrawScreen() { /// Updates the framerate void RendererOpenGL::UpdateFramerate() {} +void RendererOpenGL::CaptureScreenshot() { + // Draw the current frame to the screenshot framebuffer + screenshot_framebuffer.Create(); + GLuint old_read_fb = state.draw.read_framebuffer; + GLuint old_draw_fb = state.draw.draw_framebuffer; + state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle; + state.Apply(); + + Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; + + GLuint renderbuffer; + glGenRenderbuffers(1, &renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); + + DrawScreen(layout); + + glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, + renderer_settings.screenshot_bits); + + screenshot_framebuffer.Release(); + state.draw.read_framebuffer = old_read_fb; + state.draw.draw_framebuffer = old_draw_fb; + state.Apply(); + glDeleteRenderbuffers(1, &renderbuffer); + + renderer_settings.screenshot_complete_callback(); + renderer_settings.screenshot_requested = false; +} + static const char* GetSource(GLenum source) { #define RET(s) \ case GL_DEBUG_SOURCE_##s: \ diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index c0868c0e47..067fad81b7 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -16,6 +16,10 @@ namespace Core::Frontend { class EmuWindow; } +namespace Layout { +class FramebufferLayout; +} + namespace OpenGL { /// Structure used for storing information about the textures for the Switch screen @@ -66,10 +70,12 @@ private: void ConfigureFramebufferTexture(TextureInfo& texture, const Tegra::FramebufferConfig& framebuffer); - void DrawScreen(); + void DrawScreen(const Layout::FramebufferLayout& layout); void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h); void UpdateFramerate(); + void CaptureScreenshot(); + // Loads framebuffer from emulated memory into the display information structure void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); // Fills active OpenGL texture with the given RGBA color. @@ -82,6 +88,7 @@ private: OGLVertexArray vertex_array; OGLBuffer vertex_buffer; OGLProgram shader; + OGLFramebuffer screenshot_framebuffer; /// Display information for Switch screen ScreenInfo screen_info; diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 07e3a7d246..f7de3471bd 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include +#include "core/core.h" +#include "core/settings.h" #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/video_core.h" @@ -13,4 +15,10 @@ std::unique_ptr CreateRenderer(Core::Frontend::EmuWindow& emu_wind return std::make_unique(emu_window); } +u16 GetResolutionScaleFactor(const RendererBase& renderer) { + return !Settings::values.resolution_factor + ? renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio() + : Settings::values.resolution_factor; +} + } // namespace VideoCore diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index f79f85dfeb..5b373bcb1f 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -22,4 +22,6 @@ class RendererBase; */ std::unique_ptr CreateRenderer(Core::Frontend::EmuWindow& emu_window); +u16 GetResolutionScaleFactor(const RendererBase& renderer); + } // namespace VideoCore diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 384e17921e..40db7a5e92 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -14,6 +14,8 @@ #include "input_common/keyboard.h" #include "input_common/main.h" #include "input_common/motion_emu.h" +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" #include "yuzu/bootmanager.h" EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} @@ -333,6 +335,22 @@ void GRenderWindow::InitRenderTarget() { BackupGeometry(); } +void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) { + auto& renderer = Core::System::GetInstance().Renderer(); + + if (!res_scale) + res_scale = VideoCore::GetResolutionScaleFactor(renderer); + + const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)}; + screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); + renderer.RequestScreenshot(screenshot_image.bits(), + [=] { + screenshot_image.mirrored(false, true).save(screenshot_path); + LOG_INFO(Frontend, "The screenshot is saved."); + }, + layout); +} + void GRenderWindow::OnMinimalClientAreaChangeRequest( const std::pair& minimal_size) { setMinimumSize(minimal_size.first, minimal_size.second); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 873985564f..4e30282155 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "common/thread.h" #include "core/core.h" @@ -139,6 +140,8 @@ public: void InitRenderTarget(); + void CaptureScreenshot(u16 res_scale, const QString& screenshot_path); + public slots: void moveContext(); // overridden @@ -165,6 +168,9 @@ private: EmuThread* emu_thread; + /// Temporary storage of the screenshot taken + QImage screenshot_image; + protected: void showEvent(QShowEvent* event) override; }; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index c261611693..02e09fa184 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -445,6 +445,8 @@ void Config::ReadValues() { UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); UISettings::values.enable_discord_presence = qt_config->value("enable_discord_presence", true).toBool(); + UISettings::values.screenshot_resolution_factor = + static_cast(qt_config->value("screenshot_resolution_factor", 0).toUInt()); qt_config->beginGroup("UIGameList"); UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); @@ -648,6 +650,8 @@ void Config::SaveValues() { qt_config->beginGroup("UI"); qt_config->setValue("theme", UISettings::values.theme); qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); + qt_config->setValue("screenshot_resolution_factor", + UISettings::values.screenshot_resolution_factor); qt_config->beginGroup("UIGameList"); qt_config->setValue("show_unknown", UISettings::values.show_unknown); @@ -669,6 +673,7 @@ void Config::SaveValues() { qt_config->beginGroup("Paths"); qt_config->setValue("romsPath", UISettings::values.roms_path); qt_config->setValue("symbolsPath", UISettings::values.symbols_path); + qt_config->setValue("screenshotPath", UISettings::values.screenshot_path); qt_config->setValue("gameListRootDir", UISettings::values.gamedir); qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan); qt_config->setValue("recentFiles", UISettings::values.recent_files); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 22c207a3af..808f14fb3d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -334,6 +334,9 @@ void GMainWindow::InitializeHotkeys() { Qt::ApplicationShortcut); hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2), Qt::ApplicationShortcut); + hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot", + QKeySequence(QKeySequence::Print)); + hotkey_registry.LoadHotkeys(); connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, @@ -393,6 +396,12 @@ void GMainWindow::InitializeHotkeys() { OnLoadAmiibo(); } }); + connect(hotkey_registry.GetHotkey("Main Window", "Capture Screenshot", this), + &QShortcut::activated, this, [&] { + if (emu_thread->IsRunning()) { + OnCaptureScreenshot(); + } + }); } void GMainWindow::SetDefaultUIGeometry() { @@ -488,6 +497,10 @@ void GMainWindow::ConnectMenuEvents() { hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key()); connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); + // Movie + connect(ui.action_Capture_Screenshot, &QAction::triggered, this, + &GMainWindow::OnCaptureScreenshot); + // Help connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder); connect(ui.action_Rederive, &QAction::triggered, this, @@ -724,6 +737,7 @@ void GMainWindow::ShutdownGame() { ui.action_Restart->setEnabled(false); ui.action_Report_Compatibility->setEnabled(false); ui.action_Load_Amiibo->setEnabled(false); + ui.action_Capture_Screenshot->setEnabled(false); render_window->hide(); game_list->show(); game_list->setFilterFocus(); @@ -1261,6 +1275,7 @@ void GMainWindow::OnStartGame() { discord_rpc->Update(); ui.action_Load_Amiibo->setEnabled(true); + ui.action_Capture_Screenshot->setEnabled(true); } void GMainWindow::OnPauseGame() { @@ -1269,6 +1284,7 @@ void GMainWindow::OnPauseGame() { ui.action_Start->setEnabled(true); ui.action_Pause->setEnabled(false); ui.action_Stop->setEnabled(true); + ui.action_Capture_Screenshot->setEnabled(false); } void GMainWindow::OnStopGame() { @@ -1431,6 +1447,18 @@ void GMainWindow::OnToggleFilterBar() { } } +void GMainWindow::OnCaptureScreenshot() { + OnPauseGame(); + const QString path = + QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), + UISettings::values.screenshot_path, tr("PNG Image (*.png)")); + if (!path.isEmpty()) { + UISettings::values.screenshot_path = QFileInfo(path).path(); + render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path); + } + OnStartGame(); +} + void GMainWindow::UpdateStatusBar() { if (emu_thread == nullptr) { status_bar_update_timer.stop(); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 674e734125..9a1df51682 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -186,6 +186,7 @@ private slots: void ShowFullscreen(); void HideFullscreen(); void ToggleWindowMode(); + void OnCaptureScreenshot(); void OnCoreError(Core::System::ResultStatus, std::string); void OnReinitializeKeys(ReinitializeKeyBehavior behavior); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 75e96387f6..ffcabb4954 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -101,11 +101,13 @@ - + Tools - + + + @@ -118,7 +120,7 @@ - + @@ -173,11 +175,11 @@ &Stop
- - - Reinitialize keys... - - + + + Reinitialize keys... + + About yuzu @@ -252,39 +254,47 @@ Fullscreen - - - false - - - Restart - - - - - false - - - Load Amiibo... - + + + false + + + Restart + - - - false - - - Report Compatibility - - - false - - - - - Open yuzu Folder - - - + + + false + + + Load Amiibo... + + + + + false + + + Report Compatibility + + + false + + + + + Open yuzu Folder + + + + + false + + + Capture Screenshot + + + diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index e80aebc0a2..b03bc2de60 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h @@ -10,6 +10,7 @@ #include #include #include +#include "common/common_types.h" namespace UISettings { @@ -42,8 +43,11 @@ struct Values { // Discord RPC bool enable_discord_presence; + u16 screenshot_resolution_factor; + QString roms_path; QString symbols_path; + QString screenshot_path; QString gamedir; bool gamedir_deepscan; QStringList recent_files; From 2a533f006706836f3229a6375be0bc398277c8c8 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 17 Dec 2018 23:04:35 -0500 Subject: [PATCH 27/74] service/sm: Improve debug log for RegisterService Now it also indicates the name and max session count. This also gives a name to the unknown bool. This indicates if the created port is supposed to be using light handles or regular handles internally. This is passed to the respective svcCreatePort parameter internally. --- src/core/hle/service/sm/sm.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index d735300860..142929124d 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -145,12 +145,13 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) { const std::string name(name_buf.begin(), end); - const auto unk_bool = static_cast(rp.PopRaw()); - const auto session_count = rp.PopRaw(); + const auto is_light = static_cast(rp.PopRaw()); + const auto max_session_count = rp.PopRaw(); - LOG_DEBUG(Service_SM, "called with unk_bool={}", unk_bool); + LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name, + max_session_count, is_light); - auto handle = service_manager->RegisterService(name, session_count); + auto handle = service_manager->RegisterService(name, max_session_count); if (handle.Failed()) { LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", handle.Code().raw); From fdd649e2ef56ea473e253511d35fe6c10e0fb241 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Wed, 19 Dec 2018 12:52:32 +1100 Subject: [PATCH 28/74] Fixed uninitialized memory due to missing returns in canary Functions which are suppose to crash on non canary builds usually don't return anything which lead to uninitialized memory being used. --- src/core/file_sys/savedata_factory.cpp | 1 + src/core/hle/kernel/object.cpp | 1 + src/core/memory.cpp | 1 + src/video_core/engines/maxwell_3d.h | 2 ++ src/video_core/engines/shader_bytecode.h | 2 ++ src/video_core/gpu.cpp | 2 ++ src/video_core/macro_interpreter.cpp | 2 ++ src/video_core/morton.cpp | 1 + src/video_core/renderer_opengl/gl_shader_cache.h | 1 + .../renderer_opengl/gl_shader_decompiler.cpp | 11 +++++++++-- src/video_core/renderer_opengl/renderer_opengl.cpp | 2 ++ src/video_core/surface.cpp | 7 +++++++ src/video_core/textures/decoders.cpp | 2 +- src/yuzu/debugger/graphics/graphics_surface.cpp | 1 + 14 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index bd50fedc7b..d63b7f19b3 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -128,6 +128,7 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ return fmt::format("{}save/cache/{:016X}", out, title_id); default: ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast(type)); + return fmt::format("{}save/unknown_{:X}/{:016X}", out, static_cast(type), title_id); } } diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp index 0ea851a74a..8060786385 100644 --- a/src/core/hle/kernel/object.cpp +++ b/src/core/hle/kernel/object.cpp @@ -32,6 +32,7 @@ bool Object::IsWaitable() const { } UNREACHABLE(); + return false; } } // namespace Kernel diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 643afdee87..e9166dbd9e 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -187,6 +187,7 @@ T Read(const VAddr vaddr) { default: UNREACHABLE(); } + return {}; } template diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 25bb7604a4..0faff6fdf9 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -164,6 +164,7 @@ public: return 3; default: UNREACHABLE(); + return 1; } } @@ -871,6 +872,7 @@ public: return 4; } UNREACHABLE(); + return 1; } GPUVAddr StartAddress() const { diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 2efeb6e1a5..eb703bb5af 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -1065,6 +1065,7 @@ union Instruction { LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}", static_cast(texture_info.Value())); UNREACHABLE(); + return TextureType::Texture1D; } TextureProcessMode GetTextureProcessMode() const { @@ -1145,6 +1146,7 @@ union Instruction { LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}", static_cast(texture_info.Value())); UNREACHABLE(); + return TextureType::Texture1D; } TextureProcessMode GetTextureProcessMode() const { diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 88c45a4231..08cf6268fc 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -102,6 +102,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { return 1; default: UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast(format)); + return 1; } } @@ -119,6 +120,7 @@ u32 DepthFormatBytesPerPixel(DepthFormat format) { return 2; default: UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast(format)); + return 1; } } diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index 9c55e9f1e1..64f75db436 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp @@ -171,6 +171,7 @@ u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) default: UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", static_cast(operation)); + return 0; } } @@ -268,6 +269,7 @@ bool MacroInterpreter::EvaluateBranchCondition(BranchCondition cond, u32 value) return value != 0; } UNREACHABLE(); + return true; } } // namespace Tegra diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp index a310491a89..47e76d8fee 100644 --- a/src/video_core/morton.cpp +++ b/src/video_core/morton.cpp @@ -192,6 +192,7 @@ static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFor return linear_to_morton_fns[static_cast(format)]; } UNREACHABLE(); + return morton_to_linear_fns[static_cast(format)]; } /// 8x8 Z-Order coordinate from 2D coordinates diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index b4ef6030d7..de3671acf1 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -67,6 +67,7 @@ public: 6, "ShaderTrianglesAdjacency"); default: UNREACHABLE_MSG("Unknown primitive mode."); + return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints"); } } diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index bd61af463c..836865e149 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -364,6 +364,7 @@ public: return value; default: UNREACHABLE_MSG("Unimplemented conversion size: {}", static_cast(size)); + return value; } } @@ -626,6 +627,7 @@ public: return "floatBitsToInt(" + value + ')'; } else { UNREACHABLE(); + return value; } } @@ -2064,6 +2066,8 @@ private: std::to_string(instr.alu.GetSignedImm20_20())}; default: UNREACHABLE(); + return {regs.GetRegisterAsInteger(instr.gpr39, 0, false), + std::to_string(instr.alu.GetSignedImm20_20())}; } }(); const std::string offset = '(' + packed_shift + " & 0xff)"; @@ -3314,6 +3318,7 @@ private: return std::to_string(instr.r2p.immediate_mask); default: UNREACHABLE(); + return std::to_string(instr.r2p.immediate_mask); } }(); const std::string mask = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + @@ -3777,7 +3782,9 @@ private: } break; } - default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); } + default: { + UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); + } } break; @@ -3932,4 +3939,4 @@ std::optional DecompileProgram(const ProgramCode& program_code, u return {}; } -} // namespace OpenGL::GLShader::Decompiler \ No newline at end of file +} // namespace OpenGL::GLShader::Decompiler diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 4fd0d66c55..f024151399 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -427,6 +427,7 @@ static const char* GetSource(GLenum source) { RET(OTHER); default: UNREACHABLE(); + return "Unknown source"; } #undef RET } @@ -445,6 +446,7 @@ static const char* GetType(GLenum type) { RET(MARKER); default: UNREACHABLE(); + return "Unknown type"; } #undef RET } diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 9582dd2ca1..a97b1562b5 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -65,6 +65,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { default: LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast(format)); UNREACHABLE(); + return PixelFormat::S8Z24; } } @@ -141,6 +142,7 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) default: LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast(format)); UNREACHABLE(); + return PixelFormat::RGBA8_SRGB; } } @@ -327,6 +329,7 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast(format), static_cast(component_type)); UNREACHABLE(); + return PixelFormat::ABGR8U; } } @@ -346,6 +349,7 @@ ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { default: LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast(type)); UNREACHABLE(); + return ComponentType::UNorm; } } @@ -393,6 +397,7 @@ ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) { default: LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast(format)); UNREACHABLE(); + return ComponentType::UNorm; } } @@ -403,6 +408,7 @@ PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat default: LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast(format)); UNREACHABLE(); + return PixelFormat::ABGR8U; } } @@ -418,6 +424,7 @@ ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) { default: LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast(format)); UNREACHABLE(); + return ComponentType::UNorm; } } diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index bbae9285f4..5db75de22c 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -226,7 +226,7 @@ u32 BytesPerPixel(TextureFormat format) { return 8; default: UNIMPLEMENTED_MSG("Format not implemented"); - break; + return 1; } } diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index 7077474228..2097985211 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp @@ -30,6 +30,7 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat( return Tegra::Texture::TextureFormat::A2B10G10R10; default: UNIMPLEMENTED_MSG("Unimplemented RT format"); + return Tegra::Texture::TextureFormat::A8R8G8B8; } } From 20859802f01e64e4407df8bf7fb362449bd68cee Mon Sep 17 00:00:00 2001 From: David Marcec Date: Wed, 19 Dec 2018 13:22:09 +1100 Subject: [PATCH 29/74] hopefully fix clang format issue --- src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 836865e149..68c5913160 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -3784,6 +3784,7 @@ private: } default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); + break; } } From 807e7640aa27dbe3ffca45f2c7da5cc14e07106e Mon Sep 17 00:00:00 2001 From: David Marcec Date: Wed, 19 Dec 2018 14:16:30 +1100 Subject: [PATCH 30/74] Device handle should not be a random id, instead it's the current npad id Found during hardware testing --- src/core/hle/service/nfp/nfp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index d5df112a06..a7bed00400 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -317,8 +317,8 @@ private: } bool has_attached_handle{}; - const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')}; - const u32 npad_id{0}; // Player 1 controller + const u64 device_handle{0}; // Npad device 1 + const u32 npad_id{0}; // Player 1 controller State state{State::NonInitialized}; DeviceState device_state{DeviceState::Initialized}; Kernel::EventPair deactivate_event; From 9b3a38e3d331b2fb647cd7286dad51d7051bdf64 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Dec 2018 22:16:53 -0500 Subject: [PATCH 31/74] kernel/process: Make process_id a 64-bit value In the actual kernel, this is a 64-bit value, so we shouldn't be using a 32-bit type to handle it. --- src/core/hle/kernel/kernel.cpp | 4 ++-- src/core/hle/kernel/kernel.h | 2 +- src/core/hle/kernel/process.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e441c5bc6a..a221734c10 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -155,7 +155,7 @@ struct KernelCore::Impl { std::atomic next_object_id{0}; // TODO(Subv): Start the process ids from 10 for now, as lower PIDs are // reserved for low-level services - std::atomic next_process_id{10}; + std::atomic next_process_id{10}; std::atomic next_thread_id{1}; // Lists all processes that exist in the current session. @@ -246,7 +246,7 @@ u32 KernelCore::CreateNewThreadID() { return impl->next_thread_id++; } -u32 KernelCore::CreateNewProcessID() { +u64 KernelCore::CreateNewProcessID() { return impl->next_process_id++; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index ea00c89f59..4f0f2331c8 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -88,7 +88,7 @@ private: u32 CreateNewObjectID(); /// Creates a new process ID, incrementing the internal process ID counter; - u32 CreateNewProcessID(); + u64 CreateNewProcessID(); /// Creates a new thread ID, incrementing the internal thread ID counter. u32 CreateNewThreadID(); diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 459eedfa6d..725bfa01a4 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -162,7 +162,7 @@ public: } /// Gets the unique ID that identifies this particular process. - u32 GetProcessID() const { + u64 GetProcessID() const { return process_id; } @@ -288,10 +288,10 @@ private: ProcessStatus status; /// The ID of this process - u32 process_id = 0; + u64 process_id = 0; /// Title ID corresponding to the process - u64 program_id; + u64 program_id = 0; /// Resource limit descriptor for this process SharedPtr resource_limit; From 43e1189688a948e167ade54fdf2ba4007289aefd Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Dec 2018 22:30:53 -0500 Subject: [PATCH 32/74] kernel/svc: Correct output parameter for svcGetProcessId svcGetProcessId's out parameter is a pointer to a 64-bit value, not a 32-bit one. --- src/core/hle/kernel/svc.cpp | 2 +- src/core/hle/kernel/svc_wrap.h | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 348a229047..c8b60b16c1 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -365,7 +365,7 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { } /// Get the ID of the specified process -static ResultCode GetProcessId(u32* process_id, Handle process_handle) { +static ResultCode GetProcessId(u64* process_id, Handle process_handle) { LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 2f758b959b..2a2c2c5ead 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -73,7 +73,15 @@ void SvcWrap() { template void SvcWrap() { u32 param_1 = 0; - u32 retval = func(¶m_1, Param(1)).raw; + const u32 retval = func(¶m_1, Param(1)).raw; + Core::CurrentArmInterface().SetReg(1, param_1); + FuncReturn(retval); +} + +template +void SvcWrap() { + u64 param_1 = 0; + const u32 retval = func(¶m_1, static_cast(Param(1))).raw; Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } From 8435451093b193c1a1556a9edadc5663d3372b02 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Dec 2018 22:37:01 -0500 Subject: [PATCH 33/74] kernel/thread: Make thread_id a 64-bit value The kernel uses a 64-bit value for the thread ID, so we shouldn't be using a 32-bit value. --- src/core/gdbstub/gdbstub.cpp | 4 ++-- src/core/hle/kernel/kernel.cpp | 4 ++-- src/core/hle/kernel/kernel.h | 2 +- src/core/hle/kernel/thread.h | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index e6b5171eed..a1cad4fcb4 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -201,11 +201,11 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) { modules.push_back(std::move(module)); } -static Kernel::Thread* FindThreadById(int id) { +static Kernel::Thread* FindThreadById(s64 id) { for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList(); for (auto& thread : threads) { - if (thread->GetThreadID() == static_cast(id)) { + if (thread->GetThreadID() == static_cast(id)) { current_core = core; return thread.get(); } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index a221734c10..2be39fb52b 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -156,7 +156,7 @@ struct KernelCore::Impl { // TODO(Subv): Start the process ids from 10 for now, as lower PIDs are // reserved for low-level services std::atomic next_process_id{10}; - std::atomic next_thread_id{1}; + std::atomic next_thread_id{1}; // Lists all processes that exist in the current session. std::vector> process_list; @@ -242,7 +242,7 @@ u32 KernelCore::CreateNewObjectID() { return impl->next_object_id++; } -u32 KernelCore::CreateNewThreadID() { +u64 KernelCore::CreateNewThreadID() { return impl->next_thread_id++; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 4f0f2331c8..58c9d108b9 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -91,7 +91,7 @@ private: u64 CreateNewProcessID(); /// Creates a new thread ID, incrementing the internal thread ID counter. - u32 CreateNewThreadID(); + u64 CreateNewThreadID(); /// Creates a timer callback handle for the given timer. ResultVal CreateTimerCallbackHandle(const SharedPtr& timer); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 77aec099ae..d6e7981d3c 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -151,7 +151,7 @@ public: * Gets the thread's thread ID * @return The thread's ID */ - u32 GetThreadID() const { + u64 GetThreadID() const { return thread_id; } @@ -379,7 +379,7 @@ private: Core::ARM_Interface::ThreadContext context{}; - u32 thread_id = 0; + u64 thread_id = 0; ThreadStatus status = ThreadStatus::Dormant; From 0906302ca92332c8928c3a896e66d85d229fadb9 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Dec 2018 22:38:22 -0500 Subject: [PATCH 34/74] kernel/svc: Correct output parameter for svcGetThreadId The service call uses a 64-bit value, just like svcGetProcessId. This amends the function signature accordingly. --- src/core/hle/kernel/svc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index c8b60b16c1..bcc9864f3f 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -350,7 +350,7 @@ static ResultCode SendSyncRequest(Handle handle) { } /// Get the ID for the specified thread. -static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { +static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); From 62d437705367421a1b919922f1ecf3c4a43d75c5 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Dec 2018 22:53:58 -0500 Subject: [PATCH 35/74] kernel/kernel: Use correct initial PID for userland Process instances Starts the process ID counter off at 81, which is what the kernel itself checks against internally when creating processes. It's actually supposed to panic if the PID is less than 81 for a userland process. --- src/core/hle/kernel/kernel.cpp | 6 ++---- src/core/hle/kernel/process.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 2be39fb52b..1c2290651e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -112,7 +112,7 @@ struct KernelCore::Impl { void Shutdown() { next_object_id = 0; - next_process_id = 10; + next_process_id = Process::ProcessIDMin; next_thread_id = 1; process_list.clear(); @@ -153,9 +153,7 @@ struct KernelCore::Impl { } std::atomic next_object_id{0}; - // TODO(Subv): Start the process ids from 10 for now, as lower PIDs are - // reserved for low-level services - std::atomic next_process_id{10}; + std::atomic next_process_id{Process::ProcessIDMin}; std::atomic next_thread_id{1}; // Lists all processes that exist in the current session. diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 725bfa01a4..7da367251f 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -120,6 +120,18 @@ struct CodeSet final { class Process final : public WaitObject { public: + 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, + }; + static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; static SharedPtr Create(KernelCore& kernel, std::string&& name); From 603cc72168b2165a0aa77a39a14ca63a879cf8f9 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sat, 15 Dec 2018 13:49:40 -0500 Subject: [PATCH 36/74] vm_manager: Add member function for checking a memory range adheres to certain attributes, permissions and states --- src/core/hle/kernel/vm_manager.cpp | 60 ++++++++++++++++++++++++++++++ src/core/hle/kernel/vm_manager.h | 40 ++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 252f92df2e..02504d750c 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -592,6 +592,66 @@ void VMManager::ClearPageTable() { Memory::PageType::Unmapped); } +VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask, + MemoryState state, VMAPermission permission_mask, + VMAPermission permissions, + MemoryAttribute attribute_mask, + MemoryAttribute attribute, + MemoryAttribute ignore_mask) const { + auto iter = FindVMA(address); + + // If we don't have a valid VMA handle at this point, then it means this is + // being called with an address outside of the address space, which is definitely + // indicative of a bug, as this function only operates on mapped memory regions. + DEBUG_ASSERT(IsValidHandle(iter)); + + const VAddr end_address = address + size - 1; + const MemoryAttribute initial_attributes = iter->second.attribute; + const VMAPermission initial_permissions = iter->second.permissions; + const MemoryState initial_state = iter->second.state; + + while (true) { + // The iterator should be valid throughout the traversal. Hitting the end of + // the mapped VMA regions is unquestionably indicative of a bug. + DEBUG_ASSERT(IsValidHandle(iter)); + + const auto& vma = iter->second; + + if (vma.state != initial_state) { + return ERR_INVALID_ADDRESS_STATE; + } + + if ((vma.state & state_mask) != state) { + return ERR_INVALID_ADDRESS_STATE; + } + + if (vma.permissions != initial_permissions) { + return ERR_INVALID_ADDRESS_STATE; + } + + if ((vma.permissions & permission_mask) != permissions) { + return ERR_INVALID_ADDRESS_STATE; + } + + if ((vma.attribute | ignore_mask) != (initial_attributes | ignore_mask)) { + return ERR_INVALID_ADDRESS_STATE; + } + + if ((vma.attribute & attribute_mask) != attribute) { + return ERR_INVALID_ADDRESS_STATE; + } + + if (end_address <= vma.EndAddress()) { + break; + } + + ++iter; + } + + return MakeResult( + std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask)); +} + u64 VMManager::GetTotalMemoryUsage() const { LOG_WARNING(Kernel, "(STUBBED) called"); return 0xF8000000; diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index e2614cd4c9..9fa9a18fb5 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "common/common_types.h" #include "core/hle/result.h" @@ -256,6 +257,16 @@ struct PageInfo { * also backed by a single host memory allocation. */ struct VirtualMemoryArea { + /// Gets the starting (base) address of this VMA. + VAddr StartAddress() const { + return base; + } + + /// Gets the ending address of this VMA. + VAddr EndAddress() const { + return base + size - 1; + } + /// Virtual base address of the region. VAddr base = 0; /// Size of the region. @@ -517,6 +528,35 @@ private: /// Clears out the page table void ClearPageTable(); + using CheckResults = ResultVal>; + + /// Checks if an address range adheres to the specified states provided. + /// + /// @param address The starting address of the address range. + /// @param size The size of the address range. + /// @param state_mask The memory state mask. + /// @param state The state to compare the individual VMA states against, + /// which is done in the form of: (vma.state & state_mask) != state. + /// @param permission_mask The memory permissions mask. + /// @param permissions The permission to compare the individual VMA permissions against, + /// which is done in the form of: + /// (vma.permission & permission_mask) != permission. + /// @param attribute_mask The memory attribute mask. + /// @param attribute The memory attributes to compare the individual VMA attributes + /// against, which is done in the form of: + /// (vma.attributes & attribute_mask) != attribute. + /// @param ignore_mask The memory attributes to ignore during the check. + /// + /// @returns If successful, returns a tuple containing the memory attributes + /// (with ignored bits specified by ignore_mask unset), memory permissions, and + /// memory state across the memory range. + /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE. + /// + CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state, + VMAPermission permission_mask, VMAPermission permissions, + MemoryAttribute attribute_mask, MemoryAttribute attribute, + MemoryAttribute ignore_mask) const; + /** * A map covering the entirety of the managed address space, keyed by the `base` field of each * VMA. It must always be modified by splitting or merging VMAs, so that the invariant From 622242e3451fd562425f67317f3f0d7855eb5741 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sat, 15 Dec 2018 14:29:39 -0500 Subject: [PATCH 37/74] vm_manager: Add member function for setting memory attributes across an address range This puts the backing functionality for svcSetMemoryAttribute in place, which will be utilized in a following change. --- src/core/hle/kernel/vm_manager.cpp | 28 ++++++++++++++++++++++++++++ src/core/hle/kernel/vm_manager.h | 13 +++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 02504d750c..f39e096ca4 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -322,6 +322,34 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const { return memory_info; } +ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, + MemoryAttribute attribute) { + constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped; + constexpr auto attribute_mask = ~ignore_mask; + + const auto result = CheckRangeState( + address, size, MemoryState::FlagUncached, MemoryState::FlagUncached, VMAPermission::None, + VMAPermission::None, attribute_mask, MemoryAttribute::None, ignore_mask); + + if (result.Failed()) { + return result.Code(); + } + + const auto [prev_state, prev_permissions, prev_attributes] = *result; + const auto new_attribute = (prev_attributes & ~mask) | (mask & attribute); + + const auto carve_result = CarveVMARange(address, size); + if (carve_result.Failed()) { + return carve_result.Code(); + } + + auto vma_iter = *carve_result; + vma_iter->second.attribute = new_attribute; + + MergeAdjacent(vma_iter); + return RESULT_SUCCESS; +} + ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { const auto vma = FindVMA(src_addr); diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 9fa9a18fb5..6091533bca 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -392,6 +392,19 @@ public: /// MemoryInfo QueryMemory(VAddr address) const; + /// Sets an attribute across the given address range. + /// + /// @param address The starting address + /// @param size The size of the range to set the attribute on. + /// @param mask The attribute mask + /// @param attribute The attribute to set across the given address range + /// + /// @returns RESULT_SUCCESS if successful + /// @returns ERR_INVALID_ADDRESS_STATE if the attribute could not be set. + /// + ResultCode SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, + MemoryAttribute attribute); + /** * Scans all VMAs and updates the page table range of any that use the given vector as backing * memory. This should be called after any operation that causes reallocation of the vector. From caab838bdbdb08ba7a41a85a77f00432621666d2 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sat, 15 Dec 2018 15:21:41 -0500 Subject: [PATCH 38/74] svc: Implement svcSetMemoryAttribute With all the basic backing functionality implemented, we can now unstub svcSetMemoryAttribute. --- src/core/hle/kernel/svc.cpp | 51 +++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 348a229047..c826dfd967 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -254,11 +254,52 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { return vm_manager.ReprotectRange(addr, size, converted_permissions); } -static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) { - LOG_WARNING(Kernel_SVC, - "(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr, - size, state0, state1); - return RESULT_SUCCESS; +static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) { + LOG_DEBUG(Kernel_SVC, + "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, + size, mask, attribute); + + if (!Common::Is4KBAligned(address)) { + LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address); + return ERR_INVALID_ADDRESS; + } + + if (size == 0 || !Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.", + size); + return ERR_INVALID_ADDRESS; + } + + if (!IsValidAddressRange(address, size)) { + LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})", + address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto mem_attribute = static_cast(attribute); + const auto mem_mask = static_cast(mask); + const auto attribute_with_mask = mem_attribute | mem_mask; + + if (attribute_with_mask != mem_mask) { + LOG_ERROR(Kernel_SVC, + "Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}", + attribute, mask); + return ERR_INVALID_COMBINATION; + } + + if ((attribute_with_mask | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) { + LOG_ERROR(Kernel_SVC, "Specified attribute isn't equal to MemoryAttributeUncached (8)."); + return ERR_INVALID_COMBINATION; + } + + auto& vm_manager = Core::CurrentProcess()->VMManager(); + if (!IsInsideAddressSpace(vm_manager, address, size)) { + LOG_ERROR(Kernel_SVC, + "Given address (0x{:016X}) is outside the bounds of the address space.", address); + return ERR_INVALID_ADDRESS_STATE; + } + + return vm_manager.SetMemoryAttribute(address, size, mem_mask, mem_attribute); } /// Maps a memory range into a different range. From b74eb88c68e85d08695667eaf4603bb565c8eb64 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 18 Dec 2018 23:09:08 -0500 Subject: [PATCH 39/74] kernel/svc: Handle thread handles within GetProcessId If a thread handle is passed to svcGetProcessId, the kernel attempts to access the process ID via the thread's instance's owning process. Technically, this function should also be handling the kernel debug objects as well, however we currently don't handle those kernel objects yet, so I've left a note via a comment about it to remind myself when implementing it in the future. --- src/core/hle/kernel/svc.cpp | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index bcc9864f3f..0303330776 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -364,20 +364,33 @@ static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) { return RESULT_SUCCESS; } -/// Get the ID of the specified process -static ResultCode GetProcessId(u64* process_id, Handle process_handle) { - LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); +/// Gets the ID of the specified process or a specified thread's owning process. +static ResultCode GetProcessId(u64* process_id, Handle handle) { + LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); - const SharedPtr process = handle_table.Get(process_handle); - if (!process) { - LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", - process_handle); - return ERR_INVALID_HANDLE; + const SharedPtr process = handle_table.Get(handle); + if (process) { + *process_id = process->GetProcessID(); + return RESULT_SUCCESS; } - *process_id = process->GetProcessID(); - return RESULT_SUCCESS; + const SharedPtr thread = handle_table.Get(handle); + if (thread) { + const Process* const owner_process = thread->GetOwnerProcess(); + if (!owner_process) { + LOG_ERROR(Kernel_SVC, "Non-existent owning process encountered."); + return ERR_INVALID_HANDLE; + } + + *process_id = owner_process->GetProcessID(); + return RESULT_SUCCESS; + } + + // NOTE: This should also handle debug objects before returning. + + LOG_ERROR(Kernel_SVC, "Handle does not exist, handle=0x{:08X}", handle); + return ERR_INVALID_HANDLE; } /// Default thread wakeup callback for WaitSynchronization From fc8da2d5e36b665dae784ee8e9915dfffb379830 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 19 Dec 2018 15:25:12 -0500 Subject: [PATCH 40/74] common: Add basic bit manipulation utility function to Common --- src/common/CMakeLists.txt | 1 + src/common/bit_util.h | 61 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/common/bit_util.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index a5e71d8791..845626fc5b 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -44,6 +44,7 @@ add_library(common STATIC detached_tasks.cpp detached_tasks.h bit_field.h + bit_util.h cityhash.cpp cityhash.h color.h diff --git a/src/common/bit_util.h b/src/common/bit_util.h new file mode 100644 index 0000000000..1eea17ba13 --- /dev/null +++ b/src/common/bit_util.h @@ -0,0 +1,61 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#ifdef _MSC_VER +#include +#endif + +#include "common/common_types.h" + +namespace Common { + +/// Gets the size of a specified type T in bits. +template +constexpr std::size_t BitSize() { + return sizeof(T) * CHAR_BIT; +} + +#ifdef _MSC_VER +inline u32 CountLeadingZeroes32(u32 value) { + unsigned long leading_zero = 0; + + if (_BitScanReverse(&leading_zero, value) != 0) { + return 31 - leading_zero; + } + + return 32; +} + +inline u64 CountLeadingZeroes64(u64 value) { + unsigned long leading_zero = 0; + + if (_BitScanReverse64(&leading_zero, value) != 0) { + return 63 - leading_zero; + } + + return 64; +} +#else +inline u32 CountLeadingZeroes32(u32 value) { + if (value == 0) { + return 32; + } + + return __builtin_clz(value); +} + +inline u64 CountLeadingZeroes64(u64 value) { + if (value == 0) { + return 64; + } + + return __builtin_clzll(value); +} +#endif +} // namespace Common From 6ff5135521a59cd510ba3ecc8d5ff5d56cdbf08e Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 19 Dec 2018 12:57:47 -0500 Subject: [PATCH 41/74] kernel/process: Introduce process capability parsing skeleton We've had the old kernel capability parser from Citra, however, this is unused code and doesn't actually map to how the kernel on the Switch does it. This introduces the basic functional skeleton for parsing process capabilities. --- src/core/CMakeLists.txt | 2 + src/core/hle/kernel/errors.h | 1 + src/core/hle/kernel/handle_table.h | 6 +- src/core/hle/kernel/process_capability.cpp | 253 +++++++++++++++++++++ src/core/hle/kernel/process_capability.h | 209 +++++++++++++++++ 5 files changed, 468 insertions(+), 3 deletions(-) create mode 100644 src/core/hle/kernel/process_capability.cpp create mode 100644 src/core/hle/kernel/process_capability.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 882c9ab59e..a7d5e44315 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -113,6 +113,8 @@ add_library(core STATIC hle/kernel/object.h hle/kernel/process.cpp hle/kernel/process.h + hle/kernel/process_capability.cpp + hle/kernel/process_capability.h hle/kernel/readable_event.cpp hle/kernel/readable_event.h hle/kernel/resource_limit.cpp diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 8b58d701d6..a3d7258660 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -11,6 +11,7 @@ namespace Kernel { // Confirmed Switch kernel error codes constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; +constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index 6b7927fd83..89a3bc7403 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h @@ -43,6 +43,9 @@ enum KernelHandle : Handle { */ class HandleTable final : NonCopyable { public: + /// This is the maximum limit of handles allowed per process in Horizon + static constexpr std::size_t MAX_COUNT = 1024; + HandleTable(); ~HandleTable(); @@ -91,9 +94,6 @@ public: void Clear(); private: - /// This is the maximum limit of handles allowed per process in Horizon - static constexpr std::size_t MAX_COUNT = 1024; - /// Stores the Object referenced by the handle or null if the slot is empty. std::array, MAX_COUNT> objects; diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp new file mode 100644 index 0000000000..8d787547b6 --- /dev/null +++ b/src/core/hle/kernel/process_capability.cpp @@ -0,0 +1,253 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/bit_util.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/process_capability.h" +#include "core/hle/kernel/vm_manager.h" + +namespace Kernel { +namespace { + +// clang-format off + +// Shift offsets for kernel capability types. +enum : u32 { + CapabilityOffset_PriorityAndCoreNum = 3, + CapabilityOffset_Syscall = 4, + CapabilityOffset_MapPhysical = 6, + CapabilityOffset_MapIO = 7, + CapabilityOffset_Interrupt = 11, + CapabilityOffset_ProgramType = 13, + CapabilityOffset_KernelVersion = 14, + CapabilityOffset_HandleTableSize = 15, + CapabilityOffset_Debug = 16, +}; + +// Combined mask of all parameters that may be initialized only once. +constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) | + (1U << CapabilityOffset_ProgramType) | + (1U << CapabilityOffset_KernelVersion) | + (1U << CapabilityOffset_HandleTableSize) | + (1U << CapabilityOffset_Debug); + +// Packed kernel version indicating 10.4.0 +constexpr u32 PackedKernelVersion = 0x520000; + +// Indicates possible types of capabilities that can be specified. +enum class CapabilityType : u32 { + Unset = 0U, + PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1, + Syscall = (1U << CapabilityOffset_Syscall) - 1, + MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1, + MapIO = (1U << CapabilityOffset_MapIO) - 1, + Interrupt = (1U << CapabilityOffset_Interrupt) - 1, + ProgramType = (1U << CapabilityOffset_ProgramType) - 1, + KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1, + HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1, + Debug = (1U << CapabilityOffset_Debug) - 1, + Ignorable = 0xFFFFFFFFU, +}; + +// clang-format on + +constexpr CapabilityType GetCapabilityType(u32 value) { + return static_cast((~value & (value + 1)) - 1); +} + +u32 GetFlagBitOffset(CapabilityType type) { + const auto value = static_cast(type); + return static_cast(Common::BitSize() - Common::CountLeadingZeroes32(value)); +} + +} // Anonymous namespace + +ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities, + std::size_t num_capabilities, + VMManager& vm_manager) { + Clear(); + + // Allow all cores and priorities. + core_mask = 0xF; + priority_mask = 0xFFFFFFFFFFFFFFFF; + kernel_version = PackedKernelVersion; + + return ParseCapabilities(capabilities, num_capabilities, vm_manager); +} + +ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities, + std::size_t num_capabilities, + VMManager& vm_manager) { + Clear(); + + return ParseCapabilities(capabilities, num_capabilities, vm_manager); +} + +void ProcessCapabilities::InitializeForMetadatalessProcess() { + // Allow all cores and priorities + core_mask = 0xF; + priority_mask = 0xFFFFFFFFFFFFFFFF; + kernel_version = PackedKernelVersion; + + // Allow all system calls and interrupts. + svc_capabilities.set(); + interrupt_capabilities.set(); + + // Allow using the maximum possible amount of handles + handle_table_size = static_cast(HandleTable::MAX_COUNT); + + // Allow all debugging capabilities. + is_debuggable = true; + can_force_debug = true; +} + +ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities, + std::size_t num_capabilities, + VMManager& vm_manager) { + u32 set_flags = 0; + u32 set_svc_bits = 0; + + for (std::size_t i = 0; i < num_capabilities; ++i) { + const u32 descriptor = capabilities[i]; + const auto type = GetCapabilityType(descriptor); + + if (type == CapabilityType::MapPhysical) { + i++; + + // The MapPhysical type uses two descriptor flags for its parameters. + // If there's only one, then there's a problem. + if (i >= num_capabilities) { + return ERR_INVALID_COMBINATION; + } + + const auto size_flags = capabilities[i]; + if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) { + return ERR_INVALID_COMBINATION; + } + + const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager); + if (result.IsError()) { + return result; + } + } else { + const auto result = + ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager); + if (result.IsError()) { + return result; + } + } + } + + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, + u32 flag, VMManager& vm_manager) { + const auto type = GetCapabilityType(flag); + + if (type == CapabilityType::Unset) { + return ERR_INVALID_CAPABILITY_DESCRIPTOR; + } + + // Bail early on ignorable entries, as one would expect, + // ignorable descriptors can be ignored. + if (type == CapabilityType::Ignorable) { + return RESULT_SUCCESS; + } + + // Ensure that the give flag hasn't already been initialized before. + // If it has been, then bail. + const u32 flag_length = GetFlagBitOffset(type); + const u32 set_flag = 1U << flag_length; + if ((set_flag & set_flags & InitializeOnceMask) != 0) { + return ERR_INVALID_COMBINATION; + } + set_flags |= set_flag; + + switch (type) { + case CapabilityType::PriorityAndCoreNum: + return HandlePriorityCoreNumFlags(flag); + case CapabilityType::Syscall: + return HandleSyscallFlags(set_svc_bits, flag); + case CapabilityType::MapIO: + return HandleMapIOFlags(flag, vm_manager); + case CapabilityType::Interrupt: + return HandleInterruptFlags(flag); + case CapabilityType::ProgramType: + return HandleProgramTypeFlags(flag); + case CapabilityType::KernelVersion: + return HandleKernelVersionFlags(flag); + case CapabilityType::HandleTableSize: + return HandleHandleTableFlags(flag); + case CapabilityType::Debug: + return HandleDebugFlags(flag); + default: + break; + } + + return ERR_INVALID_CAPABILITY_DESCRIPTOR; +} + +void ProcessCapabilities::Clear() { + svc_capabilities.reset(); + interrupt_capabilities.reset(); + + core_mask = 0; + priority_mask = 0; + + handle_table_size = 0; + kernel_version = 0; + + is_debuggable = false; + can_force_debug = false; +} + +ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { + // TODO: Implement + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { + // TODO: Implement + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, + VMManager& vm_manager) { + // TODO(Lioncache): Implement once the memory manager can handle this. + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) { + // TODO(Lioncache): Implement once the memory manager can handle this. + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) { + // TODO: Implement + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { + // TODO: Implement + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { + // TODO: Implement + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { + // TODO: Implement + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) { + // TODO: Implement + return RESULT_SUCCESS; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h new file mode 100644 index 0000000000..5cff104764 --- /dev/null +++ b/src/core/hle/kernel/process_capability.h @@ -0,0 +1,209 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/common_types.h" + +union ResultCode; + +namespace Kernel { + +class VMManager; + +/// Handles kernel capability descriptors that are provided by +/// application metadata. These descriptors provide information +/// that alters certain parameters for kernel process instance +/// that will run said application (or applet). +/// +/// Capabilities are a sequence of flag descriptors, that indicate various +/// configurations and constraints for a particular process. +/// +/// Flag types are indicated by a sequence of set low bits. E.g. the +/// types are indicated with the low bits as follows (where x indicates "don't care"): +/// +/// - Priority and core mask : 0bxxxxxxxxxxxx0111 +/// - Allowed service call mask: 0bxxxxxxxxxxx01111 +/// - Map physical memory : 0bxxxxxxxxx0111111 +/// - Map IO memory : 0bxxxxxxxx01111111 +/// - Interrupts : 0bxxxx011111111111 +/// - Application type : 0bxx01111111111111 +/// - Kernel version : 0bx011111111111111 +/// - Handle table size : 0b0111111111111111 +/// - Debugger flags : 0b1111111111111111 +/// +/// These are essentially a bit offset subtracted by 1 to create a mask. +/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000) +/// subtracted by one (7 -> 0b0111) +/// +/// An example of a bit layout (using the map physical layout): +/// +/// The MapPhysical type indicates a sequence entry pair of: +/// +/// [initial, memory_flags], where: +/// +/// initial: +/// bits: +/// 7-24: Starting page to map memory at. +/// 25 : Indicates if the memory should be mapped as read only. +/// +/// memory_flags: +/// bits: +/// 7-20 : Number of pages to map +/// 21-25: Seems to be reserved (still checked against though) +/// 26 : Whether or not the memory being mapped is IO memory, or physical memory +/// +/// +class ProcessCapabilities { +public: + using InterruptCapabilities = std::bitset<1024>; + using SyscallCapabilities = std::bitset<128>; + + ProcessCapabilities() = default; + ProcessCapabilities(const ProcessCapabilities&) = delete; + ProcessCapabilities(ProcessCapabilities&&) = default; + + ProcessCapabilities& operator=(const ProcessCapabilities&) = delete; + ProcessCapabilities& operator=(ProcessCapabilities&&) = default; + + /// Initializes this process capabilities instance for a kernel process. + /// + /// @param capabilities The capabilities to parse + /// @param num_capabilities The number of capabilities to parse. + /// @param vm_manager The memory manager to use for handling any mapping-related + /// operations (such as mapping IO memory, etc). + /// + /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized, + /// otherwise, an error code upon failure. + /// + ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, + VMManager& vm_manager); + + /// Initializes this process capabilities instance for a userland process. + /// + /// @param capabilities The capabilities to parse. + /// @param num_capabilities The total number of capabilities to parse. + /// @param vm_manager The memory manager to use for handling any mapping-related + /// operations (such as mapping IO memory, etc). + /// + /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized, + /// otherwise, an error code upon failure. + /// + ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, + VMManager& vm_manager); + + /// Initializes this process capabilities instance for a process that does not + /// have any metadata to parse. + /// + /// This is necessary, as we allow running raw executables, and the internal + /// kernel process capabilities also determine what CPU cores the process is + /// allowed to run on, and what priorities are allowed for threads. It also + /// determines the max handle table size, what the program type is, whether or + /// not the process can be debugged, or whether it's possible for a process to + /// forcibly debug another process. + /// + /// Given the above, this essentially enables all capabilities across the board + /// for the process. It allows the process to: + /// + /// - Run on any core + /// - Use any thread priority + /// - Use the maximum amount of handles a process is allowed to. + /// - Be debuggable + /// - Forcibly debug other processes. + /// + /// Note that this is not a behavior that the kernel allows a process to do via + /// a single function like this. This is yuzu-specific behavior to handle + /// executables with no capability descriptors whatsoever to derive behavior from. + /// It being yuzu-specific is why this is also not the default behavior and not + /// done by default in the constructor. + /// + void InitializeForMetadatalessProcess(); + +private: + /// Attempts to parse a given sequence of capability descriptors. + /// + /// @param capabilities The sequence of capability descriptors to parse. + /// @param num_capabilities The number of descriptors within the given sequence. + /// @param vm_manager The memory manager that will perform any memory + /// mapping if necessary. + /// + /// @return RESULT_SUCCESS if no errors occur, otherwise an error code. + /// + ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, + VMManager& vm_manager); + + /// Attempts to parse a capability descriptor that is only represented by a + /// single flag set. + /// + /// @param set_flags Running set of flags that are used to catch + /// flags being initialized more than once when they shouldn't be. + /// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask. + /// @param flag The flag to attempt to parse. + /// @param vm_manager The memory manager that will perform any memory + /// mapping if necessary. + /// + /// @return RESULT_SUCCESS if no errors occurred, otherwise an error code. + /// + ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, + VMManager& vm_manager); + + /// Clears the internal state of this process capability instance. Necessary, + /// to have a sane starting point due to us allowing running executables without + /// configuration metadata. We assume a process is not going to have metadata, + /// and if it turns out that the process does, in fact, have metadata, then + /// we attempt to parse it. Thus, we need this to reset data members back to + /// a good state. + /// + /// DO NOT ever make this a public member function. This isn't an invariant + /// anything external should depend upon (and if anything comes to rely on it, + /// you should immediately be questioning the design of that thing, not this + /// class. If the kernel itself can run without depending on behavior like that, + /// then so can yuzu). + /// + void Clear(); + + /// Handles flags related to the priority and core number capability flags. + ResultCode HandlePriorityCoreNumFlags(u32 flags); + + /// Handles flags related to determining the allowable SVC mask. + ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags); + + /// Handles flags related to mapping physical memory pages. + ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager); + + /// Handles flags related to mapping IO pages. + ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager); + + /// Handles flags related to the interrupt capability flags. + ResultCode HandleInterruptFlags(u32 flags); + + /// Handles flags related to the program type. + ResultCode HandleProgramTypeFlags(u32 flags); + + /// Handles flags related to the handle table size. + ResultCode HandleHandleTableFlags(u32 flags); + + /// Handles flags related to the kernel version capability flags. + ResultCode HandleKernelVersionFlags(u32 flags); + + /// Handles flags related to debug-specific capabilities. + ResultCode HandleDebugFlags(u32 flags); + + SyscallCapabilities svc_capabilities; + InterruptCapabilities interrupt_capabilities; + + u64 core_mask = 0; + u64 priority_mask = 0; + + u32 handle_table_size = 0; + u32 kernel_version = 0; + u32 program_type = 0; + + bool is_debuggable = false; + bool can_force_debug = false; +}; + +} // namespace Kernel From 27caf7120444d1c34e1c2e322ab97ba9f5275b28 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 19 Dec 2018 19:09:18 -0500 Subject: [PATCH 42/74] kernel/process_capability: Handle the priority mask and core mask flags Handles the priority mask and core mask flags to allow building up the masks to determine the usable thread priorities and cores for a kernel process instance. --- src/core/hle/kernel/process_capability.cpp | 31 +++++++++++++++++++++- src/core/hle/kernel/process_capability.h | 10 +++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 8d787547b6..9f513b25bd 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -205,7 +205,36 @@ void ProcessCapabilities::Clear() { } ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { - // TODO: Implement + if (priority_mask != 0 || core_mask != 0) { + return ERR_INVALID_CAPABILITY_DESCRIPTOR; + } + + const u32 core_num_min = (flags >> 16) & 0xFF; + const u32 core_num_max = (flags >> 24) & 0xFF; + if (core_num_min > core_num_max) { + return ERR_INVALID_COMBINATION; + } + + const u32 priority_min = (flags >> 10) & 0x3F; + const u32 priority_max = (flags >> 4) & 0x3F; + if (priority_min > priority_max) { + return ERR_INVALID_COMBINATION; + } + + // The switch only has 4 usable cores. + if (core_num_max >= 4) { + return ERR_INVALID_PROCESSOR_ID; + } + + const auto make_mask = [](u64 min, u64 max) { + const u64 range = max - min + 1; + const u64 mask = (1ULL << range) - 1; + + return mask << min; + }; + + core_mask = make_mask(core_num_min, core_num_max); + priority_mask = make_mask(priority_min, priority_max); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index 5cff104764..4b27ee8b98 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -122,6 +122,16 @@ public: /// void InitializeForMetadatalessProcess(); + /// Gets the allowable core mask + u64 GetCoreMask() const { + return core_mask; + } + + /// Gets the allowable priority mask + u64 GetPriorityMask() const { + return priority_mask; + } + private: /// Attempts to parse a given sequence of capability descriptors. /// From 3dc59b74ec92f8ee6b856eb4a18c3efa9286730e Mon Sep 17 00:00:00 2001 From: Lioncash Date: Thu, 20 Dec 2018 22:54:05 -0500 Subject: [PATCH 43/74] kernel/process_capability: Handle syscall capability flags --- src/core/hle/kernel/process_capability.cpp | 25 +++++++++++++++++++++- src/core/hle/kernel/process_capability.h | 5 +++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 9f513b25bd..615b354c6a 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -239,7 +239,30 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { } ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { - // TODO: Implement + const u32 index = flags >> 29; + const u32 svc_bit = 1U << index; + + // If we've already set this svc before, bail. + if ((set_svc_bits & svc_bit) != 0) { + return ERR_INVALID_COMBINATION; + } + set_svc_bits |= svc_bit; + + const u32 svc_mask = (flags >> 5) & 0xFFFFFF; + for (u32 i = 0; i < 24; ++i) { + const u32 svc_number = index * 24 + i; + + if ((svc_mask & (1U << i)) == 0) { + continue; + } + + if (svc_number >= svc_capabilities.size()) { + return ERR_OUT_OF_RANGE; + } + + svc_capabilities[svc_number] = true; + } + return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index 4b27ee8b98..4c151b3d5e 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -132,6 +132,11 @@ public: return priority_mask; } + /// Gets the SVC access permission bits + const SyscallCapabilities& GetServiceCapabilities() const { + return svc_capabilities; + } + private: /// Attempts to parse a given sequence of capability descriptors. /// From 0f216d20e368dfc3978bef0b9327b5fbf150af4a Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 19 Dec 2018 20:38:29 -0500 Subject: [PATCH 44/74] kernel/process_capability: Handle interrupt capability flags Similar to the service capability flags, however, we currently don't emulate the GIC, so this currently handles all interrupts as being valid for the time being. --- src/core/hle/kernel/process_capability.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 615b354c6a..e98157f9c0 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -278,7 +278,27 @@ ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manage } ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) { - // TODO: Implement + constexpr u32 interrupt_ignore_value = 0x3FF; + const u32 interrupt0 = (flags >> 12) & 0x3FF; + const u32 interrupt1 = (flags >> 22) & 0x3FF; + + for (u32 interrupt : {interrupt0, interrupt1}) { + if (interrupt == interrupt_ignore_value) { + continue; + } + + // NOTE: + // This should be checking a generic interrupt controller value + // as part of the calculation, however, given we don't currently + // emulate that, it's sufficient to mark every interrupt as defined. + + if (interrupt >= interrupt_capabilities.size()) { + return ERR_OUT_OF_RANGE; + } + + interrupt_capabilities[interrupt] = true; + } + return RESULT_SUCCESS; } From 010bc677f36304964e753057740a8ca32a7dcb83 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 19 Dec 2018 21:14:47 -0500 Subject: [PATCH 45/74] kernel/process_capability: Handle program capability flags --- src/core/hle/kernel/errors.h | 1 + src/core/hle/kernel/process_capability.cpp | 9 ++++++++- src/core/hle/kernel/process_capability.h | 21 ++++++++++++++++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index a3d7258660..ec574c0974 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -31,6 +31,7 @@ constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122}; constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; +constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126}; constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132}; } // namespace Kernel diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index e98157f9c0..ef506b9f38 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -200,6 +200,8 @@ void ProcessCapabilities::Clear() { handle_table_size = 0; kernel_version = 0; + program_type = ProgramType::SysModule; + is_debuggable = false; can_force_debug = false; } @@ -303,7 +305,12 @@ ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) { } ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { - // TODO: Implement + const u32 reserved = flags >> 17; + if (reserved != 0) { + return ERR_RESERVED_VALUE; + } + + program_type = static_cast((flags >> 14) & 0b111); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index 4c151b3d5e..140d602676 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -14,6 +14,14 @@ namespace Kernel { class VMManager; +/// The possible types of programs that may be indicated +/// by the program type capability descriptor. +enum class ProgramType { + SysModule, + Application, + Applet, +}; + /// Handles kernel capability descriptors that are provided by /// application metadata. These descriptors provide information /// that alters certain parameters for kernel process instance @@ -137,6 +145,16 @@ public: return svc_capabilities; } + /// Gets the valid interrupt bits. + const InterruptCapabilities& GetInterruptCapabilities() const { + return interrupt_capabilities; + } + + /// Gets the program type for this process. + ProgramType GetProgramType() const { + return program_type; + } + private: /// Attempts to parse a given sequence of capability descriptors. /// @@ -215,7 +233,8 @@ private: u32 handle_table_size = 0; u32 kernel_version = 0; - u32 program_type = 0; + + ProgramType program_type = ProgramType::SysModule; bool is_debuggable = false; bool can_force_debug = false; From e0e84aede01763f219c4ac4fc0c9c5034dd2656b Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 19 Dec 2018 21:28:44 -0500 Subject: [PATCH 46/74] kernel/process_capability: Handle kernel version capability flags --- src/core/hle/kernel/process_capability.cpp | 14 +++++++++++++- src/core/hle/kernel/process_capability.h | 5 +++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index ef506b9f38..fb4467793d 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -315,7 +315,19 @@ ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { } ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { - // TODO: Implement + // Yes, the internal member variable is checked in the actual kernel here. + // This might look odd for options that are only allowed to be initialized + // just once, however the kernel has a separate initialization function for + // kernel processes and userland processes. The kernel variant sets this + // member variable ahead of time. + + const u32 major_version = kernel_version >> 19; + + if (major_version != 0 || flags < 0x80000) { + return ERR_INVALID_CAPABILITY_DESCRIPTOR; + } + + kernel_version = flags; return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index 140d602676..9a7da8bfa7 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -155,6 +155,11 @@ public: return program_type; } + /// Gets the kernel version value. + u32 GetKernelVersion() const { + return kernel_version; + } + private: /// Attempts to parse a given sequence of capability descriptors. /// From 10824c5d635be0bdfb79f4b3af8c9481034b437f Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 19 Dec 2018 21:43:10 -0500 Subject: [PATCH 47/74] kernel/process_capability: Handle handle table capability flags This just specifies the handle table size. There's also a section of reserved bits that are checked against. --- src/core/hle/kernel/process_capability.cpp | 7 ++++++- src/core/hle/kernel/process_capability.h | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index fb4467793d..7ee0ad9cc3 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -332,7 +332,12 @@ ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { } ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { - // TODO: Implement + const u32 reserved = flags >> 26; + if (reserved != 0) { + return ERR_RESERVED_VALUE; + } + + handle_table_size = (flags >> 16) & 0x3FF; return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index 9a7da8bfa7..7b9f24d518 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -155,6 +155,11 @@ public: return program_type; } + /// Gets the number of total allowable handles for the process' handle table. + u32 GetHandleTableSize() const { + return handle_table_size; + } + /// Gets the kernel version value. u32 GetKernelVersion() const { return kernel_version; From d09fb82113e0a912a66872baa0dd6f1f5c1ef315 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Thu, 20 Dec 2018 23:40:30 -0500 Subject: [PATCH 48/74] kernel/process_capability: Handle debug capability flags --- src/core/hle/kernel/process_capability.cpp | 8 +++++++- src/core/hle/kernel/process_capability.h | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 7ee0ad9cc3..3a2164b25a 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -342,7 +342,13 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { } ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) { - // TODO: Implement + const u32 reserved = flags >> 19; + if (reserved != 0) { + return ERR_RESERVED_VALUE; + } + + is_debuggable = (flags & 0x20000) != 0; + can_force_debug = (flags & 0x40000) != 0; return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index 7b9f24d518..fbc8812a3f 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -165,6 +165,17 @@ public: return kernel_version; } + /// Whether or not this process can be debugged. + bool IsDebuggable() const { + return is_debuggable; + } + + /// Whether or not this process can forcibly debug another + /// process, even if that process is not considered debuggable. + bool CanForceDebug() const { + return can_force_debug; + } + private: /// Attempts to parse a given sequence of capability descriptors. /// From 002ae08bbd3e5e851d8a682203462efbcf59e3dd Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 19 Dec 2018 23:50:20 -0500 Subject: [PATCH 49/74] kernel/process: Hook up the process capability parser to the process itself While we're at it, we can also toss out the leftover capability parsing from Citra. --- src/core/file_sys/program_metadata.cpp | 11 +++ src/core/file_sys/program_metadata.h | 6 ++ src/core/hle/kernel/process.cpp | 80 ++----------------- src/core/hle/kernel/process.h | 58 +++----------- .../loader/deconstructed_rom_directory.cpp | 5 +- src/core/loader/loader.cpp | 4 +- src/core/loader/loader.h | 2 + 7 files changed, 44 insertions(+), 122 deletions(-) diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 8903ed1d3f..e90c8c2de0 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -40,6 +40,13 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) return Loader::ResultStatus::ErrorBadFileAccessHeader; + aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32)); + const u64 read_size = aci_header.kac_size; + const u64 read_offset = npdm_header.aci_offset + aci_header.kac_offset; + if (file->ReadBytes(aci_kernel_capabilities.data(), read_size, read_offset) != read_size) { + return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors; + } + return Loader::ResultStatus::Success; } @@ -71,6 +78,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const { return aci_file_access.permissions; } +const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { + return aci_kernel_capabilities; +} + void ProgramMetadata::Print() const { LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data()); LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority); diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index e4470d6f08..0033ba3475 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/bit_field.h" #include "common/common_types.h" #include "common/swap.h" @@ -38,6 +39,8 @@ enum class ProgramFilePermission : u64 { */ class ProgramMetadata { public: + using KernelCapabilityDescriptors = std::vector; + ProgramMetadata(); ~ProgramMetadata(); @@ -50,6 +53,7 @@ public: u32 GetMainThreadStackSize() const; u64 GetTitleID() const; u64 GetFilesystemPermissions() const; + const KernelCapabilityDescriptors& GetKernelCapabilities() const; void Print() const; @@ -154,6 +158,8 @@ private: FileAccessControl acid_file_access; FileAccessHeader aci_file_access; + + KernelCapabilityDescriptors aci_kernel_capabilities; }; } // namespace FileSys diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 5356a4a3f6..4f209a979f 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -28,13 +28,11 @@ SharedPtr Process::Create(KernelCore& kernel, std::string&& name) { SharedPtr process(new Process(kernel)); process->name = std::move(name); - process->flags.raw = 0; - process->flags.memory_region.Assign(MemoryRegion::APPLICATION); process->resource_limit = kernel.GetSystemResourceLimit(); process->status = ProcessStatus::Created; process->program_id = 0; process->process_id = kernel.CreateNewProcessID(); - process->svc_access_mask.set(); + process->capabilities.InitializeForMetadatalessProcess(); std::mt19937 rng(Settings::values.rng_seed.value_or(0)); std::uniform_int_distribution distribution; @@ -64,83 +62,15 @@ ResultCode Process::ClearSignalState() { return RESULT_SUCCESS; } -void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { +ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { program_id = metadata.GetTitleID(); ideal_processor = metadata.GetMainThreadCore(); is_64bit_process = metadata.Is64BitProgram(); + vm_manager.Reset(metadata.GetAddressSpaceType()); -} -void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { - for (std::size_t i = 0; i < len; ++i) { - u32 descriptor = kernel_caps[i]; - u32 type = descriptor >> 20; - - if (descriptor == 0xFFFFFFFF) { - // Unused descriptor entry - continue; - } else if ((type & 0xF00) == 0xE00) { // 0x0FFF - // Allowed interrupts list - LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored"); - } else if ((type & 0xF80) == 0xF00) { // 0x07FF - // Allowed syscalls mask - unsigned int index = ((descriptor >> 24) & 7) * 24; - u32 bits = descriptor & 0xFFFFFF; - - while (bits && index < svc_access_mask.size()) { - svc_access_mask.set(index, bits & 1); - ++index; - bits >>= 1; - } - } else if ((type & 0xFF0) == 0xFE0) { // 0x00FF - // Handle table size - handle_table_size = descriptor & 0x3FF; - } else if ((type & 0xFF8) == 0xFF0) { // 0x007F - // Misc. flags - flags.raw = descriptor & 0xFFFF; - } else if ((type & 0xFFE) == 0xFF8) { // 0x001F - // Mapped memory range - if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) { - LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored."); - continue; - } - u32 end_desc = kernel_caps[i + 1]; - ++i; // Skip over the second descriptor on the next iteration - - AddressMapping mapping; - mapping.address = descriptor << 12; - VAddr end_address = end_desc << 12; - - if (mapping.address < end_address) { - mapping.size = end_address - mapping.address; - } else { - mapping.size = 0; - } - - mapping.read_only = (descriptor & (1 << 20)) != 0; - mapping.unk_flag = (end_desc & (1 << 20)) != 0; - - address_mappings.push_back(mapping); - } else if ((type & 0xFFF) == 0xFFE) { // 0x000F - // Mapped memory page - AddressMapping mapping; - mapping.address = descriptor << 12; - mapping.size = Memory::PAGE_SIZE; - mapping.read_only = false; - mapping.unk_flag = false; - - address_mappings.push_back(mapping); - } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF - // Kernel version - kernel_version = descriptor & 0xFFFF; - - int minor = kernel_version & 0xFF; - int major = (kernel_version >> 8) & 0xFF; - LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor); - } else { - LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor); - } - } + const auto& caps = metadata.GetKernelCapabilities(); + return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager); } void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 459eedfa6d..f6fb72770f 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -11,9 +11,9 @@ #include #include #include -#include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/process_capability.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/wait_object.h" @@ -42,24 +42,6 @@ enum class MemoryRegion : u16 { BASE = 3, }; -union ProcessFlags { - u16 raw; - - BitField<0, 1, u16> - allow_debug; ///< Allows other processes to attach to and debug this process. - BitField<1, 1, u16> force_debug; ///< Allows this process to attach to processes even if they - /// don't have allow_debug set. - BitField<2, 1, u16> allow_nonalphanum; - BitField<3, 1, u16> shared_page_writable; ///< Shared page is mapped with write permissions. - BitField<4, 1, u16> privileged_priority; ///< Can use priority levels higher than 24. - BitField<5, 1, u16> allow_main_args; - BitField<6, 1, u16> shared_device_mem; - BitField<7, 1, u16> runnable_on_sleep; - BitField<8, 4, MemoryRegion> - memory_region; ///< Default region for memory allocations for this process - BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000). -}; - /** * Indicates the status of a Process instance. * @@ -180,13 +162,13 @@ public: } /// Gets the bitmask of allowed CPUs that this process' threads can run on. - u32 GetAllowedProcessorMask() const { - return allowed_processor_mask; + u64 GetAllowedProcessorMask() const { + return capabilities.GetCoreMask(); } /// Gets the bitmask of allowed thread priorities. - u32 GetAllowedThreadPriorityMask() const { - return allowed_thread_priority_mask; + u64 GetAllowedThreadPriorityMask() const { + return capabilities.GetPriorityMask(); } u32 IsVirtualMemoryEnabled() const { @@ -227,15 +209,12 @@ public: * Loads process-specifics configuration info with metadata provided * by an executable. * - * @param metadata The provided metadata to load process specific info. + * @param metadata The provided metadata to load process specific info from. + * + * @returns RESULT_SUCCESS if all relevant metadata was able to be + * loaded and parsed. Otherwise, an error code is returned. */ - void LoadFromMetadata(const FileSys::ProgramMetadata& metadata); - - /** - * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them - * to this process. - */ - void ParseKernelCaps(const u32* kernel_caps, std::size_t len); + ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata); /** * Applies address space changes and launches the process main thread. @@ -296,22 +275,8 @@ private: /// Resource limit descriptor for this process SharedPtr resource_limit; - /// The process may only call SVCs which have the corresponding bit set. - std::bitset<0x80> svc_access_mask; - /// Maximum size of the handle table for the process. - u32 handle_table_size = 0x200; - /// Special memory ranges mapped into this processes address space. This is used to give - /// processes access to specific I/O regions and device memory. - boost::container::static_vector address_mappings; - ProcessFlags flags; - /// Kernel compatibility version for this process - u16 kernel_version = 0; /// The default CPU for this process, threads are scheduled on this cpu by default. u8 ideal_processor = 0; - /// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse - /// this value from the process header. - u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK; - u32 allowed_thread_priority_mask = 0xFFFFFFFF; u32 is_virtual_address_memory_enabled = 0; /// The Thread Local Storage area is allocated as processes create threads, @@ -321,6 +286,9 @@ private: /// This vector will grow as more pages are allocated for new threads. std::vector> tls_slots; + /// Contains the parsed process capability descriptors. + ProcessCapabilities capabilities; + /// 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. diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index ac04d72d79..07aa7a1cd6 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -129,7 +129,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) return ResultStatus::Error32BitISA; } - process.LoadFromMetadata(metadata); + if (process.LoadFromMetadata(metadata).IsError()) { + return ResultStatus::ErrorUnableToParseKernelMetadata; + } + const FileSys::PatchManager pm(metadata.GetTitleID()); // Load NSO modules diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 9cd0b0ccd0..d8cc309595 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) { return "unknown"; } -constexpr std::array RESULT_MESSAGES{ +constexpr std::array RESULT_MESSAGES{ "The operation completed successfully.", "The loader requested to load is already loaded.", "The operation is not implemented.", @@ -103,6 +103,7 @@ constexpr std::array RESULT_MESSAGES{ "The NPDM has a bad ACI header,", "The NPDM file has a bad file access control.", "The NPDM has a bad file access header.", + "The NPDM has bad kernel capability descriptors.", "The PFS/HFS partition has a bad header.", "The PFS/HFS partition has incorrect size as determined by the header.", "The NCA file has a bad header.", @@ -125,6 +126,7 @@ constexpr std::array RESULT_MESSAGES{ "The file could not be found or does not exist.", "The game is missing a program metadata file (main.npdm).", "The game uses the currently-unimplemented 32-bit architecture.", + "Unable to completely parse the kernel metadata when loading the emulated process", "The RomFS could not be found.", "The ELF file has incorrect size as determined by the header.", "There was a general error loading the NRO into emulated memory.", diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 0838e303b3..a39cb812dd 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -67,6 +67,7 @@ enum class ResultStatus : u16 { ErrorBadACIHeader, ErrorBadFileAccessControl, ErrorBadFileAccessHeader, + ErrorBadKernelCapabilityDescriptors, ErrorBadPFSHeader, ErrorIncorrectPFSFileSize, ErrorBadNCAHeader, @@ -89,6 +90,7 @@ enum class ResultStatus : u16 { ErrorNullFile, ErrorMissingNPDM, Error32BitISA, + ErrorUnableToParseKernelMetadata, ErrorNoRomFS, ErrorIncorrectELFFileSize, ErrorLoadingNRO, From 1120e0b4d200c3761a4de9877b1f7130df387095 Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 21 Dec 2018 13:37:39 -0500 Subject: [PATCH 50/74] hid: Fix SetNpadJoyHoldType and improve logging. --- src/core/hle/service/hid/hid.cpp | 207 ++++++++++++++++++++++++------- 1 file changed, 163 insertions(+), 44 deletions(-) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 2ec38c726a..268409257a 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -306,7 +306,10 @@ private: std::shared_ptr applet_resource; void CreateAppletResource(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); if (applet_resource == nullptr) { applet_resource = std::make_shared(); @@ -318,7 +321,12 @@ private: } void ActivateXpad(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto basic_xpad_id{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", + basic_xpad_id, applet_resource_user_id); applet_resource->ActivateController(HidController::XPad); IPC::ResponseBuilder rb{ctx, 2}; @@ -326,7 +334,10 @@ private: } void ActivateDebugPad(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); applet_resource->ActivateController(HidController::DebugPad); IPC::ResponseBuilder rb{ctx, 2}; @@ -334,7 +345,10 @@ private: } void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); applet_resource->ActivateController(HidController::Touchscreen); IPC::ResponseBuilder rb{ctx, 2}; @@ -342,7 +356,10 @@ private: } void ActivateMouse(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); applet_resource->ActivateController(HidController::Mouse); IPC::ResponseBuilder rb{ctx, 2}; @@ -350,7 +367,10 @@ private: } void ActivateKeyboard(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); applet_resource->ActivateController(HidController::Keyboard); IPC::ResponseBuilder rb{ctx, 2}; @@ -358,7 +378,12 @@ private: } void ActivateGesture(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto unknown{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, + applet_resource_user_id); applet_resource->ActivateController(HidController::Gesture); IPC::ResponseBuilder rb{ctx, 2}; @@ -367,7 +392,12 @@ private: void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { // Should have no effect with how our npad sets up the data - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto unknown{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, + applet_resource_user_id); applet_resource->ActivateController(HidController::NPad); IPC::ResponseBuilder rb{ctx, 2}; @@ -376,22 +406,37 @@ private: void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto handle = rp.PopRaw(); - LOG_WARNING(Service_HID, "(STUBBED) called with handle={}", handle); + const auto handle{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop()}; + const auto drift_mode{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, handle={}, drift_mode={}, applet_resource_user_id={}", + handle, drift_mode, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -401,8 +446,9 @@ private: void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto supported_styleset = rp.PopRaw(); - LOG_DEBUG(Service_HID, "called with supported_styleset={}", supported_styleset); + const auto supported_styleset{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset); applet_resource->GetController(HidController::NPad) .SetSupportedStyleSet({supported_styleset}); @@ -412,7 +458,10 @@ private: } void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); auto& controller = applet_resource->GetController(HidController::NPad); @@ -422,7 +471,10 @@ private: } void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); applet_resource->GetController(HidController::NPad) .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); @@ -431,7 +483,10 @@ private: } void ActivateNpad(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -440,8 +495,12 @@ private: void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto npad_id = rp.PopRaw(); - LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id); + const auto npad_id{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + const auto unknown{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", + npad_id, applet_resource_user_id, unknown); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); @@ -451,8 +510,11 @@ private: void DisconnectNpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto npad_id = rp.PopRaw(); - LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id); + const auto npad_id{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, + applet_resource_user_id); applet_resource->GetController(HidController::NPad) .DisconnectNPad(npad_id); @@ -462,8 +524,9 @@ private: void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto npad_id = rp.PopRaw(); - LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id); + const auto npad_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -474,8 +537,11 @@ private: void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto hold_type = rp.PopRaw(); - LOG_DEBUG(Service_HID, "called with hold_type={}", hold_type); + const auto applet_resource_user_id{rp.Pop()}; + const auto hold_type{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", + applet_resource_user_id, hold_type); auto& controller = applet_resource->GetController(HidController::NPad); controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type}); @@ -485,7 +551,10 @@ private: } void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); const auto& controller = applet_resource->GetController(HidController::NPad); @@ -496,15 +565,21 @@ private: void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto npad_id = rp.PopRaw(); - LOG_WARNING(Service_HID, "(STUBBED) called with npad_id={}", npad_id); + const auto npad_id{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + npad_id, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); applet_resource->GetController(HidController::NPad) .SetVibrationEnabled(true); @@ -523,9 +598,12 @@ private: void SendVibrationValue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto controller_id = rp.PopRaw(); - const auto vibration_values = rp.PopRaw(); - LOG_DEBUG(Service_HID, "called with controller_id={}", controller_id); + const auto controller_id{rp.Pop()}; + const auto vibration_values{rp.PopRaw()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", + controller_id, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -535,7 +613,10 @@ private: } void SendVibrationValues(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); const auto controllers = ctx.ReadBuffer(0); const auto vibrations = ctx.ReadBuffer(1); @@ -557,7 +638,12 @@ private: } void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto controller_id{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", + controller_id, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); @@ -568,8 +654,11 @@ private: void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id = rp.PopRaw(); - LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id); + const auto npad_id{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, + applet_resource_user_id); auto& controller = applet_resource->GetController(HidController::NPad); controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual); @@ -579,7 +668,14 @@ private: } void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto unknown_1{rp.Pop()}; + const auto unknown_2{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", + unknown_1, unknown_2, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -587,8 +683,11 @@ private: void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto mode = rp.PopRaw(); - LOG_WARNING(Service_HID, "(STUBBED) called with mode={}", mode); + const auto applet_resource_user_id{rp.Pop()}; + const auto mode{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}", + applet_resource_user_id, mode); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -612,35 +711,55 @@ private: } void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void StopSixAxisSensor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}", handle); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + const auto unknown{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, unknown={}", + applet_resource_user_id, unknown); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto unknown{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}", unknown); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); From acddf16e57b7673a413fd0ecde618246d8539c5b Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 21 Dec 2018 15:19:35 -0500 Subject: [PATCH 51/74] common/quaternion: Ensure that w is always initialized Previously xyz was always being zero initialized due to its constructor, but w wasn't. Ensures that we always have a deterministic initial state. --- src/common/quaternion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/quaternion.h b/src/common/quaternion.h index ea39298c1f..c528c0b688 100644 --- a/src/common/quaternion.h +++ b/src/common/quaternion.h @@ -12,7 +12,7 @@ template class Quaternion { public: Math::Vec3 xyz; - T w; + T w{}; Quaternion Inverse() const { return {-xyz, w}; From 7e72b5e453ee4c2a62021f6b3c8ad21267c8b24a Mon Sep 17 00:00:00 2001 From: Rodolfo Bogado Date: Tue, 11 Dec 2018 00:47:17 -0300 Subject: [PATCH 52/74] complete emulation of ZeroFlag --- .../renderer_opengl/gl_shader_decompiler.cpp | 197 +++++++++--------- 1 file changed, 97 insertions(+), 100 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 4e685fa2c5..3828b87624 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -405,10 +405,17 @@ public: */ void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value, u64 dest_num_components, u64 value_num_components, - bool is_saturated = false, u64 dest_elem = 0, bool precise = false) { + bool is_saturated = false, bool sets_cc = false, u64 dest_elem = 0, + bool precise = false) { SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value, dest_num_components, value_num_components, dest_elem, precise); + if (sets_cc) { + const std::string zero_condition = + "( " + GetRegister(reg, static_cast(dest_elem)) + " == 0 )"; + SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); + LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete."); + } } /** @@ -425,8 +432,8 @@ public: void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem, const std::string& value, u64 dest_num_components, u64 value_num_components, bool is_saturated = false, - u64 dest_elem = 0, Register::Size size = Register::Size::Word, - bool sets_cc = false) { + bool sets_cc = false, u64 dest_elem = 0, + Register::Size size = Register::Size::Word) { UNIMPLEMENTED_IF(is_saturated); const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; @@ -435,7 +442,8 @@ public: dest_num_components, value_num_components, dest_elem, false); if (sets_cc) { - const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; + const std::string zero_condition = + "( " + GetRegister(reg, static_cast(dest_elem)) + " == 0 )"; SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete."); } @@ -1275,7 +1283,7 @@ private: void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a, const std::string& op_b, Tegra::Shader::PredicateResultMode predicate_mode, - Tegra::Shader::Pred predicate) { + Tegra::Shader::Pred predicate, const bool set_cc) { std::string result{}; switch (logic_op) { case LogicOperation::And: { @@ -1299,7 +1307,7 @@ private: } if (dest != Tegra::Shader::Register::ZeroIndex) { - regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); + regs.SetRegisterToInteger(dest, true, 0, result, 1, 1, false, set_cc); } using Tegra::Shader::PredicateResultMode; @@ -1319,7 +1327,8 @@ private: } void WriteLop3Instruction(Register dest, const std::string& op_a, const std::string& op_b, - const std::string& op_c, const std::string& imm_lut) { + const std::string& op_c, const std::string& imm_lut, + const bool set_cc) { if (dest == Tegra::Shader::Register::ZeroIndex) { return; } @@ -1342,7 +1351,7 @@ private: result += ')'; - regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); + regs.SetRegisterToInteger(dest, true, 0, result, 1, 1, false, set_cc); } void WriteTexsInstructionFloat(const Instruction& instr, const std::string& texture) { @@ -1357,12 +1366,12 @@ private: if (written_components < 2) { // Write the first two swizzle components to gpr0 and gpr0+1 - regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false, + regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false, false, written_components % 2); } else { ASSERT(instr.texs.HasTwoDestinations()); // Write the rest of the swizzle components to gpr28 and gpr28+1 - regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false, + regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false, false, written_components % 2); } @@ -1608,7 +1617,7 @@ private: if (process_mode != Tegra::Shader::TextureProcessMode::None && gl_lod_supported) { if (process_mode == Tegra::Shader::TextureProcessMode::LZ) { texture += ", 0.0"; - } else { + } else { // If present, lod or bias are always stored in the register indexed by the // gpr20 // field with an offset depending on the usage of the other registers @@ -1871,8 +1880,6 @@ private: instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented", instr.fmul.tab5c68_0 .Value()); // SMO typical sends 1 here which seems to be the default - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in FMUL is not implemented"); op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); @@ -1896,20 +1903,17 @@ private: } regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + postfactor_op, 1, 1, - instr.alu.saturate_d, 0, true); + instr.alu.saturate_d, instr.generates_cc, 0, true); break; } case OpCode::Id::FADD_C: case OpCode::Id::FADD_R: case OpCode::Id::FADD_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in FADD is not implemented"); - op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, - instr.alu.saturate_d, 0, true); + instr.alu.saturate_d, instr.generates_cc, 0, true); break; } case OpCode::Id::MUFU: { @@ -1917,31 +1921,31 @@ private: switch (instr.sub_op) { case SubOp::Cos: regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1, - instr.alu.saturate_d, 0, true); + instr.alu.saturate_d, false, 0, true); break; case SubOp::Sin: regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1, - instr.alu.saturate_d, 0, true); + instr.alu.saturate_d, false, 0, true); break; case SubOp::Ex2: regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1, - instr.alu.saturate_d, 0, true); + instr.alu.saturate_d, false, 0, true); break; case SubOp::Lg2: regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1, - instr.alu.saturate_d, 0, true); + instr.alu.saturate_d, false, 0, true); break; case SubOp::Rcp: regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1, - instr.alu.saturate_d, 0, true); + instr.alu.saturate_d, false, 0, true); break; case SubOp::Rsq: regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1, - instr.alu.saturate_d, 0, true); + instr.alu.saturate_d, false, 0, true); break; case SubOp::Sqrt: regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1, - instr.alu.saturate_d, 0, true); + instr.alu.saturate_d, false, 0, true); break; default: UNIMPLEMENTED_MSG("Unhandled MUFU sub op={0:x}", @@ -1952,8 +1956,9 @@ private: case OpCode::Id::FMNMX_C: case OpCode::Id::FMNMX_R: case OpCode::Id::FMNMX_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in FMNMX is not implemented"); + UNIMPLEMENTED_IF_MSG( + instr.generates_cc, + "Condition codes generation in FMNMX is partially implemented"); op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); @@ -1964,7 +1969,7 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, '(' + condition + ") ? min(" + parameters + ") : max(" + parameters + ')', - 1, 1, false, 0, true); + 1, 1, false, instr.generates_cc, 0, true); break; } case OpCode::Id::RRO_C: @@ -1989,18 +1994,16 @@ private: break; } case OpCode::Id::FMUL32_IMM: { - UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, - "Condition codes generation in FMUL32 is not implemented"); - - regs.SetRegisterToFloat(instr.gpr0, 0, - regs.GetRegisterAsFloat(instr.gpr8) + " * " + - GetImmediate32(instr), - 1, 1, instr.fmul32.saturate, 0, true); + regs.SetRegisterToFloat( + instr.gpr0, 0, + regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1, + instr.fmul32.saturate, instr.op_32.generates_cc, 0, true); break; } case OpCode::Id::FADD32I: { - UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, - "Condition codes generation in FADD32I is not implemented"); + UNIMPLEMENTED_IF_MSG( + instr.op_32.generates_cc, + "Condition codes generation in FADD32I is partially implemented"); std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); std::string op_b = GetImmediate32(instr); @@ -2021,7 +2024,8 @@ private: op_b = "-(" + op_b + ')'; } - regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); + regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, + instr.op_32.generates_cc, 0, true); break; } } @@ -2035,16 +2039,14 @@ private: switch (opcode->get().GetId()) { case OpCode::Id::BFE_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in BFE is not implemented"); - std::string inner_shift = '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; std::string outer_shift = '(' + inner_shift + " >> " + std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; - regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); + regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1, false, + instr.generates_cc); break; } default: { @@ -2055,8 +2057,6 @@ private: break; } case OpCode::Type::Bfi: { - UNIMPLEMENTED_IF(instr.generates_cc); - const auto [base, packed_shift] = [&]() -> std::tuple { switch (opcode->get().GetId()) { case OpCode::Id::BFI_IMM_R: @@ -2071,9 +2071,10 @@ private: const std::string offset = '(' + packed_shift + " & 0xff)"; const std::string bits = "((" + packed_shift + " >> 8) & 0xff)"; const std::string insert = regs.GetRegisterAsInteger(instr.gpr8, 0, false); - regs.SetRegisterToInteger( - instr.gpr0, false, 0, - "bitfieldInsert(" + base + ", " + insert + ", " + offset + ", " + bits + ')', 1, 1); + regs.SetRegisterToInteger(instr.gpr0, false, 0, + "bitfieldInsert(" + base + ", " + insert + ", " + offset + + ", " + bits + ')', + 1, 1, false, instr.generates_cc); break; } case OpCode::Type::Shift: { @@ -2095,9 +2096,6 @@ private: case OpCode::Id::SHR_C: case OpCode::Id::SHR_R: case OpCode::Id::SHR_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in SHR is not implemented"); - if (!instr.shift.is_signed) { // Logical shift right op_a = "uint(" + op_a + ')'; @@ -2105,7 +2103,7 @@ private: // Cast to int is superfluous for arithmetic shift, it's only for a logical shift regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', - 1, 1); + 1, 1, false, instr.generates_cc); break; } case OpCode::Id::SHL_C: @@ -2113,7 +2111,8 @@ private: case OpCode::Id::SHL_IMM: UNIMPLEMENTED_IF_MSG(instr.generates_cc, "Condition codes generation in SHL is not implemented"); - regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); + regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1, false, + instr.generates_cc); break; default: { UNIMPLEMENTED_MSG("Unhandled shift instruction: {}", opcode->get().GetName()); @@ -2127,18 +2126,17 @@ private: switch (opcode->get().GetId()) { case OpCode::Id::IADD32I: - UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, - "Condition codes generation in IADD32I is not implemented"); + UNIMPLEMENTED_IF_MSG( + instr.op_32.generates_cc, + "Condition codes generation in IADD32I is partially implemented"); if (instr.iadd32i.negate_a) op_a = "-(" + op_a + ')'; regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, - instr.iadd32i.saturate != 0); + instr.iadd32i.saturate, instr.op_32.generates_cc); break; case OpCode::Id::LOP32I: { - UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, - "Condition codes generation in LOP32I is not implemented"); if (instr.alu.lop32i.invert_a) op_a = "~(" + op_a + ')'; @@ -2148,7 +2146,7 @@ private: WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, Tegra::Shader::PredicateResultMode::None, - Tegra::Shader::Pred::UnusedIndex); + Tegra::Shader::Pred::UnusedIndex, instr.op_32.generates_cc); break; } default: { @@ -2177,7 +2175,7 @@ private: case OpCode::Id::IADD_R: case OpCode::Id::IADD_IMM: { UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in IADD is not implemented"); + "Condition codes generation in IADD is partially implemented"); if (instr.alu_integer.negate_a) op_a = "-(" + op_a + ')'; @@ -2186,14 +2184,15 @@ private: op_b = "-(" + op_b + ')'; regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, - instr.alu.saturate_d); + instr.alu.saturate_d, instr.generates_cc); break; } case OpCode::Id::IADD3_C: case OpCode::Id::IADD3_R: case OpCode::Id::IADD3_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in IADD3 is not implemented"); + UNIMPLEMENTED_IF_MSG( + instr.generates_cc, + "Condition codes generation in IADD3 is partially implemented"); std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); @@ -2249,14 +2248,16 @@ private: result = '(' + op_a + " + " + op_b + " + " + op_c + ')'; } - regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); + regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1, false, + instr.generates_cc); break; } case OpCode::Id::ISCADD_C: case OpCode::Id::ISCADD_R: case OpCode::Id::ISCADD_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in ISCADD is not implemented"); + UNIMPLEMENTED_IF_MSG( + instr.generates_cc, + "Condition codes generation in ISCADD is partially implemented"); if (instr.alu_integer.negate_a) op_a = "-(" + op_a + ')'; @@ -2267,7 +2268,8 @@ private: const std::string shift = std::to_string(instr.alu_integer.shift_amount.Value()); regs.SetRegisterToInteger(instr.gpr0, true, 0, - "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); + "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1, + false, instr.generates_cc); break; } case OpCode::Id::POPC_C: @@ -2291,8 +2293,6 @@ private: case OpCode::Id::LOP_C: case OpCode::Id::LOP_R: case OpCode::Id::LOP_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in LOP is not implemented"); if (instr.alu.lop.invert_a) op_a = "~(" + op_a + ')'; @@ -2301,15 +2301,13 @@ private: op_b = "~(" + op_b + ')'; WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, - instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); + instr.alu.lop.pred_result_mode, instr.alu.lop.pred48, + instr.generates_cc); break; } case OpCode::Id::LOP3_C: case OpCode::Id::LOP3_R: case OpCode::Id::LOP3_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in LOP3 is not implemented"); - const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); std::string lut; @@ -2319,15 +2317,16 @@ private: lut = '(' + std::to_string(instr.alu.lop3.GetImmLut48()) + ')'; } - WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); + WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut, instr.generates_cc); break; } case OpCode::Id::IMNMX_C: case OpCode::Id::IMNMX_R: case OpCode::Id::IMNMX_IMM: { UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None); - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in IMNMX is not implemented"); + UNIMPLEMENTED_IF_MSG( + instr.generates_cc, + "Condition codes generation in IMNMX is partially implemented"); const std::string condition = GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); @@ -2335,7 +2334,7 @@ private: regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0, '(' + condition + ") ? min(" + parameters + ") : max(" + parameters + ')', - 1, 1); + 1, 1, false, instr.generates_cc); break; } case OpCode::Id::LEA_R2: @@ -2396,7 +2395,8 @@ private: UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast(Pred::UnusedIndex), "Unhandled LEA Predicate"); const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))"; - regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1); + regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1, false, + instr.generates_cc); break; } @@ -2501,7 +2501,7 @@ private: UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented", instr.ffma.tab5980_1.Value()); UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in FFMA is not implemented"); + "Condition codes generation in FFMA is partially implemented"); switch (opcode->get().GetId()) { case OpCode::Id::FFMA_CR: { @@ -2532,7 +2532,7 @@ private: } regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', - 1, 1, instr.alu.saturate_d, 0, true); + 1, 1, instr.alu.saturate_d, instr.generates_cc, 0, true); break; } case OpCode::Type::Hfma2: { @@ -2603,16 +2603,14 @@ private: } regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, - 1, instr.alu.saturate_d, 0, instr.conversion.dest_size, - instr.generates_cc.Value() != 0); + 1, instr.alu.saturate_d, instr.generates_cc, 0, + instr.conversion.dest_size); break; } case OpCode::Id::I2F_R: case OpCode::Id::I2F_C: { UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); UNIMPLEMENTED_IF(instr.conversion.selector); - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in I2F is not implemented"); std::string op_a; if (instr.is_b_gpr) { @@ -2635,14 +2633,12 @@ private: op_a = "-(" + op_a + ')'; } - regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); + regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, false, instr.generates_cc); break; } case OpCode::Id::F2F_R: { UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in F2F is not implemented"); std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); if (instr.conversion.abs_a) { @@ -2674,14 +2670,13 @@ private: break; } - regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); + regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d, + instr.generates_cc); break; } case OpCode::Id::F2I_R: case OpCode::Id::F2I_C: { UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); - UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in F2I is not implemented"); std::string op_a{}; if (instr.is_b_gpr) { @@ -2724,7 +2719,8 @@ private: } regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, - 1, false, 0, instr.conversion.dest_size); + 1, false, instr.generates_cc, 0, + instr.conversion.dest_size); break; } default: { @@ -2887,7 +2883,7 @@ private: shader.AddLine(coord); if (depth_compare) { - regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); + regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1); } else { shader.AddLine("vec4 texture_tmp = " + texture + ';'); std::size_t dest_elem{}; @@ -2896,7 +2892,7 @@ private: // Skip disabled components continue; } - regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, + regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, false, dest_elem); ++dest_elem; } @@ -2982,7 +2978,7 @@ private: // Skip disabled components continue; } - regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, + regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, false, dest_elem); ++dest_elem; } @@ -3231,7 +3227,7 @@ private: } case OpCode::Type::PredicateSetRegister: { UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in PSET is not implemented"); + "Condition codes generation in PSET is partially implemented"); const std::string op_a = GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); @@ -3248,10 +3244,11 @@ private: const std::string result = '(' + predicate + ") " + combiner + " (" + second_pred + ')'; if (instr.pset.bf == 0) { const std::string value = '(' + result + ") ? 0xFFFFFFFF : 0"; - regs.SetRegisterToInteger(instr.gpr0, false, 0, value, 1, 1); + regs.SetRegisterToInteger(instr.gpr0, false, 0, value, 1, 1, false, + instr.generates_cc); } else { const std::string value = '(' + result + ") ? 1.0 : 0.0"; - regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); + regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1, false, instr.generates_cc); } break; } @@ -3462,7 +3459,7 @@ private: UNIMPLEMENTED_IF(instr.xmad.sign_a); UNIMPLEMENTED_IF(instr.xmad.sign_b); UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "Condition codes generation in XMAD is not implemented"); + "Condition codes generation in XMAD is partially implemented"); std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)}; std::string op_b; @@ -3548,7 +3545,8 @@ private: sum = "((" + sum + " & 0xFFFF) | (" + src2 + "<< 16))"; } - regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); + regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1, false, + instr.generates_cc); break; } default: { @@ -3752,8 +3750,7 @@ private: } regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, - instr.vmad.saturate == 1, 0, Register::Size::Word, - instr.vmad.cc); + instr.vmad.saturate, instr.vmad.cc); break; } case OpCode::Id::VSETP: { From 946777601ba6d2ee2dafa7e27e10c30c0dea45c0 Mon Sep 17 00:00:00 2001 From: Rodolfo Bogado Date: Wed, 12 Dec 2018 00:34:12 -0300 Subject: [PATCH 53/74] Handle RZ cases evaluating the expression instead of the register value. --- .../renderer_opengl/gl_shader_decompiler.cpp | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 3828b87624..7a16dccc9f 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -347,6 +347,15 @@ public: BuildInputList(); } + void SetConditionalCodesFromExpression(const std::string& expresion) { + SetInternalFlag(InternalFlag::ZeroFlag, "(" + expresion + ") == 0"); + LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete."); + } + + void SetConditionalCodesFromRegister(const Register& reg, u64 dest_elem = 0) { + SetConditionalCodesFromExpression(GetRegister(reg, static_cast(dest_elem))); + } + /** * Returns code that does an integer size conversion for the specified size. * @param value Value to perform integer size conversion on. @@ -411,10 +420,11 @@ public: SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value, dest_num_components, value_num_components, dest_elem, precise); if (sets_cc) { - const std::string zero_condition = - "( " + GetRegister(reg, static_cast(dest_elem)) + " == 0 )"; - SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); - LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete."); + if (reg == Register::ZeroIndex) { + SetConditionalCodesFromExpression(value); + } else { + SetConditionalCodesFromRegister(reg, dest_elem); + } } } @@ -442,10 +452,11 @@ public: dest_num_components, value_num_components, dest_elem, false); if (sets_cc) { - const std::string zero_condition = - "( " + GetRegister(reg, static_cast(dest_elem)) + " == 0 )"; - SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); - LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete."); + if (reg == Register::ZeroIndex) { + SetConditionalCodesFromExpression(value); + } else { + SetConditionalCodesFromRegister(reg, dest_elem); + } } } @@ -3365,14 +3376,11 @@ private: ") " + combiner + " (" + second_pred + "))"; if (instr.fset.bf) { - regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); + regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1, false, + instr.generates_cc); } else { regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1, - 1); - } - if (instr.generates_cc.Value() != 0) { - regs.SetInternalFlag(InternalFlag::ZeroFlag, predicate); - LOG_WARNING(HW_GPU, "FSET Condition Code is incomplete"); + 1, false, instr.generates_cc); } break; } From bbf8d6bf01b7eb745e47143ec3e01dc19e9f05bd Mon Sep 17 00:00:00 2001 From: Rodolfo Bogado Date: Wed, 12 Dec 2018 00:54:21 -0300 Subject: [PATCH 54/74] Includde saturation in the evaluation of the control code --- src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 7a16dccc9f..c7812d3776 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -416,12 +416,13 @@ public: u64 dest_num_components, u64 value_num_components, bool is_saturated = false, bool sets_cc = false, u64 dest_elem = 0, bool precise = false) { - - SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value, + const const std::string clamped_value = + is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value; + SetRegister(reg, elem, clamped_value, dest_num_components, value_num_components, dest_elem, precise); if (sets_cc) { if (reg == Register::ZeroIndex) { - SetConditionalCodesFromExpression(value); + SetConditionalCodesFromExpression(clamped_value); } else { SetConditionalCodesFromRegister(reg, dest_elem); } From aaa0e6c3465649417b9334326ab864f19dba2cd5 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Wed, 26 Dec 2018 01:35:44 -0300 Subject: [PATCH 55/74] shader_bytecode: Fixup TEXS.F16 encoding --- src/video_core/engines/shader_bytecode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index eb703bb5af..e53c77f2b2 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -1049,7 +1049,7 @@ union Instruction { BitField<49, 1, u64> nodep_flag; BitField<50, 3, u64> component_mask_selector; BitField<53, 4, u64> texture_info; - BitField<60, 1, u64> fp32_flag; + BitField<59, 1, u64> fp32_flag; TextureType GetTextureType() const { // The TEXS instruction has a weird encoding for the texture type. From 7e622c55758c55e0fdb7a7cc42886272d4187f06 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 26 Dec 2018 15:47:11 -0500 Subject: [PATCH 56/74] npad: Remove code to invert input in horizontal mode. - This was incorrect, the game appears to handle this for us. - Fixes horizontal mode with Puyo Puyo Tetris and Super Mario Odyssey. --- src/core/hle/service/hid/controllers/npad.cpp | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index d6829d0b8e..75fdb861a8 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -339,52 +339,6 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index]; auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; - if (hold_type == NpadHoldType::Horizontal) { - ControllerPadState state{}; - AnalogPosition temp_lstick_entry{}; - AnalogPosition temp_rstick_entry{}; - if (controller_type == NPadControllerType::JoyLeft) { - state.d_down.Assign(pad_state.pad_states.d_left.Value()); - state.d_left.Assign(pad_state.pad_states.d_up.Value()); - state.d_right.Assign(pad_state.pad_states.d_down.Value()); - state.d_up.Assign(pad_state.pad_states.d_right.Value()); - state.l.Assign(pad_state.pad_states.l.Value() | - pad_state.pad_states.left_sl.Value()); - state.r.Assign(pad_state.pad_states.r.Value() | - pad_state.pad_states.left_sr.Value()); - - state.zl.Assign(pad_state.pad_states.zl.Value()); - state.plus.Assign(pad_state.pad_states.minus.Value()); - - temp_lstick_entry = pad_state.l_stick; - temp_rstick_entry = pad_state.r_stick; - std::swap(temp_lstick_entry.x, temp_lstick_entry.y); - std::swap(temp_rstick_entry.x, temp_rstick_entry.y); - temp_lstick_entry.y *= -1; - } else if (controller_type == NPadControllerType::JoyRight) { - state.x.Assign(pad_state.pad_states.a.Value()); - state.a.Assign(pad_state.pad_states.b.Value()); - state.b.Assign(pad_state.pad_states.y.Value()); - state.y.Assign(pad_state.pad_states.b.Value()); - - state.l.Assign(pad_state.pad_states.l.Value() | - pad_state.pad_states.right_sl.Value()); - state.r.Assign(pad_state.pad_states.r.Value() | - pad_state.pad_states.right_sr.Value()); - state.zr.Assign(pad_state.pad_states.zr.Value()); - state.plus.Assign(pad_state.pad_states.plus.Value()); - - temp_lstick_entry = pad_state.l_stick; - temp_rstick_entry = pad_state.r_stick; - std::swap(temp_lstick_entry.x, temp_lstick_entry.y); - std::swap(temp_rstick_entry.x, temp_rstick_entry.y); - temp_rstick_entry.x *= -1; - } - pad_state.pad_states.raw = state.raw; - pad_state.l_stick = temp_lstick_entry; - pad_state.r_stick = temp_rstick_entry; - } - libnx_entry.connection_status.raw = 0; switch (controller_type) { From 8047873a66146ae25e9707796b16062ed00e60b7 Mon Sep 17 00:00:00 2001 From: David <25727384+ogniK5377@users.noreply.github.com> Date: Thu, 27 Dec 2018 07:55:39 +1100 Subject: [PATCH 57/74] Fixed shader linking error due to TLDS (#1934) * Fixed shader linking error due to TLDS coord should be coords * Fix remaining coords --- src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 4e685fa2c5..76d9b35381 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -1755,7 +1755,7 @@ private: instr.tlds.GetTextureProcessMode() == Tegra::Shader::TextureProcessMode::LL; constexpr std::array coord_container{ - {"", "int coord = (", "ivec2 coord = ivec2(", "ivec3 coord = ivec3("}}; + {"", "int coords = (", "ivec2 coords = ivec2(", "ivec3 coords = ivec3("}}; std::string coord = coord_container[total_coord_count]; From 33056dd8336b4e61d839408f71ba5a92f849dd77 Mon Sep 17 00:00:00 2001 From: Rodolfo Bogado Date: Wed, 12 Dec 2018 19:02:37 -0300 Subject: [PATCH 58/74] Apply CC test to the final value to be stored in the register --- .../renderer_opengl/gl_shader_decompiler.cpp | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index c7812d3776..3640d2b898 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -410,16 +410,17 @@ public: * @param dest_num_components Number of components in the destination. * @param value_num_components Number of components in the value. * @param is_saturated Optional, when True, saturates the provided value. + * @param sets_cc Optional, when True, sets the corresponding values to the implemented + * condition flags. * @param dest_elem Optional, the destination element to use for the operation. */ void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value, u64 dest_num_components, u64 value_num_components, bool is_saturated = false, bool sets_cc = false, u64 dest_elem = 0, bool precise = false) { - const const std::string clamped_value = - is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value; - SetRegister(reg, elem, clamped_value, - dest_num_components, value_num_components, dest_elem, precise); + const std::string clamped_value = is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value; + SetRegister(reg, elem, clamped_value, dest_num_components, value_num_components, dest_elem, + precise); if (sets_cc) { if (reg == Register::ZeroIndex) { SetConditionalCodesFromExpression(clamped_value); @@ -437,6 +438,8 @@ public: * @param dest_num_components Number of components in the destination. * @param value_num_components Number of components in the value. * @param is_saturated Optional, when True, saturates the provided value. + * @param sets_cc Optional, when True, sets the corresponding values to the implemented + * condition flags. * @param dest_elem Optional, the destination element to use for the operation. * @param size Register size to use for conversion instructions. */ @@ -446,15 +449,15 @@ public: bool sets_cc = false, u64 dest_elem = 0, Register::Size size = Register::Size::Word) { UNIMPLEMENTED_IF(is_saturated); - + const std::string final_value = ConvertIntegerSize(value, size); const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; - SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', - dest_num_components, value_num_components, dest_elem, false); + SetRegister(reg, elem, func + '(' + final_value + ')', dest_num_components, + value_num_components, dest_elem, false); if (sets_cc) { if (reg == Register::ZeroIndex) { - SetConditionalCodesFromExpression(value); + SetConditionalCodesFromExpression(final_value); } else { SetConditionalCodesFromRegister(reg, dest_elem); } @@ -1629,7 +1632,7 @@ private: if (process_mode != Tegra::Shader::TextureProcessMode::None && gl_lod_supported) { if (process_mode == Tegra::Shader::TextureProcessMode::LZ) { texture += ", 0.0"; - } else { + } else { // If present, lod or bias are always stored in the register indexed by the // gpr20 // field with an offset depending on the usage of the other registers From 67fa21e143778090964a744cd46ff17829591b7d Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 26 Dec 2018 17:32:30 -0500 Subject: [PATCH 59/74] renderer_opengl: Correct forward declaration of FramebufferLayout This is actually a struct, not a class, which can lead to compilation warnings. --- src/video_core/renderer_opengl/renderer_opengl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 067fad81b7..b85cc262fd 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -17,7 +17,7 @@ class EmuWindow; } namespace Layout { -class FramebufferLayout; +struct FramebufferLayout; } namespace OpenGL { From faa9110541fb38a3edb67eed418982785923044f Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 26 Dec 2018 17:30:19 -0500 Subject: [PATCH 60/74] configure_input_simple: Make input profile array constexpr Calling tr() from a file-scope array isn't advisable, since it can be executed before the Qt libraries are even fully initialized, which can lead to crashes. Instead, the translatable strings should be annotated, and the tr() function should be called at the string's usage site. --- .../configuration/configure_input_simple.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp index b4f3724bd6..07d71e9d18 100644 --- a/src/yuzu/configuration/configure_input_simple.cpp +++ b/src/yuzu/configuration/configure_input_simple.cpp @@ -3,12 +3,8 @@ // Refer to the license.txt file included. #include -#include -#include #include -#include - #include "ui_configure_input_simple.h" #include "yuzu/configuration/configure_input.h" #include "yuzu/configuration/configure_input_player.h" @@ -73,20 +69,18 @@ void DualJoyconsDockedOnProfileSelect() { // Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure // is clicked) -using InputProfile = - std::tuple, std::function>; +using InputProfile = std::tuple; -const std::array INPUT_PROFILES{{ - {ConfigureInputSimple::tr("Single Player - Handheld - Undocked"), HandheldOnProfileSelect, +constexpr std::array INPUT_PROFILES{{ + {QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect, [](ConfigureInputSimple* caller) { CallConfigureDialog(caller, HANDHELD_INDEX, false); }}, - {ConfigureInputSimple::tr("Single Player - Dual Joycons - Docked"), - DualJoyconsDockedOnProfileSelect, + {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect, [](ConfigureInputSimple* caller) { CallConfigureDialog(caller, 1, false); }}, - {ConfigureInputSimple::tr("Custom"), [] {}, CallConfigureDialog}, + {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog}, }}; } // namespace @@ -101,7 +95,8 @@ ConfigureInputSimple::ConfigureInputSimple(QWidget* parent) ui->setupUi(this); for (const auto& profile : INPUT_PROFILES) { - ui->profile_combobox->addItem(std::get<0>(profile), std::get<0>(profile)); + const QString label = tr(std::get<0>(profile)); + ui->profile_combobox->addItem(label, label); } connect(ui->profile_combobox, QOverload::of(&QComboBox::currentIndexChanged), this, From 0c18d473489444b7c5bf8821eac906de77f9bb8b Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 26 Dec 2018 17:45:12 -0500 Subject: [PATCH 61/74] configure_per_general: Mark UI strings as translatable in the constructor These are user-facing strings, so they should be translatable. --- src/yuzu/configuration/configure_per_general.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp index 80109b434d..5ca493b6df 100644 --- a/src/yuzu/configuration/configure_per_general.cpp +++ b/src/yuzu/configuration/configure_per_general.cpp @@ -47,8 +47,8 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) tree_view->setContextMenuPolicy(Qt::NoContextMenu); item_model->insertColumns(0, 2); - item_model->setHeaderData(0, Qt::Horizontal, "Patch Name"); - item_model->setHeaderData(1, Qt::Horizontal, "Version"); + item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name")); + item_model->setHeaderData(1, Qt::Horizontal, tr("Version")); // We must register all custom types with the Qt Automoc system so that we are able to use it // with signals/slots. In this case, QList falls under the umbrells of custom types. From 1392597ede66a75b738772d678cf61d614c22679 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 26 Dec 2018 20:15:26 -0500 Subject: [PATCH 62/74] kernel/vm_manager: Reset region attributes when unmapping a VMA Like the other members related to memory regions, the attributes need to be reset back to their defaults as well. --- src/core/hle/kernel/vm_manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index f39e096ca4..10ad94aa69 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -190,6 +190,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { vma.type = VMAType::Free; vma.permissions = VMAPermission::None; vma.state = MemoryState::Unmapped; + vma.attribute = MemoryAttribute::None; vma.backing_block = nullptr; vma.offset = 0; From 4a6ba5807343b668861efbd8367e39640cafc9f4 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 6 Dec 2018 20:23:44 -0500 Subject: [PATCH 63/74] vfs: Add reinterpret_casts to WriteArray and Object Allows these functions to compile when T is not u8. --- src/core/file_sys/vfs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index e5641b2554..9540947729 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -150,7 +150,7 @@ public: template std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - return Write(data, number_elements * sizeof(T), offset); + return Write(reinterpret_cast(data), number_elements * sizeof(T), offset); } // Writes size bytes starting at memory location data to offset in file. @@ -166,7 +166,7 @@ public: template std::size_t WriteObject(const T& data, std::size_t offset = 0) { static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - return Write(&data, sizeof(T), offset); + return Write(reinterpret_cast(&data), sizeof(T), offset); } // Renames the file to name. Returns whether or not the operation was successsful. From 5c4259ec1a499528e132b1d41f7559f73bfd1143 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 6 Dec 2018 20:25:32 -0500 Subject: [PATCH 64/74] control_metadata: Use value member instead of unique_ptr to store struct Serves no actual purpose in this instance besides making NACP's copy assignment deleted, which is not intended behavior. --- src/core/file_sys/control_metadata.cpp | 20 +++++++++++--------- src/core/file_sys/control_metadata.h | 3 ++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index e065e592fb..fe2077f943 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -36,18 +36,20 @@ std::string LanguageEntry::GetDeveloperName() const { developer_name.size()); } -NACP::NACP(VirtualFile file) : raw(std::make_unique()) { - file->ReadObject(raw.get()); +NACP::NACP() : raw{} {} + +NACP::NACP(VirtualFile file) { + file->ReadObject(&raw); } NACP::~NACP() = default; const LanguageEntry& NACP::GetLanguageEntry(Language language) const { if (language != Language::Default) { - return raw->language_entries.at(static_cast(language)); + return raw.language_entries.at(static_cast(language)); } - for (const auto& language_entry : raw->language_entries) { + for (const auto& language_entry : raw.language_entries) { if (!language_entry.GetApplicationName().empty()) return language_entry; } @@ -65,21 +67,21 @@ std::string NACP::GetDeveloperName(Language language) const { } u64 NACP::GetTitleId() const { - return raw->title_id; + return raw.title_id; } u64 NACP::GetDLCBaseTitleId() const { - return raw->dlc_base_title_id; + return raw.dlc_base_title_id; } std::string NACP::GetVersionString() const { - return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), - raw->version_string.size()); + return Common::StringFromFixedZeroTerminatedBuffer(raw.version_string.data(), + raw.version_string.size()); } std::vector NACP::GetRawBytes() const { std::vector out(sizeof(RawNACP)); - std::memcpy(out.data(), raw.get(), sizeof(RawNACP)); + std::memcpy(out.data(), &raw, sizeof(RawNACP)); return out; } } // namespace FileSys diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index bfaad46b41..d5c2ed2b54 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -72,6 +72,7 @@ extern const std::array LANGUAGE_NAMES; // These store application name, dev name, title id, and other miscellaneous data. class NACP { public: + explicit NACP(); explicit NACP(VirtualFile file); ~NACP(); @@ -84,7 +85,7 @@ public: std::vector GetRawBytes() const; private: - std::unique_ptr raw; + RawNACP raw; }; } // namespace FileSys From 417e1ef09cfcf9157e260721d3059e73f0eca73e Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 6 Dec 2018 20:25:55 -0500 Subject: [PATCH 65/74] control_metadata: Update NACP fields with latest Switchbrew data --- src/core/file_sys/control_metadata.cpp | 8 ++++++++ src/core/file_sys/control_metadata.h | 27 ++++++++++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index fe2077f943..9624df054c 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -79,6 +79,14 @@ std::string NACP::GetVersionString() const { raw.version_string.size()); } +u64 NACP::GetDefaultNormalSaveSize() const { + return raw.normal_save_data_size; +} + +u64 NACP::GetDefaultJournalSaveSize() const { + return raw.journal_sava_data_size; +} + std::vector NACP::GetRawBytes() const { std::vector out(sizeof(RawNACP)); std::memcpy(out.data(), &raw, sizeof(RawNACP)); diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index d5c2ed2b54..9bc2720c9b 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -28,17 +28,30 @@ static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size. // The raw file format of a NACP file. struct RawNACP { std::array language_entries; - INSERT_PADDING_BYTES(0x38); + std::array isbn; + u8 startup_user_account; + INSERT_PADDING_BYTES(2); + u32_le application_attribute; + u32_le supported_languages; + u32_le parental_control; + bool screenshot_enabled; + u8 video_capture_mode; + bool data_loss_confirmation; + INSERT_PADDING_BYTES(1); u64_le title_id; - INSERT_PADDING_BYTES(0x20); + std::array rating_age; std::array version_string; u64_le dlc_base_title_id; u64_le title_id_2; - INSERT_PADDING_BYTES(0x28); + u64_le normal_save_data_size; + u64_le journal_sava_data_size; + INSERT_PADDING_BYTES(0x18); u64_le product_code; - u64_le title_id_3; - std::array title_id_array; - INSERT_PADDING_BYTES(0x8); + std::array local_communication; + u8 logo_type; + u8 logo_handling; + bool runtime_add_on_content_install; + INSERT_PADDING_BYTES(5); u64_le title_id_update; std::array bcat_passphrase; INSERT_PADDING_BYTES(0xEC0); @@ -82,6 +95,8 @@ public: u64 GetTitleId() const; u64 GetDLCBaseTitleId() const; std::string GetVersionString() const; + u64 GetDefaultNormalSaveSize() const; + u64 GetDefaultJournalSaveSize() const; std::vector GetRawBytes() const; private: From 0756f29a2c99070a6c676b39d797015d140ad7ac Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 6 Dec 2018 20:27:50 -0500 Subject: [PATCH 66/74] loader: Add accessor for game control data --- src/core/loader/loader.h | 10 +++++++--- src/core/loader/nsp.cpp | 4 ++-- src/core/loader/nsp.h | 2 +- src/core/loader/xci.cpp | 5 +++-- src/core/loader/xci.h | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 0838e303b3..cfd67adc0f 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -15,6 +15,10 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/vfs.h" +namespace FileSys { +class NACP; +} // namespace FileSys + namespace Kernel { struct AddressMapping; class Process; @@ -245,11 +249,11 @@ public: } /** - * Get the developer of the application - * @param developer Reference to store the application developer into + * Get the control data (CNMT) of the application + * @param control Reference to store the application control data into * @return ResultStatus result of function */ - virtual ResultStatus ReadDeveloper(std::string& developer) { + virtual ResultStatus ReadControlData(FileSys::NACP& control) { return ResultStatus::ErrorNotImplemented; } diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index b4ab88ae8a..4d4b44571d 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -152,10 +152,10 @@ ResultStatus AppLoader_NSP::ReadTitle(std::string& title) { return ResultStatus::Success; } -ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) { +ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) { if (nacp_file == nullptr) return ResultStatus::ErrorNoControl; - developer = nacp_file->GetDeveloperName(); + nacp = *nacp_file; return ResultStatus::Success; } } // namespace Loader diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 2b1e0719bb..32eb0193d2 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h @@ -43,7 +43,7 @@ public: ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadIcon(std::vector& buffer) override; ResultStatus ReadTitle(std::string& title) override; - ResultStatus ReadDeveloper(std::string& developer) override; + ResultStatus ReadControlData(FileSys::NACP& nacp) override; private: std::unique_ptr nsp; diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index bd5a83b49f..e67e43c69d 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -121,10 +121,11 @@ ResultStatus AppLoader_XCI::ReadTitle(std::string& title) { return ResultStatus::Success; } -ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) { +ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) { if (nacp_file == nullptr) return ResultStatus::ErrorNoControl; - developer = nacp_file->GetDeveloperName(); + control = *nacp_file; return ResultStatus::Success; } + } // namespace Loader diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 15d1b1a235..9d3923f62b 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h @@ -43,7 +43,7 @@ public: ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadIcon(std::vector& buffer) override; ResultStatus ReadTitle(std::string& title) override; - ResultStatus ReadDeveloper(std::string& developer) override; + ResultStatus ReadControlData(FileSys::NACP& control) override; private: std::unique_ptr xci; From 4082c4eda6d8282abf0ba7763ed4fd66ecd176f1 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 6 Dec 2018 20:28:51 -0500 Subject: [PATCH 67/74] savedata_factory: Partially implement IVFC save sizes using files This stores a file in the save directory called '.yuzu_save_size' which stores the two save sizes (normal area and journaled area) sequentially as u64s. --- src/core/file_sys/savedata_factory.cpp | 30 ++++++++++++++++++++++++++ src/core/file_sys/savedata_factory.h | 8 +++++++ 2 files changed, 38 insertions(+) diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index d63b7f19b3..54f5b698a3 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -13,6 +13,8 @@ namespace FileSys { +constexpr const char* SAVE_DATA_SIZE_FILENAME = ".yuzu_save_size"; + std::string SaveDataDescriptor::DebugInfo() const { return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]", static_cast(type), title_id, user_id[1], user_id[0], save_id); @@ -132,4 +134,32 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ } } +SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, + u128 user_id) const { + const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + const auto dir = GetOrCreateDirectoryRelative(this->dir, path); + + const auto size_file = dir->GetFile(SAVE_DATA_SIZE_FILENAME); + if (size_file == nullptr || size_file->GetSize() < sizeof(SaveDataSize)) + return {0, 0}; + + SaveDataSize out; + if (size_file->ReadObject(&out) != sizeof(SaveDataSize)) + return {0, 0}; + return out; +} + +void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, + SaveDataSize new_value) { + const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + const auto dir = GetOrCreateDirectoryRelative(this->dir, path); + + const auto size_file = dir->CreateFile(SAVE_DATA_SIZE_FILENAME); + if (size_file == nullptr) + return; + + size_file->Resize(sizeof(SaveDataSize)); + size_file->WriteObject(new_value); +} + } // namespace FileSys diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index bd4919610e..3a1caf2926 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -46,6 +46,11 @@ struct SaveDataDescriptor { }; static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size."); +struct SaveDataSize { + u64 normal; + u64 journal; +}; + /// File system interface to the SaveData archive class SaveDataFactory { public: @@ -60,6 +65,9 @@ public: static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id); + SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; + void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value); + private: VirtualDir dir; }; From 2e6b67a079593e810dd7f27cd4388fef385b9c99 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 6 Dec 2018 20:29:36 -0500 Subject: [PATCH 68/74] filesystem: Populate save data sizes from control data --- .../hle/service/filesystem/filesystem.cpp | 47 +++++++++++++++++++ src/core/hle/service/filesystem/filesystem.h | 6 +++ 2 files changed, 53 insertions(+) diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index b1490e6fac..c6da2df43c 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -8,18 +8,23 @@ #include "common/file_util.h" #include "core/core.h" #include "core/file_sys/bis_factory.h" +#include "core/file_sys/control_metadata.h" #include "core/file_sys/errors.h" #include "core/file_sys/mode.h" +#include "core/file_sys/partition_filesystem.h" +#include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/sdmc_factory.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_offset.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/fsp_ldr.h" #include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_srv.h" +#include "core/loader/loader.h" namespace Service::FileSystem { @@ -28,6 +33,10 @@ namespace Service::FileSystem { // TODO(DarkLordZach): Eventually make this configurable in settings. constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; +// A default size for normal/journal save data size if application control metadata cannot be found. +// This should be large enough to satisfy even the most extreme requirements (~4.2GB) +constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; + static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, std::string_view dir_name_) { std::string dir_name(FileUtil::SanitizePath(dir_name_)); @@ -341,6 +350,44 @@ ResultVal OpenSDMC() { return sdmc_factory->Open(); } +FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) { + if (save_data_factory == nullptr) { + return {0, 0}; + } + + const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id); + + if (value.normal == 0 && value.journal == 0) { + FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE}; + + FileSys::NACP nacp; + const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp); + + if (res != Loader::ResultStatus::Success) { + FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()}; + auto [nacp_unique, discard] = pm.GetControlMetadata(); + + if (nacp_unique != nullptr) { + new_size = {nacp_unique->GetDefaultNormalSaveSize(), + nacp_unique->GetDefaultJournalSaveSize()}; + } + } else { + new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()}; + } + + WriteSaveDataSize(type, title_id, user_id, new_size); + return new_size; + } + + return value; +} + +void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value) { + if (save_data_factory != nullptr) + save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); +} + FileSys::RegisteredCacheUnion GetUnionContents() { return FileSys::RegisteredCacheUnion{ {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}}; diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 965414be02..6fd5e7b230 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -21,9 +21,11 @@ class SDMCFactory; enum class ContentRecordType : u8; enum class Mode : u32; enum class SaveDataSpaceId : u8; +enum class SaveDataType : u8; enum class StorageId : u8; struct SaveDataDescriptor; +struct SaveDataSize; } // namespace FileSys namespace Service { @@ -48,6 +50,10 @@ ResultVal OpenSaveData(FileSys::SaveDataSpaceId space, ResultVal OpenSaveDataSpace(FileSys::SaveDataSpaceId space); ResultVal OpenSDMC(); +FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id); +void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value); + FileSys::RegisteredCacheUnion GetUnionContents(); FileSys::RegisteredCache* GetSystemNANDContents(); From c643f364b4c81543782009ce4bb5a87021fe35ed Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 10 Dec 2018 22:17:45 -0500 Subject: [PATCH 69/74] am: Implement GetSaveDataSize and ExtendSaveData These functions come in a pair and are needed by Smash Ultimate, Minecraft, and Skyrim, amongst others. --- src/core/file_sys/control_metadata.cpp | 2 +- src/core/file_sys/control_metadata.h | 2 +- src/core/file_sys/savedata_factory.cpp | 2 +- src/core/hle/service/am/am.cpp | 47 ++++++++++++++++++- src/core/hle/service/am/am.h | 2 + .../configuration/configure_per_general.cpp | 6 +-- 6 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 9624df054c..83c184750b 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -36,7 +36,7 @@ std::string LanguageEntry::GetDeveloperName() const { developer_name.size()); } -NACP::NACP() : raw{} {} +NACP::NACP() = default; NACP::NACP(VirtualFile file) { file->ReadObject(&raw); diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 9bc2720c9b..7b9cdc9105 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -100,7 +100,7 @@ public: std::vector GetRawBytes() const; private: - RawNACP raw; + RawNACP raw{}; }; } // namespace FileSys diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 54f5b698a3..1913dc956c 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -13,7 +13,7 @@ namespace FileSys { -constexpr const char* SAVE_DATA_SIZE_FILENAME = ".yuzu_save_size"; +constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; std::string SaveDataDescriptor::DebugInfo() const { return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]", diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 5fc02a5211..d13ce4dca2 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -8,6 +8,7 @@ #include #include "audio_core/audio_renderer.h" #include "core/core.h" +#include "core/file_sys/savedata_factory.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" @@ -865,8 +866,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"}, {24, nullptr, "GetLaunchStorageInfoForDebug"}, - {25, nullptr, "ExtendSaveData"}, - {26, nullptr, "GetSaveDataSize"}, + {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, + {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, @@ -1043,6 +1044,48 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) { rb.Push(0); } +void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto type{rp.PopRaw()}; + rp.Skip(1, false); + const auto user_id{rp.PopRaw()}; + const auto new_normal_size{rp.PopRaw()}; + const auto new_journal_size{rp.PopRaw()}; + + LOG_DEBUG(Service_AM, + "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, " + "new_journal={:016X}", + static_cast(type), user_id[1], user_id[0], new_normal_size, new_journal_size); + + FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id, + {new_normal_size, new_journal_size}); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + + // The following value is used upon failure to help the system recover. + // Since we always succeed, this should be 0. + rb.Push(0); +} + +void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto type{rp.PopRaw()}; + rp.Skip(1, false); + const auto user_id{rp.PopRaw()}; + + LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast(type), + user_id[1], user_id[0]); + + const auto size = + FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.Push(size.normal); + rb.Push(size.journal); +} + void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr nvflinger) { auto message_queue = std::make_shared(); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 34c45fadf0..b6113cfdd1 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -206,6 +206,8 @@ private: void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); void NotifyRunning(Kernel::HLERequestContext& ctx); void GetPseudoDeviceId(Kernel::HLERequestContext& ctx); + void ExtendSaveData(Kernel::HLERequestContext& ctx); + void GetSaveDataSize(Kernel::HLERequestContext& ctx); void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp index 5ca493b6df..dffaba5ed2 100644 --- a/src/yuzu/configuration/configure_per_general.cpp +++ b/src/yuzu/configuration/configure_per_general.cpp @@ -108,9 +108,9 @@ void ConfigurePerGameGeneral::loadConfiguration() { if (loader->ReadTitle(title) == Loader::ResultStatus::Success) ui->display_name->setText(QString::fromStdString(title)); - std::string developer; - if (loader->ReadDeveloper(developer) == Loader::ResultStatus::Success) - ui->display_developer->setText(QString::fromStdString(developer)); + FileSys::NACP nacp; + if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) + ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName())); ui->display_version->setText(QStringLiteral("1.0.0")); } From fbe900ba6da524cd0f6831bcafdd373f8a7d650c Mon Sep 17 00:00:00 2001 From: Rodolfo Bogado Date: Thu, 27 Dec 2018 14:39:10 -0300 Subject: [PATCH 70/74] Add missing uintBitsToFloat to SetRegisterToHalfFloat --- src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index a1cef99aec..1bb09e61be 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -493,10 +493,10 @@ public: // pack. I couldn't test this on hardware but it shouldn't really matter since most // of the time when a Mrg_* flag is used both components will be mirrored. That // being said, it deserves a test. - return "((" + GetRegisterAsInteger(reg, 0, false) + + return "uintBitsToFloat((" + GetRegisterAsInteger(reg, 0, false) + " & 0xffff0000) | (packHalf2x16(" + value + ") & 0x0000ffff))"; case Tegra::Shader::HalfMerge::Mrg_H1: - return "((" + GetRegisterAsInteger(reg, 0, false) + + return "uintBitsToFloat((" + GetRegisterAsInteger(reg, 0, false) + " & 0x0000ffff) | (packHalf2x16(" + value + ") & 0xffff0000))"; default: UNREACHABLE(); From fbeaa330a35f93857c249aa5f69dfee6b09eefe0 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Thu, 27 Dec 2018 18:31:31 -0500 Subject: [PATCH 71/74] kernel/process: Remove most allocation functions from Process' interface In all cases that these functions are needed, the VMManager can just be retrieved and used instead of providing the same functions in Process' interface. This also makes it a little nicer dependency-wise, since it gets rid of cases where the VMManager interface was being used, and then switched over to using the interface for a Process instance. Instead, it makes all accesses uniform and uses the VMManager instance for all necessary tasks. All the basic memory mapping functions did was forward to the Process' VMManager instance anyways. --- src/core/hle/kernel/process.cpp | 16 ---------------- src/core/hle/kernel/process.h | 9 +-------- src/core/hle/kernel/svc.cpp | 32 ++++++++++++++++++-------------- src/core/hle/service/ldr/ldr.cpp | 27 ++++++++++++++++----------- 4 files changed, 35 insertions(+), 49 deletions(-) diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 4f209a979f..06a673b9b9 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -198,22 +198,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); } -ResultVal Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { - return vm_manager.HeapAllocate(target, size, perms); -} - -ResultCode Process::HeapFree(VAddr target, u32 size) { - return vm_manager.HeapFree(target, size); -} - -ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { - return vm_manager.MirrorMemory(dst_addr, src_addr, size, state); -} - -ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { - return vm_manager.UnmapRange(dst_addr, size); -} - Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {} Kernel::Process::~Process() {} diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 2c0b20f9eb..ac69562667 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -242,7 +242,7 @@ public: void LoadModule(CodeSet module_, VAddr base_addr); /////////////////////////////////////////////////////////////////////////////////////////////// - // Memory Management + // Thread-local storage management // Marks the next available region as used and returns the address of the slot. VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread); @@ -250,13 +250,6 @@ public: // Frees a used TLS slot identified by the given address void FreeTLSSlot(VAddr tls_address); - ResultVal HeapAllocate(VAddr target, u64 size, VMAPermission perms); - ResultCode HeapFree(VAddr target, u32 size); - - ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state); - - ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); - private: explicit Process(KernelCore& kernel); ~Process() override; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2e80b48c2e..b955f9839a 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -190,10 +190,16 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { return ERR_INVALID_SIZE; } - auto& process = *Core::CurrentProcess(); - const VAddr heap_base = process.VMManager().GetHeapRegionBaseAddress(); - CASCADE_RESULT(*heap_addr, - process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite)); + auto& vm_manager = Core::CurrentProcess()->VMManager(); + const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress(); + const auto alloc_result = + vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite); + + if (alloc_result.Failed()) { + return alloc_result.Code(); + } + + *heap_addr = *alloc_result; return RESULT_SUCCESS; } @@ -307,15 +313,14 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); - auto* const current_process = Core::CurrentProcess(); - const auto& vm_manager = current_process->VMManager(); - + auto& vm_manager = Core::CurrentProcess()->VMManager(); const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); - if (result != RESULT_SUCCESS) { + + if (result.IsError()) { return result; } - return current_process->MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack); + return vm_manager.MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack); } /// Unmaps a region that was previously mapped with svcMapMemory @@ -323,15 +328,14 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); - auto* const current_process = Core::CurrentProcess(); - const auto& vm_manager = current_process->VMManager(); - + auto& vm_manager = Core::CurrentProcess()->VMManager(); const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); - if (result != RESULT_SUCCESS) { + + if (result.IsError()) { return result; } - return current_process->UnmapMemory(dst_addr, src_addr, size); + return vm_manager.UnmapRange(dst_addr, size); } /// Connect to an OS service given the port name, returns the handle to the port to out diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 13bcefe078..9df7ac50fd 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -318,14 +318,18 @@ public: return; } - ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size, - Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); - ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS); + ASSERT(vm_manager + .MirrorMemory(*map_address, nro_addr, nro_size, + Kernel::MemoryState::ModuleCodeStatic) + .IsSuccess()); + ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); if (bss_size > 0) { - ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size, - Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); - ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS); + ASSERT(vm_manager + .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, + Kernel::MemoryState::ModuleCodeStatic) + .IsSuccess()); + ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); } vm_manager.ReprotectRange(*map_address, header.text_size, @@ -380,13 +384,14 @@ public: return; } - auto* process = Core::CurrentProcess(); - auto& vm_manager = process->VMManager(); + auto& vm_manager = Core::CurrentProcess()->VMManager(); const auto& nro_size = iter->second.size; - ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size, - Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); - ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS); + ASSERT(vm_manager + .MirrorMemory(heap_addr, mapped_addr, nro_size, + Kernel::MemoryState::ModuleCodeStatic) + .IsSuccess()); + ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess()); Core::System::GetInstance().InvalidateCpuInstructionCaches(); From 9aa68212d9c2f803d316a89e70318700f44f6c81 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Thu, 27 Dec 2018 19:16:43 -0500 Subject: [PATCH 72/74] file_sys/program_metadata: Print out more descriptive address space descriptions Provides extra information that makes it easier to tell if an executable being run is using a 36-bit address space or a 39-bit address space. While we don't support AArch32 executables yet, this also puts in distinguishing information for the 32-bit address space types as well. --- src/core/file_sys/program_metadata.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index e90c8c2de0..d3e00437f4 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -92,16 +92,20 @@ void ProgramMetadata::Print() const { LOG_DEBUG(Service_FS, " > 64-bit instructions: {}", npdm_header.has_64_bit_instructions ? "YES" : "NO"); - auto address_space = "Unknown"; + const char* address_space = "Unknown"; switch (npdm_header.address_space_type) { case ProgramAddressSpaceType::Is36Bit: + address_space = "64-bit (36-bit address space)"; + break; case ProgramAddressSpaceType::Is39Bit: - address_space = "64-bit"; + address_space = "64-bit (39-bit address space)"; break; case ProgramAddressSpaceType::Is32Bit: - case ProgramAddressSpaceType::Is32BitNoMap: address_space = "32-bit"; break; + case ProgramAddressSpaceType::Is32BitNoMap: + address_space = "32-bit (no map region)"; + break; } LOG_DEBUG(Service_FS, " > Address space: {}\n", address_space); From a73c7c73eb3c7edffd3c12e2becb55f86df1bc68 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 28 Dec 2018 14:04:44 -0500 Subject: [PATCH 73/74] audio_core: Convert LOG_CRITICAL + UNREACHABLE over to UNIMPLEMENTED/UNIMPLEMENTED_MSG These two macros being used in tandem were used prior to the introduction of UNIMPLEMENTED and UNIMPLEMENTED_MSG. This provides equivalent behavior, just with less typing/reading involved. --- src/audio_core/audio_out.cpp | 3 +-- src/audio_core/audio_renderer.cpp | 6 ++---- src/audio_core/stream.cpp | 7 +++---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index cbba17632f..50d2a1ed35 100644 --- a/src/audio_core/audio_out.cpp +++ b/src/audio_core/audio_out.cpp @@ -22,8 +22,7 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) { return Stream::Format::Multi51Channel16; } - LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unimplemented num_channels={}", num_channels); return {}; } diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 2683f3a5fa..00c0265110 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -260,8 +260,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() { break; } default: - LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format); break; } @@ -280,8 +279,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() { break; } default: - LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count); break; } diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index f35628e45f..874673c4e5 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -28,8 +28,7 @@ u32 Stream::GetNumChannels() const { case Format::Multi51Channel16: return 6; } - LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast(format)); - UNREACHABLE(); + UNIMPLEMENTED_MSG("Unimplemented format={}", static_cast(format)); return {}; } @@ -49,7 +48,7 @@ void Stream::Play() { void Stream::Stop() { state = State::Stopped; - ASSERT_MSG(false, "Unimplemented"); + UNIMPLEMENTED(); } Stream::State Stream::GetState() const { @@ -120,7 +119,7 @@ bool Stream::QueueBuffer(BufferPtr&& buffer) { } bool Stream::ContainsBuffer(Buffer::Tag tag) const { - ASSERT_MSG(false, "Unimplemented"); + UNIMPLEMENTED(); return {}; } From 2020ba06e10d46eafa8ddd28460ebbdc87df3db5 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 27 Dec 2018 20:43:06 -0500 Subject: [PATCH 74/74] gpu: Remove PixelFormat G8R8U and G8R8S, as they do not seem to exist. - Fixes UI rendering issues in The Legend of Zelda: Breath of the Wild. --- src/video_core/morton.cpp | 4 - .../renderer_opengl/gl_rasterizer_cache.cpp | 22 ----- src/video_core/surface.cpp | 7 +- src/video_core/surface.h | 92 +++++++++---------- 4 files changed, 46 insertions(+), 79 deletions(-) diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp index 47e76d8fee..b68f4fb131 100644 --- a/src/video_core/morton.cpp +++ b/src/video_core/morton.cpp @@ -66,8 +66,6 @@ static constexpr ConversionArray morton_to_linear_fns = { MortonCopy, MortonCopy, MortonCopy, - MortonCopy, - MortonCopy, MortonCopy, MortonCopy, MortonCopy, @@ -138,8 +136,6 @@ static constexpr ConversionArray linear_to_morton_fns = { MortonCopy, // TODO(Subv): Swizzling ASTC formats are not supported nullptr, - MortonCopy, - MortonCopy, MortonCopy, MortonCopy, MortonCopy, diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 7ea07631ab..75b4fe88dc 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -288,8 +288,6 @@ static constexpr std::array tex {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float, true}, // BC6H_SF16 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 - {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U - {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // G8R8S {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8 {GL_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F {GL_RG32F, GL_RG, GL_FLOAT, ComponentType::Float, false}, // RG32F @@ -620,18 +618,6 @@ static void ConvertS8Z24ToZ24S8(std::vector& data, u32 width, u32 height, bo } } -static void ConvertG8R8ToR8G8(std::vector& data, u32 width, u32 height) { - constexpr auto bpp{GetBytesPerPixel(PixelFormat::G8R8U)}; - for (std::size_t y = 0; y < height; ++y) { - for (std::size_t x = 0; x < width; ++x) { - const std::size_t offset{bpp * (y * width + x)}; - const u8 temp{data[offset]}; - data[offset] = data[offset + 1]; - data[offset + 1] = temp; - } - } -} - /** * Helper function to perform software conversion (as needed) when loading a buffer from Switch * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with @@ -664,12 +650,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector& data, PixelForma // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24. ConvertS8Z24ToZ24S8(data, width, height, false); break; - - case PixelFormat::G8R8U: - case PixelFormat::G8R8S: - // Convert the G8R8 color format to R8G8, as OpenGL does not support G8R8. - ConvertG8R8ToR8G8(data, width, height); - break; } } @@ -681,8 +661,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector& data, PixelForma static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector& data, PixelFormat pixel_format, u32 width, u32 height) { switch (pixel_format) { - case PixelFormat::G8R8U: - case PixelFormat::G8R8S: case PixelFormat::ASTC_2D_4X4: case PixelFormat::ASTC_2D_8X8: case PixelFormat::ASTC_2D_4X4_SRGB: diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index a97b1562b5..1a344229f0 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -196,11 +196,14 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast(component_type)); UNREACHABLE(); case Tegra::Texture::TextureFormat::G8R8: + // TextureFormat::G8R8 is actually ordered red then green, as such we can use + // PixelFormat::RG8U and PixelFormat::RG8S. This was tested with The Legend of Zelda: Breath + // of the Wild, which uses this format to render the hearts on the UI. switch (component_type) { case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::G8R8U; + return PixelFormat::RG8U; case Tegra::Texture::ComponentType::SNORM: - return PixelFormat::G8R8S; + return PixelFormat::RG8S; } LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast(component_type)); UNREACHABLE(); diff --git a/src/video_core/surface.h b/src/video_core/surface.h index e23cfecbc2..c2259c3c2b 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -38,57 +38,55 @@ enum class PixelFormat { BC6H_UF16 = 20, BC6H_SF16 = 21, ASTC_2D_4X4 = 22, - G8R8U = 23, - G8R8S = 24, - BGRA8 = 25, - RGBA32F = 26, - RG32F = 27, - R32F = 28, - R16F = 29, - R16U = 30, - R16S = 31, - R16UI = 32, - R16I = 33, - RG16 = 34, - RG16F = 35, - RG16UI = 36, - RG16I = 37, - RG16S = 38, - RGB32F = 39, - RGBA8_SRGB = 40, - RG8U = 41, - RG8S = 42, - RG32UI = 43, - R32UI = 44, - ASTC_2D_8X8 = 45, - ASTC_2D_8X5 = 46, - ASTC_2D_5X4 = 47, - BGRA8_SRGB = 48, - DXT1_SRGB = 49, - DXT23_SRGB = 50, - DXT45_SRGB = 51, - BC7U_SRGB = 52, - ASTC_2D_4X4_SRGB = 53, - ASTC_2D_8X8_SRGB = 54, - ASTC_2D_8X5_SRGB = 55, - ASTC_2D_5X4_SRGB = 56, - ASTC_2D_5X5 = 57, - ASTC_2D_5X5_SRGB = 58, - ASTC_2D_10X8 = 59, - ASTC_2D_10X8_SRGB = 60, + BGRA8 = 23, + RGBA32F = 24, + RG32F = 25, + R32F = 26, + R16F = 27, + R16U = 28, + R16S = 29, + R16UI = 30, + R16I = 31, + RG16 = 32, + RG16F = 33, + RG16UI = 34, + RG16I = 35, + RG16S = 36, + RGB32F = 37, + RGBA8_SRGB = 38, + RG8U = 39, + RG8S = 40, + RG32UI = 41, + R32UI = 42, + ASTC_2D_8X8 = 43, + ASTC_2D_8X5 = 44, + ASTC_2D_5X4 = 45, + BGRA8_SRGB = 46, + DXT1_SRGB = 47, + DXT23_SRGB = 48, + DXT45_SRGB = 49, + BC7U_SRGB = 50, + ASTC_2D_4X4_SRGB = 51, + ASTC_2D_8X8_SRGB = 52, + ASTC_2D_8X5_SRGB = 53, + ASTC_2D_5X4_SRGB = 54, + ASTC_2D_5X5 = 55, + ASTC_2D_5X5_SRGB = 56, + ASTC_2D_10X8 = 57, + ASTC_2D_10X8_SRGB = 58, MaxColorFormat, // Depth formats - Z32F = 61, - Z16 = 62, + Z32F = 59, + Z16 = 60, MaxDepthFormat, // DepthStencil formats - Z24S8 = 63, - S8Z24 = 64, - Z32FS8 = 65, + Z24S8 = 61, + S8Z24 = 62, + Z32FS8 = 63, MaxDepthStencilFormat, @@ -149,8 +147,6 @@ constexpr std::array compression_factor_table = {{ 4, // BC6H_UF16 4, // BC6H_SF16 4, // ASTC_2D_4X4 - 1, // G8R8U - 1, // G8R8S 1, // BGRA8 1, // RGBA32F 1, // RG32F @@ -232,8 +228,6 @@ constexpr std::array block_width_table = {{ 4, // BC6H_UF16 4, // BC6H_SF16 4, // ASTC_2D_4X4 - 1, // G8R8U - 1, // G8R8S 1, // BGRA8 1, // RGBA32F 1, // RG32F @@ -309,8 +303,6 @@ constexpr std::array block_height_table = {{ 4, // BC6H_UF16 4, // BC6H_SF16 4, // ASTC_2D_4X4 - 1, // G8R8U - 1, // G8R8S 1, // BGRA8 1, // RGBA32F 1, // RG32F @@ -386,8 +378,6 @@ constexpr std::array bpp_table = {{ 128, // BC6H_UF16 128, // BC6H_SF16 128, // ASTC_2D_4X4 - 16, // G8R8U - 16, // G8R8S 32, // BGRA8 128, // RGBA32F 64, // RG32F