diff options
Diffstat (limited to 'src')
45 files changed, 875 insertions, 392 deletions
diff --git a/src/citra/config.cpp b/src/citra/config.cpp index a48ef08c7..45c28ad09 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp | |||
| @@ -162,6 +162,8 @@ void Config::ReadValues() { | |||
| 162 | sdl2_config->GetBoolean("WebService", "enable_telemetry", true); | 162 | sdl2_config->GetBoolean("WebService", "enable_telemetry", true); |
| 163 | Settings::values.telemetry_endpoint_url = sdl2_config->Get( | 163 | Settings::values.telemetry_endpoint_url = sdl2_config->Get( |
| 164 | "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry"); | 164 | "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry"); |
| 165 | Settings::values.verify_endpoint_url = sdl2_config->Get( | ||
| 166 | "WebService", "verify_endpoint_url", "https://services.citra-emu.org/api/profile"); | ||
| 165 | Settings::values.citra_username = sdl2_config->Get("WebService", "citra_username", ""); | 167 | Settings::values.citra_username = sdl2_config->Get("WebService", "citra_username", ""); |
| 166 | Settings::values.citra_token = sdl2_config->Get("WebService", "citra_token", ""); | 168 | Settings::values.citra_token = sdl2_config->Get("WebService", "citra_token", ""); |
| 167 | } | 169 | } |
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 4b13a2e1b..59faf773f 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h | |||
| @@ -185,6 +185,8 @@ gdbstub_port=24689 | |||
| 185 | enable_telemetry = | 185 | enable_telemetry = |
| 186 | # Endpoint URL for submitting telemetry data | 186 | # Endpoint URL for submitting telemetry data |
| 187 | telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry | 187 | telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry |
| 188 | # Endpoint URL to verify the username and token | ||
| 189 | verify_endpoint_url = https://services.citra-emu.org/api/profile | ||
| 188 | # Username and token for Citra Web Service | 190 | # Username and token for Citra Web Service |
| 189 | # See https://services.citra-emu.org/ for more info | 191 | # See https://services.citra-emu.org/ for more info |
| 190 | citra_username = | 192 | citra_username = |
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index e0a19fd9e..add7566c2 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -79,6 +79,7 @@ set(UIS | |||
| 79 | main.ui | 79 | main.ui |
| 80 | ) | 80 | ) |
| 81 | 81 | ||
| 82 | file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*) | ||
| 82 | file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) | 83 | file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) |
| 83 | 84 | ||
| 84 | create_directory_groups(${SRCS} ${HEADERS} ${UIS}) | 85 | create_directory_groups(${SRCS} ${HEADERS} ${UIS}) |
| @@ -92,10 +93,10 @@ endif() | |||
| 92 | if (APPLE) | 93 | if (APPLE) |
| 93 | set(MACOSX_ICON "../../dist/citra.icns") | 94 | set(MACOSX_ICON "../../dist/citra.icns") |
| 94 | set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) | 95 | set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) |
| 95 | add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES} ${MACOSX_ICON}) | 96 | add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${ICONS} ${THEMES} ${MACOSX_ICON}) |
| 96 | set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) | 97 | set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) |
| 97 | else() | 98 | else() |
| 98 | add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES}) | 99 | add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${ICONS} ${THEMES}) |
| 99 | endif() | 100 | endif() |
| 100 | target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core) | 101 | target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core) |
| 101 | target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets) | 102 | target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets) |
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index ef114aad3..5261f4c4c 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp | |||
| @@ -146,6 +146,10 @@ void Config::ReadValues() { | |||
| 146 | qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry") | 146 | qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry") |
| 147 | .toString() | 147 | .toString() |
| 148 | .toStdString(); | 148 | .toStdString(); |
| 149 | Settings::values.verify_endpoint_url = | ||
| 150 | qt_config->value("verify_endpoint_url", "https://services.citra-emu.org/api/profile") | ||
| 151 | .toString() | ||
| 152 | .toStdString(); | ||
| 149 | Settings::values.citra_username = qt_config->value("citra_username").toString().toStdString(); | 153 | Settings::values.citra_username = qt_config->value("citra_username").toString().toStdString(); |
| 150 | Settings::values.citra_token = qt_config->value("citra_token").toString().toStdString(); | 154 | Settings::values.citra_token = qt_config->value("citra_token").toString().toStdString(); |
| 151 | qt_config->endGroup(); | 155 | qt_config->endGroup(); |
| @@ -293,6 +297,8 @@ void Config::SaveValues() { | |||
| 293 | qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry); | 297 | qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry); |
| 294 | qt_config->setValue("telemetry_endpoint_url", | 298 | qt_config->setValue("telemetry_endpoint_url", |
| 295 | QString::fromStdString(Settings::values.telemetry_endpoint_url)); | 299 | QString::fromStdString(Settings::values.telemetry_endpoint_url)); |
| 300 | qt_config->setValue("verify_endpoint_url", | ||
| 301 | QString::fromStdString(Settings::values.verify_endpoint_url)); | ||
| 296 | qt_config->setValue("citra_username", QString::fromStdString(Settings::values.citra_username)); | 302 | qt_config->setValue("citra_username", QString::fromStdString(Settings::values.citra_username)); |
| 297 | qt_config->setValue("citra_token", QString::fromStdString(Settings::values.citra_token)); | 303 | qt_config->setValue("citra_token", QString::fromStdString(Settings::values.citra_token)); |
| 298 | qt_config->endGroup(); | 304 | qt_config->endGroup(); |
diff --git a/src/citra_qt/configuration/configure_web.cpp b/src/citra_qt/configuration/configure_web.cpp index 8715fb018..38ce19c0f 100644 --- a/src/citra_qt/configuration/configure_web.cpp +++ b/src/citra_qt/configuration/configure_web.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QMessageBox> | ||
| 5 | #include "citra_qt/configuration/configure_web.h" | 6 | #include "citra_qt/configuration/configure_web.h" |
| 6 | #include "core/settings.h" | 7 | #include "core/settings.h" |
| 7 | #include "core/telemetry_session.h" | 8 | #include "core/telemetry_session.h" |
| @@ -11,7 +12,9 @@ ConfigureWeb::ConfigureWeb(QWidget* parent) | |||
| 11 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) { | 12 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) { |
| 12 | ui->setupUi(this); | 13 | ui->setupUi(this); |
| 13 | connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this, | 14 | connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this, |
| 14 | &ConfigureWeb::refreshTelemetryID); | 15 | &ConfigureWeb::RefreshTelemetryID); |
| 16 | connect(ui->button_verify_login, &QPushButton::clicked, this, &ConfigureWeb::VerifyLogin); | ||
| 17 | connect(this, &ConfigureWeb::LoginVerified, this, &ConfigureWeb::OnLoginVerified); | ||
| 15 | 18 | ||
| 16 | this->setConfiguration(); | 19 | this->setConfiguration(); |
| 17 | } | 20 | } |
| @@ -34,19 +37,66 @@ void ConfigureWeb::setConfiguration() { | |||
| 34 | ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry); | 37 | ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry); |
| 35 | ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username)); | 38 | ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username)); |
| 36 | ui->edit_token->setText(QString::fromStdString(Settings::values.citra_token)); | 39 | ui->edit_token->setText(QString::fromStdString(Settings::values.citra_token)); |
| 40 | // Connect after setting the values, to avoid calling OnLoginChanged now | ||
| 41 | connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged); | ||
| 42 | connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged); | ||
| 37 | ui->label_telemetry_id->setText("Telemetry ID: 0x" + | 43 | ui->label_telemetry_id->setText("Telemetry ID: 0x" + |
| 38 | QString::number(Core::GetTelemetryId(), 16).toUpper()); | 44 | QString::number(Core::GetTelemetryId(), 16).toUpper()); |
| 45 | user_verified = true; | ||
| 39 | } | 46 | } |
| 40 | 47 | ||
| 41 | void ConfigureWeb::applyConfiguration() { | 48 | void ConfigureWeb::applyConfiguration() { |
| 42 | Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked(); | 49 | Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked(); |
| 43 | Settings::values.citra_username = ui->edit_username->text().toStdString(); | 50 | if (user_verified) { |
| 44 | Settings::values.citra_token = ui->edit_token->text().toStdString(); | 51 | Settings::values.citra_username = ui->edit_username->text().toStdString(); |
| 52 | Settings::values.citra_token = ui->edit_token->text().toStdString(); | ||
| 53 | } else { | ||
| 54 | QMessageBox::warning(this, tr("Username and token not verfied"), | ||
| 55 | tr("Username and token were not verified. The changes to your " | ||
| 56 | "username and/or token have not been saved.")); | ||
| 57 | } | ||
| 45 | Settings::Apply(); | 58 | Settings::Apply(); |
| 46 | } | 59 | } |
| 47 | 60 | ||
| 48 | void ConfigureWeb::refreshTelemetryID() { | 61 | void ConfigureWeb::RefreshTelemetryID() { |
| 49 | const u64 new_telemetry_id{Core::RegenerateTelemetryId()}; | 62 | const u64 new_telemetry_id{Core::RegenerateTelemetryId()}; |
| 50 | ui->label_telemetry_id->setText("Telemetry ID: 0x" + | 63 | ui->label_telemetry_id->setText("Telemetry ID: 0x" + |
| 51 | QString::number(new_telemetry_id, 16).toUpper()); | 64 | QString::number(new_telemetry_id, 16).toUpper()); |
| 52 | } | 65 | } |
| 66 | |||
| 67 | void ConfigureWeb::OnLoginChanged() { | ||
| 68 | if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) { | ||
| 69 | user_verified = true; | ||
| 70 | ui->label_username_verified->setPixmap(QPixmap(":/icons/checked.png")); | ||
| 71 | ui->label_token_verified->setPixmap(QPixmap(":/icons/checked.png")); | ||
| 72 | } else { | ||
| 73 | user_verified = false; | ||
| 74 | ui->label_username_verified->setPixmap(QPixmap(":/icons/failed.png")); | ||
| 75 | ui->label_token_verified->setPixmap(QPixmap(":/icons/failed.png")); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | void ConfigureWeb::VerifyLogin() { | ||
| 80 | verified = | ||
| 81 | Core::VerifyLogin(ui->edit_username->text().toStdString(), | ||
| 82 | ui->edit_token->text().toStdString(), [&]() { emit LoginVerified(); }); | ||
| 83 | ui->button_verify_login->setDisabled(true); | ||
| 84 | ui->button_verify_login->setText(tr("Verifying")); | ||
| 85 | } | ||
| 86 | |||
| 87 | void ConfigureWeb::OnLoginVerified() { | ||
| 88 | ui->button_verify_login->setEnabled(true); | ||
| 89 | ui->button_verify_login->setText(tr("Verify")); | ||
| 90 | if (verified.get()) { | ||
| 91 | user_verified = true; | ||
| 92 | ui->label_username_verified->setPixmap(QPixmap(":/icons/checked.png")); | ||
| 93 | ui->label_token_verified->setPixmap(QPixmap(":/icons/checked.png")); | ||
| 94 | } else { | ||
| 95 | ui->label_username_verified->setPixmap(QPixmap(":/icons/failed.png")); | ||
| 96 | ui->label_token_verified->setPixmap(QPixmap(":/icons/failed.png")); | ||
| 97 | QMessageBox::critical( | ||
| 98 | this, tr("Verification failed"), | ||
| 99 | tr("Verification failed. Check that you have entered your username and token " | ||
| 100 | "correctly, and that your internet connection is working.")); | ||
| 101 | } | ||
| 102 | } | ||
diff --git a/src/citra_qt/configuration/configure_web.h b/src/citra_qt/configuration/configure_web.h index 20bc254b9..ad2d58f6e 100644 --- a/src/citra_qt/configuration/configure_web.h +++ b/src/citra_qt/configuration/configure_web.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <future> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <QWidget> | 9 | #include <QWidget> |
| 9 | 10 | ||
| @@ -21,10 +22,19 @@ public: | |||
| 21 | void applyConfiguration(); | 22 | void applyConfiguration(); |
| 22 | 23 | ||
| 23 | public slots: | 24 | public slots: |
| 24 | void refreshTelemetryID(); | 25 | void RefreshTelemetryID(); |
| 26 | void OnLoginChanged(); | ||
| 27 | void VerifyLogin(); | ||
| 28 | void OnLoginVerified(); | ||
| 29 | |||
| 30 | signals: | ||
| 31 | void LoginVerified(); | ||
| 25 | 32 | ||
| 26 | private: | 33 | private: |
| 27 | void setConfiguration(); | 34 | void setConfiguration(); |
| 28 | 35 | ||
| 36 | bool user_verified = true; | ||
| 37 | std::future<bool> verified; | ||
| 38 | |||
| 29 | std::unique_ptr<Ui::ConfigureWeb> ui; | 39 | std::unique_ptr<Ui::ConfigureWeb> ui; |
| 30 | }; | 40 | }; |
diff --git a/src/citra_qt/configuration/configure_web.ui b/src/citra_qt/configuration/configure_web.ui index d8d283fad..dd996ab62 100644 --- a/src/citra_qt/configuration/configure_web.ui +++ b/src/citra_qt/configuration/configure_web.ui | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | <rect> | 6 | <rect> |
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>400</width> | 9 | <width>926</width> |
| 10 | <height>300</height> | 10 | <height>561</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -31,14 +31,30 @@ | |||
| 31 | </item> | 31 | </item> |
| 32 | <item> | 32 | <item> |
| 33 | <layout class="QGridLayout" name="gridLayoutCitraUsername"> | 33 | <layout class="QGridLayout" name="gridLayoutCitraUsername"> |
| 34 | <item row="0" column="0"> | 34 | <item row="2" column="3"> |
| 35 | <widget class="QLabel" name="label_username"> | 35 | <widget class="QPushButton" name="button_verify_login"> |
| 36 | <property name="sizePolicy"> | ||
| 37 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||
| 38 | <horstretch>0</horstretch> | ||
| 39 | <verstretch>0</verstretch> | ||
| 40 | </sizepolicy> | ||
| 41 | </property> | ||
| 42 | <property name="layoutDirection"> | ||
| 43 | <enum>Qt::RightToLeft</enum> | ||
| 44 | </property> | ||
| 36 | <property name="text"> | 45 | <property name="text"> |
| 37 | <string>Username: </string> | 46 | <string>Verify</string> |
| 38 | </property> | 47 | </property> |
| 39 | </widget> | 48 | </widget> |
| 40 | </item> | 49 | </item> |
| 41 | <item row="0" column="1"> | 50 | <item row="2" column="0"> |
| 51 | <widget class="QLabel" name="web_signup_link"> | ||
| 52 | <property name="text"> | ||
| 53 | <string>Sign up</string> | ||
| 54 | </property> | ||
| 55 | </widget> | ||
| 56 | </item> | ||
| 57 | <item row="0" column="1" colspan="3"> | ||
| 42 | <widget class="QLineEdit" name="edit_username"> | 58 | <widget class="QLineEdit" name="edit_username"> |
| 43 | <property name="maxLength"> | 59 | <property name="maxLength"> |
| 44 | <number>36</number> | 60 | <number>36</number> |
| @@ -52,7 +68,22 @@ | |||
| 52 | </property> | 68 | </property> |
| 53 | </widget> | 69 | </widget> |
| 54 | </item> | 70 | </item> |
| 55 | <item row="1" column="1"> | 71 | <item row="1" column="4"> |
| 72 | <widget class="QLabel" name="label_token_verified"> | ||
| 73 | </widget> | ||
| 74 | </item> | ||
| 75 | <item row="0" column="0"> | ||
| 76 | <widget class="QLabel" name="label_username"> | ||
| 77 | <property name="text"> | ||
| 78 | <string>Username: </string> | ||
| 79 | </property> | ||
| 80 | </widget> | ||
| 81 | </item> | ||
| 82 | <item row="0" column="4"> | ||
| 83 | <widget class="QLabel" name="label_username_verified"> | ||
| 84 | </widget> | ||
| 85 | </item> | ||
| 86 | <item row="1" column="1" colspan="3"> | ||
| 56 | <widget class="QLineEdit" name="edit_token"> | 87 | <widget class="QLineEdit" name="edit_token"> |
| 57 | <property name="maxLength"> | 88 | <property name="maxLength"> |
| 58 | <number>36</number> | 89 | <number>36</number> |
| @@ -62,13 +93,6 @@ | |||
| 62 | </property> | 93 | </property> |
| 63 | </widget> | 94 | </widget> |
| 64 | </item> | 95 | </item> |
| 65 | <item row="2" column="0"> | ||
| 66 | <widget class="QLabel" name="web_signup_link"> | ||
| 67 | <property name="text"> | ||
| 68 | <string>Sign up</string> | ||
| 69 | </property> | ||
| 70 | </widget> | ||
| 71 | </item> | ||
| 72 | <item row="2" column="1"> | 96 | <item row="2" column="1"> |
| 73 | <widget class="QLabel" name="web_token_info_link"> | 97 | <widget class="QLabel" name="web_token_info_link"> |
| 74 | <property name="text"> | 98 | <property name="text"> |
| @@ -76,6 +100,19 @@ | |||
| 76 | </property> | 100 | </property> |
| 77 | </widget> | 101 | </widget> |
| 78 | </item> | 102 | </item> |
| 103 | <item row="2" column="2"> | ||
| 104 | <spacer name="horizontalSpacer"> | ||
| 105 | <property name="orientation"> | ||
| 106 | <enum>Qt::Horizontal</enum> | ||
| 107 | </property> | ||
| 108 | <property name="sizeHint" stdset="0"> | ||
| 109 | <size> | ||
| 110 | <width>40</width> | ||
| 111 | <height>20</height> | ||
| 112 | </size> | ||
| 113 | </property> | ||
| 114 | </spacer> | ||
| 115 | </item> | ||
| 79 | </layout> | 116 | </layout> |
| 80 | </item> | 117 | </item> |
| 81 | </layout> | 118 | </layout> |
| @@ -105,17 +142,17 @@ | |||
| 105 | <layout class="QGridLayout" name="gridLayoutTelemetryId"> | 142 | <layout class="QGridLayout" name="gridLayoutTelemetryId"> |
| 106 | <item row="0" column="0"> | 143 | <item row="0" column="0"> |
| 107 | <widget class="QLabel" name="label_telemetry_id"> | 144 | <widget class="QLabel" name="label_telemetry_id"> |
| 108 | <property name="text"> | 145 | <property name="text"> |
| 109 | <string>Telemetry ID:</string> | 146 | <string>Telemetry ID:</string> |
| 110 | </property> | 147 | </property> |
| 111 | </widget> | 148 | </widget> |
| 112 | </item> | 149 | </item> |
| 113 | <item row="0" column="1"> | 150 | <item row="0" column="1"> |
| 114 | <widget class="QPushButton" name="button_regenerate_telemetry_id"> | 151 | <widget class="QPushButton" name="button_regenerate_telemetry_id"> |
| 115 | <property name="sizePolicy"> | 152 | <property name="sizePolicy"> |
| 116 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | 153 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
| 117 | <horstretch>0</horstretch> | 154 | <horstretch>0</horstretch> |
| 118 | <verstretch>0</verstretch> | 155 | <verstretch>0</verstretch> |
| 119 | </sizepolicy> | 156 | </sizepolicy> |
| 120 | </property> | 157 | </property> |
| 121 | <property name="layoutDirection"> | 158 | <property name="layoutDirection"> |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 78dec8600..cd1a8de2d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -135,7 +135,8 @@ set(SRCS | |||
| 135 | hle/service/nim/nim_aoc.cpp | 135 | hle/service/nim/nim_aoc.cpp |
| 136 | hle/service/nim/nim_s.cpp | 136 | hle/service/nim/nim_s.cpp |
| 137 | hle/service/nim/nim_u.cpp | 137 | hle/service/nim/nim_u.cpp |
| 138 | hle/service/ns_s.cpp | 138 | hle/service/ns/ns.cpp |
| 139 | hle/service/ns/ns_s.cpp | ||
| 139 | hle/service/nwm/nwm.cpp | 140 | hle/service/nwm/nwm.cpp |
| 140 | hle/service/nwm/nwm_cec.cpp | 141 | hle/service/nwm/nwm_cec.cpp |
| 141 | hle/service/nwm/nwm_ext.cpp | 142 | hle/service/nwm/nwm_ext.cpp |
| @@ -335,7 +336,8 @@ set(HEADERS | |||
| 335 | hle/service/nim/nim_aoc.h | 336 | hle/service/nim/nim_aoc.h |
| 336 | hle/service/nim/nim_s.h | 337 | hle/service/nim/nim_s.h |
| 337 | hle/service/nim/nim_u.h | 338 | hle/service/nim/nim_u.h |
| 338 | hle/service/ns_s.h | 339 | hle/service/ns/ns.h |
| 340 | hle/service/ns/ns_s.h | ||
| 339 | hle/service/nwm/nwm.h | 341 | hle/service/nwm/nwm.h |
| 340 | hle/service/nwm/nwm_cec.h | 342 | hle/service/nwm/nwm_cec.h |
| 341 | hle/service/nwm/nwm_ext.h | 343 | hle/service/nwm/nwm_ext.h |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 0a0b91590..34c5aa381 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -56,7 +56,9 @@ static Dynarmic::UserCallbacks GetUserCallbacks( | |||
| 56 | user_callbacks.memory.Write16 = &Memory::Write16; | 56 | user_callbacks.memory.Write16 = &Memory::Write16; |
| 57 | user_callbacks.memory.Write32 = &Memory::Write32; | 57 | user_callbacks.memory.Write32 = &Memory::Write32; |
| 58 | user_callbacks.memory.Write64 = &Memory::Write64; | 58 | user_callbacks.memory.Write64 = &Memory::Write64; |
| 59 | user_callbacks.page_table = Memory::GetCurrentPageTablePointers(); | 59 | // TODO(Subv): Re-add the page table pointers once dynarmic supports switching page tables at |
| 60 | // runtime. | ||
| 61 | user_callbacks.page_table = nullptr; | ||
| 60 | user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state); | 62 | user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state); |
| 61 | return user_callbacks; | 63 | return user_callbacks; |
| 62 | } | 64 | } |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 5332318cf..59b8768e7 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -137,7 +137,6 @@ void System::Reschedule() { | |||
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | 139 | System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { |
| 140 | Memory::InitMemoryMap(); | ||
| 141 | LOG_DEBUG(HW_Memory, "initialized OK"); | 140 | LOG_DEBUG(HW_Memory, "initialized OK"); |
| 142 | 141 | ||
| 143 | if (Settings::values.use_cpu_jit) { | 142 | if (Settings::values.use_cpu_jit) { |
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 496d07cb5..7f27e9655 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <utility> | 9 | #include <utility> |
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | #include "audio_core/audio_core.h" | ||
| 12 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 13 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 14 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| @@ -24,7 +23,7 @@ | |||
| 24 | 23 | ||
| 25 | namespace Kernel { | 24 | namespace Kernel { |
| 26 | 25 | ||
| 27 | static MemoryRegionInfo memory_regions[3]; | 26 | MemoryRegionInfo memory_regions[3]; |
| 28 | 27 | ||
| 29 | /// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system | 28 | /// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system |
| 30 | /// memory configuration type. | 29 | /// memory configuration type. |
| @@ -96,9 +95,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) { | |||
| 96 | } | 95 | } |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | std::array<u8, Memory::VRAM_SIZE> vram; | ||
| 100 | std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram; | ||
| 101 | |||
| 102 | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { | 98 | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { |
| 103 | using namespace Memory; | 99 | using namespace Memory; |
| 104 | 100 | ||
| @@ -143,30 +139,14 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin | |||
| 143 | return; | 139 | return; |
| 144 | } | 140 | } |
| 145 | 141 | ||
| 146 | // TODO(yuriks): Use GetPhysicalPointer when that becomes independent of the virtual | 142 | u8* target_pointer = Memory::GetPhysicalPointer(area->paddr_base + offset_into_region); |
| 147 | // mappings. | ||
| 148 | u8* target_pointer = nullptr; | ||
| 149 | switch (area->paddr_base) { | ||
| 150 | case VRAM_PADDR: | ||
| 151 | target_pointer = vram.data(); | ||
| 152 | break; | ||
| 153 | case DSP_RAM_PADDR: | ||
| 154 | target_pointer = AudioCore::GetDspMemory().data(); | ||
| 155 | break; | ||
| 156 | case N3DS_EXTRA_RAM_PADDR: | ||
| 157 | target_pointer = n3ds_extra_ram.data(); | ||
| 158 | break; | ||
| 159 | default: | ||
| 160 | UNREACHABLE(); | ||
| 161 | } | ||
| 162 | 143 | ||
| 163 | // TODO(yuriks): This flag seems to have some other effect, but it's unknown what | 144 | // TODO(yuriks): This flag seems to have some other effect, but it's unknown what |
| 164 | MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO; | 145 | MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO; |
| 165 | 146 | ||
| 166 | auto vma = address_space | 147 | auto vma = |
| 167 | .MapBackingMemory(mapping.address, target_pointer + offset_into_region, | 148 | address_space.MapBackingMemory(mapping.address, target_pointer, mapping.size, memory_state) |
| 168 | mapping.size, memory_state) | 149 | .Unwrap(); |
| 169 | .Unwrap(); | ||
| 170 | address_space.Reprotect(vma, | 150 | address_space.Reprotect(vma, |
| 171 | mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); | 151 | mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); |
| 172 | } | 152 | } |
diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h index 08c1a9989..da6bb3563 100644 --- a/src/core/hle/kernel/memory.h +++ b/src/core/hle/kernel/memory.h | |||
| @@ -26,4 +26,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); | |||
| 26 | 26 | ||
| 27 | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); | 27 | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); |
| 28 | void MapSharedPages(VMManager& address_space); | 28 | void MapSharedPages(VMManager& address_space); |
| 29 | |||
| 30 | extern MemoryRegionInfo memory_regions[3]; | ||
| 29 | } // namespace Kernel | 31 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b957c45dd..324415a36 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -171,6 +171,8 @@ static void SwitchContext(Thread* new_thread) { | |||
| 171 | // Cancel any outstanding wakeup events for this thread | 171 | // Cancel any outstanding wakeup events for this thread |
| 172 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); | 172 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); |
| 173 | 173 | ||
| 174 | auto previous_process = Kernel::g_current_process; | ||
| 175 | |||
| 174 | current_thread = new_thread; | 176 | current_thread = new_thread; |
| 175 | 177 | ||
| 176 | ready_queue.remove(new_thread->current_priority, new_thread); | 178 | ready_queue.remove(new_thread->current_priority, new_thread); |
| @@ -178,8 +180,18 @@ static void SwitchContext(Thread* new_thread) { | |||
| 178 | 180 | ||
| 179 | Core::CPU().LoadContext(new_thread->context); | 181 | Core::CPU().LoadContext(new_thread->context); |
| 180 | Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); | 182 | Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); |
| 183 | |||
| 184 | if (previous_process != current_thread->owner_process) { | ||
| 185 | Kernel::g_current_process = current_thread->owner_process; | ||
| 186 | Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; | ||
| 187 | // We have switched processes and thus, page tables, clear the instruction cache so we | ||
| 188 | // don't keep stale data from the previous process. | ||
| 189 | Core::CPU().ClearInstructionCache(); | ||
| 190 | } | ||
| 181 | } else { | 191 | } else { |
| 182 | current_thread = nullptr; | 192 | current_thread = nullptr; |
| 193 | // Note: We do not reset the current process and current page table when idling because | ||
| 194 | // technically we haven't changed processes, our threads are just paused. | ||
| 183 | } | 195 | } |
| 184 | } | 196 | } |
| 185 | 197 | ||
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index cef1f7fa8..7a007c065 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -56,6 +56,10 @@ void VMManager::Reset() { | |||
| 56 | initial_vma.size = MAX_ADDRESS; | 56 | initial_vma.size = MAX_ADDRESS; |
| 57 | vma_map.emplace(initial_vma.base, initial_vma); | 57 | vma_map.emplace(initial_vma.base, initial_vma); |
| 58 | 58 | ||
| 59 | page_table.pointers.fill(nullptr); | ||
| 60 | page_table.attributes.fill(Memory::PageType::Unmapped); | ||
| 61 | page_table.cached_res_count.fill(0); | ||
| 62 | |||
| 59 | UpdatePageTableForVMA(initial_vma); | 63 | UpdatePageTableForVMA(initial_vma); |
| 60 | } | 64 | } |
| 61 | 65 | ||
| @@ -328,16 +332,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { | |||
| 328 | void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { | 332 | void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { |
| 329 | switch (vma.type) { | 333 | switch (vma.type) { |
| 330 | case VMAType::Free: | 334 | case VMAType::Free: |
| 331 | Memory::UnmapRegion(vma.base, vma.size); | 335 | Memory::UnmapRegion(page_table, vma.base, vma.size); |
| 332 | break; | 336 | break; |
| 333 | case VMAType::AllocatedMemoryBlock: | 337 | case VMAType::AllocatedMemoryBlock: |
| 334 | Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_block->data() + vma.offset); | 338 | Memory::MapMemoryRegion(page_table, vma.base, vma.size, |
| 339 | vma.backing_block->data() + vma.offset); | ||
| 335 | break; | 340 | break; |
| 336 | case VMAType::BackingMemory: | 341 | case VMAType::BackingMemory: |
| 337 | Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); | 342 | Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); |
| 338 | break; | 343 | break; |
| 339 | case VMAType::MMIO: | 344 | case VMAType::MMIO: |
| 340 | Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); | 345 | Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler); |
| 341 | break; | 346 | break; |
| 342 | } | 347 | } |
| 343 | } | 348 | } |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 38e0d74d0..1302527bb 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| 12 | #include "core/memory.h" | ||
| 12 | #include "core/mmio.h" | 13 | #include "core/mmio.h" |
| 13 | 14 | ||
| 14 | namespace Kernel { | 15 | namespace Kernel { |
| @@ -102,7 +103,6 @@ struct VirtualMemoryArea { | |||
| 102 | * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ | 103 | * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ |
| 103 | */ | 104 | */ |
| 104 | class VMManager final { | 105 | class VMManager final { |
| 105 | // TODO(yuriks): Make page tables switchable to support multiple VMManagers | ||
| 106 | public: | 106 | public: |
| 107 | /** | 107 | /** |
| 108 | * The maximum amount of address space managed by the kernel. Addresses above this are never | 108 | * The maximum amount of address space managed by the kernel. Addresses above this are never |
| @@ -184,6 +184,10 @@ public: | |||
| 184 | /// Dumps the address space layout to the log, for debugging | 184 | /// Dumps the address space layout to the log, for debugging |
| 185 | void LogLayout(Log::Level log_level) const; | 185 | void LogLayout(Log::Level log_level) const; |
| 186 | 186 | ||
| 187 | /// Each VMManager has its own page table, which is set as the main one when the owning process | ||
| 188 | /// is scheduled. | ||
| 189 | Memory::PageTable page_table; | ||
| 190 | |||
| 187 | private: | 191 | private: |
| 188 | using VMAIter = decltype(vma_map)::iterator; | 192 | using VMAIter = decltype(vma_map)::iterator; |
| 189 | 193 | ||
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 58d94768c..8c0ba73f2 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "core/hle/service/apt/apt_s.h" | 19 | #include "core/hle/service/apt/apt_s.h" |
| 20 | #include "core/hle/service/apt/apt_u.h" | 20 | #include "core/hle/service/apt/apt_u.h" |
| 21 | #include "core/hle/service/apt/bcfnt/bcfnt.h" | 21 | #include "core/hle/service/apt/bcfnt/bcfnt.h" |
| 22 | #include "core/hle/service/cfg/cfg.h" | ||
| 22 | #include "core/hle/service/fs/archive.h" | 23 | #include "core/hle/service/fs/archive.h" |
| 23 | #include "core/hle/service/ptm/ptm.h" | 24 | #include "core/hle/service/ptm/ptm.h" |
| 24 | #include "core/hle/service/service.h" | 25 | #include "core/hle/service/service.h" |
| @@ -198,6 +199,143 @@ void Initialize(Service::Interface* self) { | |||
| 198 | Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); | 199 | Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); |
| 199 | } | 200 | } |
| 200 | 201 | ||
| 202 | static u32 DecompressLZ11(const u8* in, u8* out) { | ||
| 203 | u32_le decompressed_size; | ||
| 204 | memcpy(&decompressed_size, in, sizeof(u32)); | ||
| 205 | in += 4; | ||
| 206 | |||
| 207 | u8 type = decompressed_size & 0xFF; | ||
| 208 | ASSERT(type == 0x11); | ||
| 209 | decompressed_size >>= 8; | ||
| 210 | |||
| 211 | u32 current_out_size = 0; | ||
| 212 | u8 flags = 0, mask = 1; | ||
| 213 | while (current_out_size < decompressed_size) { | ||
| 214 | if (mask == 1) { | ||
| 215 | flags = *(in++); | ||
| 216 | mask = 0x80; | ||
| 217 | } else { | ||
| 218 | mask >>= 1; | ||
| 219 | } | ||
| 220 | |||
| 221 | if (flags & mask) { | ||
| 222 | u8 byte1 = *(in++); | ||
| 223 | u32 length = byte1 >> 4; | ||
| 224 | u32 offset; | ||
| 225 | if (length == 0) { | ||
| 226 | u8 byte2 = *(in++); | ||
| 227 | u8 byte3 = *(in++); | ||
| 228 | length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11; | ||
| 229 | offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1; | ||
| 230 | } else if (length == 1) { | ||
| 231 | u8 byte2 = *(in++); | ||
| 232 | u8 byte3 = *(in++); | ||
| 233 | u8 byte4 = *(in++); | ||
| 234 | length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111; | ||
| 235 | offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1; | ||
| 236 | } else { | ||
| 237 | u8 byte2 = *(in++); | ||
| 238 | length = (byte1 >> 4) + 0x1; | ||
| 239 | offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1; | ||
| 240 | } | ||
| 241 | |||
| 242 | for (u32 i = 0; i < length; i++) { | ||
| 243 | *out = *(out - offset); | ||
| 244 | ++out; | ||
| 245 | } | ||
| 246 | |||
| 247 | current_out_size += length; | ||
| 248 | } else { | ||
| 249 | *(out++) = *(in++); | ||
| 250 | current_out_size++; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | return decompressed_size; | ||
| 254 | } | ||
| 255 | |||
| 256 | static bool LoadSharedFont() { | ||
| 257 | u8 font_region_code; | ||
| 258 | switch (CFG::GetRegionValue()) { | ||
| 259 | case 4: // CHN | ||
| 260 | font_region_code = 2; | ||
| 261 | break; | ||
| 262 | case 5: // KOR | ||
| 263 | font_region_code = 3; | ||
| 264 | break; | ||
| 265 | case 6: // TWN | ||
| 266 | font_region_code = 4; | ||
| 267 | break; | ||
| 268 | default: // JPN/EUR/USA | ||
| 269 | font_region_code = 1; | ||
| 270 | break; | ||
| 271 | } | ||
| 272 | |||
| 273 | const u64_le shared_font_archive_id_low = 0x0004009b00014002 | ((font_region_code - 1) << 8); | ||
| 274 | const u64_le shared_font_archive_id_high = 0x00000001ffffff00; | ||
| 275 | std::vector<u8> shared_font_archive_id(16); | ||
| 276 | std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64)); | ||
| 277 | std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64)); | ||
| 278 | FileSys::Path archive_path(shared_font_archive_id); | ||
| 279 | auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path); | ||
| 280 | if (archive_result.Failed()) | ||
| 281 | return false; | ||
| 282 | |||
| 283 | std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS | ||
| 284 | FileSys::Path file_path(romfs_path); | ||
| 285 | FileSys::Mode open_mode = {}; | ||
| 286 | open_mode.read_flag.Assign(1); | ||
| 287 | auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode); | ||
| 288 | if (file_result.Failed()) | ||
| 289 | return false; | ||
| 290 | |||
| 291 | auto romfs = std::move(file_result).Unwrap(); | ||
| 292 | std::vector<u8> romfs_buffer(romfs->backend->GetSize()); | ||
| 293 | romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data()); | ||
| 294 | romfs->backend->Close(); | ||
| 295 | |||
| 296 | const char16_t* file_name[4] = {u"cbf_std.bcfnt.lz", u"cbf_zh-Hans-CN.bcfnt.lz", | ||
| 297 | u"cbf_ko-Hang-KR.bcfnt.lz", u"cbf_zh-Hant-TW.bcfnt.lz"}; | ||
| 298 | const u8* font_file = | ||
| 299 | RomFS::GetFilePointer(romfs_buffer.data(), {file_name[font_region_code - 1]}); | ||
| 300 | if (font_file == nullptr) | ||
| 301 | return false; | ||
| 302 | |||
| 303 | struct { | ||
| 304 | u32_le status; | ||
| 305 | u32_le region; | ||
| 306 | u32_le decompressed_size; | ||
| 307 | INSERT_PADDING_WORDS(0x1D); | ||
| 308 | } shared_font_header{}; | ||
| 309 | static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size"); | ||
| 310 | |||
| 311 | shared_font_header.status = 2; // successfully loaded | ||
| 312 | shared_font_header.region = font_region_code; | ||
| 313 | shared_font_header.decompressed_size = | ||
| 314 | DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80)); | ||
| 315 | std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header)); | ||
| 316 | *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU" | ||
| 317 | |||
| 318 | return true; | ||
| 319 | } | ||
| 320 | |||
| 321 | static bool LoadLegacySharedFont() { | ||
| 322 | // This is the legacy method to load shared font. | ||
| 323 | // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header | ||
| 324 | // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided | ||
| 325 | // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file | ||
| 326 | // "shared_font.bin" in the Citra "sysdata" directory. | ||
| 327 | std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; | ||
| 328 | |||
| 329 | FileUtil::CreateFullPath(filepath); // Create path if not already created | ||
| 330 | FileUtil::IOFile file(filepath, "rb"); | ||
| 331 | if (file.IsOpen()) { | ||
| 332 | file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); | ||
| 333 | return true; | ||
| 334 | } | ||
| 335 | |||
| 336 | return false; | ||
| 337 | } | ||
| 338 | |||
| 201 | void GetSharedFont(Service::Interface* self) { | 339 | void GetSharedFont(Service::Interface* self) { |
| 202 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 | 340 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 |
| 203 | IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | 341 | IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); |
| @@ -206,11 +344,20 @@ void GetSharedFont(Service::Interface* self) { | |||
| 206 | Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true); | 344 | Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true); |
| 207 | 345 | ||
| 208 | if (!shared_font_loaded) { | 346 | if (!shared_font_loaded) { |
| 209 | LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); | 347 | // On real 3DS, font loading happens on booting. However, we load it on demand to coordinate |
| 210 | rb.Push<u32>(-1); // TODO: Find the right error code | 348 | // with CFG region auto configuration, which happens later than APT initialization. |
| 211 | rb.Skip(1 + 2, true); | 349 | if (LoadSharedFont()) { |
| 212 | Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); | 350 | shared_font_loaded = true; |
| 213 | return; | 351 | } else if (LoadLegacySharedFont()) { |
| 352 | LOG_WARNING(Service_APT, "Loaded shared font by legacy method"); | ||
| 353 | shared_font_loaded = true; | ||
| 354 | } else { | ||
| 355 | LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); | ||
| 356 | rb.Push<u32>(-1); // TODO: Find the right error code | ||
| 357 | rb.Skip(1 + 2, true); | ||
| 358 | Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); | ||
| 359 | return; | ||
| 360 | } | ||
| 214 | } | 361 | } |
| 215 | 362 | ||
| 216 | // The shared font has to be relocated to the new address before being passed to the | 363 | // The shared font has to be relocated to the new address before being passed to the |
| @@ -863,125 +1010,6 @@ void CheckNew3DS(Service::Interface* self) { | |||
| 863 | LOG_WARNING(Service_APT, "(STUBBED) called"); | 1010 | LOG_WARNING(Service_APT, "(STUBBED) called"); |
| 864 | } | 1011 | } |
| 865 | 1012 | ||
| 866 | static u32 DecompressLZ11(const u8* in, u8* out) { | ||
| 867 | u32_le decompressed_size; | ||
| 868 | memcpy(&decompressed_size, in, sizeof(u32)); | ||
| 869 | in += 4; | ||
| 870 | |||
| 871 | u8 type = decompressed_size & 0xFF; | ||
| 872 | ASSERT(type == 0x11); | ||
| 873 | decompressed_size >>= 8; | ||
| 874 | |||
| 875 | u32 current_out_size = 0; | ||
| 876 | u8 flags = 0, mask = 1; | ||
| 877 | while (current_out_size < decompressed_size) { | ||
| 878 | if (mask == 1) { | ||
| 879 | flags = *(in++); | ||
| 880 | mask = 0x80; | ||
| 881 | } else { | ||
| 882 | mask >>= 1; | ||
| 883 | } | ||
| 884 | |||
| 885 | if (flags & mask) { | ||
| 886 | u8 byte1 = *(in++); | ||
| 887 | u32 length = byte1 >> 4; | ||
| 888 | u32 offset; | ||
| 889 | if (length == 0) { | ||
| 890 | u8 byte2 = *(in++); | ||
| 891 | u8 byte3 = *(in++); | ||
| 892 | length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11; | ||
| 893 | offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1; | ||
| 894 | } else if (length == 1) { | ||
| 895 | u8 byte2 = *(in++); | ||
| 896 | u8 byte3 = *(in++); | ||
| 897 | u8 byte4 = *(in++); | ||
| 898 | length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111; | ||
| 899 | offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1; | ||
| 900 | } else { | ||
| 901 | u8 byte2 = *(in++); | ||
| 902 | length = (byte1 >> 4) + 0x1; | ||
| 903 | offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1; | ||
| 904 | } | ||
| 905 | |||
| 906 | for (u32 i = 0; i < length; i++) { | ||
| 907 | *out = *(out - offset); | ||
| 908 | ++out; | ||
| 909 | } | ||
| 910 | |||
| 911 | current_out_size += length; | ||
| 912 | } else { | ||
| 913 | *(out++) = *(in++); | ||
| 914 | current_out_size++; | ||
| 915 | } | ||
| 916 | } | ||
| 917 | return decompressed_size; | ||
| 918 | } | ||
| 919 | |||
| 920 | static bool LoadSharedFont() { | ||
| 921 | // TODO (wwylele): load different font archive for region CHN/KOR/TWN | ||
| 922 | const u64_le shared_font_archive_id_low = 0x0004009b00014002; | ||
| 923 | const u64_le shared_font_archive_id_high = 0x00000001ffffff00; | ||
| 924 | std::vector<u8> shared_font_archive_id(16); | ||
| 925 | std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64)); | ||
| 926 | std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64)); | ||
| 927 | FileSys::Path archive_path(shared_font_archive_id); | ||
| 928 | auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path); | ||
| 929 | if (archive_result.Failed()) | ||
| 930 | return false; | ||
| 931 | |||
| 932 | std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS | ||
| 933 | FileSys::Path file_path(romfs_path); | ||
| 934 | FileSys::Mode open_mode = {}; | ||
| 935 | open_mode.read_flag.Assign(1); | ||
| 936 | auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode); | ||
| 937 | if (file_result.Failed()) | ||
| 938 | return false; | ||
| 939 | |||
| 940 | auto romfs = std::move(file_result).Unwrap(); | ||
| 941 | std::vector<u8> romfs_buffer(romfs->backend->GetSize()); | ||
| 942 | romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data()); | ||
| 943 | romfs->backend->Close(); | ||
| 944 | |||
| 945 | const u8* font_file = RomFS::GetFilePointer(romfs_buffer.data(), {u"cbf_std.bcfnt.lz"}); | ||
| 946 | if (font_file == nullptr) | ||
| 947 | return false; | ||
| 948 | |||
| 949 | struct { | ||
| 950 | u32_le status; | ||
| 951 | u32_le region; | ||
| 952 | u32_le decompressed_size; | ||
| 953 | INSERT_PADDING_WORDS(0x1D); | ||
| 954 | } shared_font_header{}; | ||
| 955 | static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size"); | ||
| 956 | |||
| 957 | shared_font_header.status = 2; // successfully loaded | ||
| 958 | shared_font_header.region = 1; // region JPN/EUR/USA | ||
| 959 | shared_font_header.decompressed_size = | ||
| 960 | DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80)); | ||
| 961 | std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header)); | ||
| 962 | *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU" | ||
| 963 | |||
| 964 | return true; | ||
| 965 | } | ||
| 966 | |||
| 967 | static bool LoadLegacySharedFont() { | ||
| 968 | // This is the legacy method to load shared font. | ||
| 969 | // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header | ||
| 970 | // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided | ||
| 971 | // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file | ||
| 972 | // "shared_font.bin" in the Citra "sysdata" directory. | ||
| 973 | std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; | ||
| 974 | |||
| 975 | FileUtil::CreateFullPath(filepath); // Create path if not already created | ||
| 976 | FileUtil::IOFile file(filepath, "rb"); | ||
| 977 | if (file.IsOpen()) { | ||
| 978 | file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); | ||
| 979 | return true; | ||
| 980 | } | ||
| 981 | |||
| 982 | return false; | ||
| 983 | } | ||
| 984 | |||
| 985 | void Init() { | 1013 | void Init() { |
| 986 | AddService(new APT_A_Interface); | 1014 | AddService(new APT_A_Interface); |
| 987 | AddService(new APT_S_Interface); | 1015 | AddService(new APT_S_Interface); |
| @@ -995,16 +1023,6 @@ void Init() { | |||
| 995 | MemoryPermission::ReadWrite, MemoryPermission::Read, 0, | 1023 | MemoryPermission::ReadWrite, MemoryPermission::Read, 0, |
| 996 | Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); | 1024 | Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); |
| 997 | 1025 | ||
| 998 | if (LoadSharedFont()) { | ||
| 999 | shared_font_loaded = true; | ||
| 1000 | } else if (LoadLegacySharedFont()) { | ||
| 1001 | LOG_WARNING(Service_APT, "Loaded shared font by legacy method"); | ||
| 1002 | shared_font_loaded = true; | ||
| 1003 | } else { | ||
| 1004 | LOG_WARNING(Service_APT, "Unable to load shared font"); | ||
| 1005 | shared_font_loaded = false; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | lock = Kernel::Mutex::Create(false, "APT_U:Lock"); | 1026 | lock = Kernel::Mutex::Create(false, "APT_U:Lock"); |
| 1009 | 1027 | ||
| 1010 | cpu_percent = 0; | 1028 | cpu_percent = 0; |
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 3dbeb27cc..f26a1f65f 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp | |||
| @@ -168,7 +168,7 @@ void GetCountryCodeID(Service::Interface* self) { | |||
| 168 | cmd_buff[2] = country_code_id; | 168 | cmd_buff[2] = country_code_id; |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | static u32 GetRegionValue() { | 171 | u32 GetRegionValue() { |
| 172 | if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) | 172 | if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) |
| 173 | return preferred_region_code; | 173 | return preferred_region_code; |
| 174 | 174 | ||
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index 1659ebf32..282b6936b 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h | |||
| @@ -101,6 +101,8 @@ void GetCountryCodeString(Service::Interface* self); | |||
| 101 | */ | 101 | */ |
| 102 | void GetCountryCodeID(Service::Interface* self); | 102 | void GetCountryCodeID(Service::Interface* self); |
| 103 | 103 | ||
| 104 | u32 GetRegionValue(); | ||
| 105 | |||
| 104 | /** | 106 | /** |
| 105 | * CFG::SecureInfoGetRegion service function | 107 | * CFG::SecureInfoGetRegion service function |
| 106 | * Inputs: | 108 | * Inputs: |
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp new file mode 100644 index 000000000..9e19c38bf --- /dev/null +++ b/src/core/hle/service/ns/ns.cpp | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/ns/ns.h" | ||
| 6 | #include "core/hle/service/ns/ns_s.h" | ||
| 7 | |||
| 8 | namespace Service { | ||
| 9 | namespace NS { | ||
| 10 | |||
| 11 | void InstallInterfaces(SM::ServiceManager& service_manager) { | ||
| 12 | std::make_shared<NS_S>()->InstallAsService(service_manager); | ||
| 13 | } | ||
| 14 | |||
| 15 | } // namespace NS | ||
| 16 | } // namespace Service | ||
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h new file mode 100644 index 000000000..c3d67d98c --- /dev/null +++ b/src/core/hle/service/ns/ns.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Service { | ||
| 10 | namespace NS { | ||
| 11 | |||
| 12 | /// Registers all NS services with the specified service manager. | ||
| 13 | void InstallInterfaces(SM::ServiceManager& service_manager); | ||
| 14 | |||
| 15 | } // namespace NS | ||
| 16 | } // namespace Service | ||
diff --git a/src/core/hle/service/ns/ns_s.cpp b/src/core/hle/service/ns/ns_s.cpp new file mode 100644 index 000000000..d952888dc --- /dev/null +++ b/src/core/hle/service/ns/ns_s.cpp | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/ns/ns_s.h" | ||
| 6 | |||
| 7 | namespace Service { | ||
| 8 | namespace NS { | ||
| 9 | |||
| 10 | NS_S::NS_S() : ServiceFramework("ns:s", 2) { | ||
| 11 | static const FunctionInfo functions[] = { | ||
| 12 | {0x000100C0, nullptr, "LaunchFIRM"}, | ||
| 13 | {0x000200C0, nullptr, "LaunchTitle"}, | ||
| 14 | {0x00030000, nullptr, "TerminateApplication"}, | ||
| 15 | {0x00040040, nullptr, "TerminateProcess"}, | ||
| 16 | {0x000500C0, nullptr, "LaunchApplicationFIRM"}, | ||
| 17 | {0x00060042, nullptr, "SetFIRMParams4A0"}, | ||
| 18 | {0x00070042, nullptr, "CardUpdateInitialize"}, | ||
| 19 | {0x00080000, nullptr, "CardUpdateShutdown"}, | ||
| 20 | {0x000D0140, nullptr, "SetTWLBannerHMAC"}, | ||
| 21 | {0x000E0000, nullptr, "ShutdownAsync"}, | ||
| 22 | {0x00100180, nullptr, "RebootSystem"}, | ||
| 23 | {0x00110100, nullptr, "TerminateTitle"}, | ||
| 24 | {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"}, | ||
| 25 | {0x00150140, nullptr, "LaunchApplication"}, | ||
| 26 | {0x00160000, nullptr, "RebootSystemClean"}, | ||
| 27 | }; | ||
| 28 | RegisterHandlers(functions); | ||
| 29 | } | ||
| 30 | |||
| 31 | NS_S::~NS_S() = default; | ||
| 32 | |||
| 33 | } // namespace NS | ||
| 34 | } // namespace Service | ||
diff --git a/src/core/hle/service/ns_s.h b/src/core/hle/service/ns/ns_s.h index 90288a521..660ae453f 100644 --- a/src/core/hle/service/ns_s.h +++ b/src/core/hle/service/ns/ns_s.h | |||
| @@ -4,18 +4,17 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/kernel/kernel.h" | ||
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | 9 | ||
| 9 | namespace Service { | 10 | namespace Service { |
| 10 | namespace NS { | 11 | namespace NS { |
| 11 | 12 | ||
| 12 | class NS_S final : public Interface { | 13 | /// Interface to "ns:s" service |
| 14 | class NS_S final : public ServiceFramework<NS_S> { | ||
| 13 | public: | 15 | public: |
| 14 | NS_S(); | 16 | NS_S(); |
| 15 | 17 | ~NS_S(); | |
| 16 | std::string GetPortName() const override { | ||
| 17 | return "ns:s"; | ||
| 18 | } | ||
| 19 | }; | 18 | }; |
| 20 | 19 | ||
| 21 | } // namespace NS | 20 | } // namespace NS |
diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp deleted file mode 100644 index 215c9aacc..000000000 --- a/src/core/hle/service/ns_s.cpp +++ /dev/null | |||
| @@ -1,33 +0,0 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/ns_s.h" | ||
| 6 | |||
| 7 | namespace Service { | ||
| 8 | namespace NS { | ||
| 9 | |||
| 10 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 11 | {0x000100C0, nullptr, "LaunchFIRM"}, | ||
| 12 | {0x000200C0, nullptr, "LaunchTitle"}, | ||
| 13 | {0x00030000, nullptr, "TerminateApplication"}, | ||
| 14 | {0x00040040, nullptr, "TerminateProcess"}, | ||
| 15 | {0x000500C0, nullptr, "LaunchApplicationFIRM"}, | ||
| 16 | {0x00060042, nullptr, "SetFIRMParams4A0"}, | ||
| 17 | {0x00070042, nullptr, "CardUpdateInitialize"}, | ||
| 18 | {0x00080000, nullptr, "CardUpdateShutdown"}, | ||
| 19 | {0x000D0140, nullptr, "SetTWLBannerHMAC"}, | ||
| 20 | {0x000E0000, nullptr, "ShutdownAsync"}, | ||
| 21 | {0x00100180, nullptr, "RebootSystem"}, | ||
| 22 | {0x00110100, nullptr, "TerminateTitle"}, | ||
| 23 | {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"}, | ||
| 24 | {0x00150140, nullptr, "LaunchApplication"}, | ||
| 25 | {0x00160000, nullptr, "RebootSystemClean"}, | ||
| 26 | }; | ||
| 27 | |||
| 28 | NS_S::NS_S() { | ||
| 29 | Register(FunctionTable); | ||
| 30 | } | ||
| 31 | |||
| 32 | } // namespace NS | ||
| 33 | } // namespace Service | ||
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index aad950e50..f267aad74 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -38,7 +38,7 @@ | |||
| 38 | #include "core/hle/service/news/news.h" | 38 | #include "core/hle/service/news/news.h" |
| 39 | #include "core/hle/service/nfc/nfc.h" | 39 | #include "core/hle/service/nfc/nfc.h" |
| 40 | #include "core/hle/service/nim/nim.h" | 40 | #include "core/hle/service/nim/nim.h" |
| 41 | #include "core/hle/service/ns_s.h" | 41 | #include "core/hle/service/ns/ns.h" |
| 42 | #include "core/hle/service/nwm/nwm.h" | 42 | #include "core/hle/service/nwm/nwm.h" |
| 43 | #include "core/hle/service/pm_app.h" | 43 | #include "core/hle/service/pm_app.h" |
| 44 | #include "core/hle/service/ptm/ptm.h" | 44 | #include "core/hle/service/ptm/ptm.h" |
| @@ -215,6 +215,8 @@ void Init() { | |||
| 215 | SM::g_service_manager = std::make_shared<SM::ServiceManager>(); | 215 | SM::g_service_manager = std::make_shared<SM::ServiceManager>(); |
| 216 | SM::ServiceManager::InstallInterfaces(SM::g_service_manager); | 216 | SM::ServiceManager::InstallInterfaces(SM::g_service_manager); |
| 217 | 217 | ||
| 218 | NS::InstallInterfaces(*SM::g_service_manager); | ||
| 219 | |||
| 218 | AddNamedPort(new ERR::ERR_F); | 220 | AddNamedPort(new ERR::ERR_F); |
| 219 | 221 | ||
| 220 | FS::ArchiveInit(); | 222 | FS::ArchiveInit(); |
| @@ -246,7 +248,6 @@ void Init() { | |||
| 246 | AddService(new HTTP::HTTP_C); | 248 | AddService(new HTTP::HTTP_C); |
| 247 | AddService(new LDR::LDR_RO); | 249 | AddService(new LDR::LDR_RO); |
| 248 | AddService(new MIC::MIC_U); | 250 | AddService(new MIC::MIC_U); |
| 249 | AddService(new NS::NS_S); | ||
| 250 | AddService(new PM::PM_APP); | 251 | AddService(new PM::PM_APP); |
| 251 | AddService(new SOC::SOC_U); | 252 | AddService(new SOC::SOC_U); |
| 252 | AddService(new SSL::SSL_C); | 253 | AddService(new SSL::SSL_C); |
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 74e336487..69cdc0867 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp | |||
| @@ -270,6 +270,7 @@ ResultStatus AppLoader_THREEDSX::Load() { | |||
| 270 | Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); | 270 | Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); |
| 271 | Kernel::g_current_process->svc_access_mask.set(); | 271 | Kernel::g_current_process->svc_access_mask.set(); |
| 272 | Kernel::g_current_process->address_mappings = default_address_mappings; | 272 | Kernel::g_current_process->address_mappings = default_address_mappings; |
| 273 | Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; | ||
| 273 | 274 | ||
| 274 | // Attach the default resource limit (APPLICATION) to the process | 275 | // Attach the default resource limit (APPLICATION) to the process |
| 275 | Kernel::g_current_process->resource_limit = | 276 | Kernel::g_current_process->resource_limit = |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index cfcde9167..2f27606a1 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -397,6 +397,7 @@ ResultStatus AppLoader_ELF::Load() { | |||
| 397 | Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); | 397 | Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); |
| 398 | Kernel::g_current_process->svc_access_mask.set(); | 398 | Kernel::g_current_process->svc_access_mask.set(); |
| 399 | Kernel::g_current_process->address_mappings = default_address_mappings; | 399 | Kernel::g_current_process->address_mappings = default_address_mappings; |
| 400 | Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; | ||
| 400 | 401 | ||
| 401 | // Attach the default resource limit (APPLICATION) to the process | 402 | // Attach the default resource limit (APPLICATION) to the process |
| 402 | Kernel::g_current_process->resource_limit = | 403 | Kernel::g_current_process->resource_limit = |
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 7aff7f29b..79ea50147 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp | |||
| @@ -172,6 +172,7 @@ ResultStatus AppLoader_NCCH::LoadExec() { | |||
| 172 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); | 172 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); |
| 173 | 173 | ||
| 174 | Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); | 174 | Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); |
| 175 | Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; | ||
| 175 | 176 | ||
| 176 | // Attach a resource limit to the process based on the resource limit category | 177 | // Attach a resource limit to the process based on the resource limit category |
| 177 | Kernel::g_current_process->resource_limit = | 178 | Kernel::g_current_process->resource_limit = |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 097bc5b47..68a6b1ac2 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -4,83 +4,31 @@ | |||
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include "audio_core/audio_core.h" | ||
| 7 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 10 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/hle/kernel/memory.h" | ||
| 11 | #include "core/hle/kernel/process.h" | 13 | #include "core/hle/kernel/process.h" |
| 12 | #include "core/hle/lock.h" | 14 | #include "core/hle/lock.h" |
| 13 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 14 | #include "core/memory_setup.h" | 16 | #include "core/memory_setup.h" |
| 15 | #include "core/mmio.h" | ||
| 16 | #include "video_core/renderer_base.h" | 17 | #include "video_core/renderer_base.h" |
| 17 | #include "video_core/video_core.h" | 18 | #include "video_core/video_core.h" |
| 18 | 19 | ||
| 19 | namespace Memory { | 20 | namespace Memory { |
| 20 | 21 | ||
| 21 | enum class PageType { | 22 | static std::array<u8, Memory::VRAM_SIZE> vram; |
| 22 | /// Page is unmapped and should cause an access error. | 23 | static std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram; |
| 23 | Unmapped, | ||
| 24 | /// Page is mapped to regular memory. This is the only type you can get pointers to. | ||
| 25 | Memory, | ||
| 26 | /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and | ||
| 27 | /// invalidation | ||
| 28 | RasterizerCachedMemory, | ||
| 29 | /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. | ||
| 30 | Special, | ||
| 31 | /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and | ||
| 32 | /// invalidation | ||
| 33 | RasterizerCachedSpecial, | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct SpecialRegion { | ||
| 37 | VAddr base; | ||
| 38 | u32 size; | ||
| 39 | MMIORegionPointer handler; | ||
| 40 | }; | ||
| 41 | 24 | ||
| 42 | /** | 25 | PageTable* current_page_table = nullptr; |
| 43 | * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely | ||
| 44 | * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and | ||
| 45 | * fetching requirements when accessing. In the usual case of an access to regular memory, it only | ||
| 46 | * requires an indexed fetch and a check for NULL. | ||
| 47 | */ | ||
| 48 | struct PageTable { | ||
| 49 | /** | ||
| 50 | * Array of memory pointers backing each page. An entry can only be non-null if the | ||
| 51 | * corresponding entry in the `attributes` array is of type `Memory`. | ||
| 52 | */ | ||
| 53 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers; | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of | ||
| 57 | * type `Special`. | ||
| 58 | */ | ||
| 59 | std::vector<SpecialRegion> special_regions; | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Array of fine grained page attributes. If it is set to any value other than `Memory`, then | ||
| 63 | * the corresponding entry in `pointers` MUST be set to null. | ||
| 64 | */ | ||
| 65 | std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Indicates the number of externally cached resources touching a page that should be | ||
| 69 | * flushed before the memory is accessed | ||
| 70 | */ | ||
| 71 | std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count; | ||
| 72 | }; | ||
| 73 | |||
| 74 | /// Singular page table used for the singleton process | ||
| 75 | static PageTable main_page_table; | ||
| 76 | /// Currently active page table | ||
| 77 | static PageTable* current_page_table = &main_page_table; | ||
| 78 | 26 | ||
| 79 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { | 27 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { |
| 80 | return ¤t_page_table->pointers; | 28 | return ¤t_page_table->pointers; |
| 81 | } | 29 | } |
| 82 | 30 | ||
| 83 | static void MapPages(u32 base, u32 size, u8* memory, PageType type) { | 31 | static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) { |
| 84 | LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, | 32 | LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, |
| 85 | (base + size) * PAGE_SIZE); | 33 | (base + size) * PAGE_SIZE); |
| 86 | 34 | ||
| @@ -91,9 +39,9 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { | |||
| 91 | while (base != end) { | 39 | while (base != end) { |
| 92 | ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); | 40 | ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); |
| 93 | 41 | ||
| 94 | current_page_table->attributes[base] = type; | 42 | page_table.attributes[base] = type; |
| 95 | current_page_table->pointers[base] = memory; | 43 | page_table.pointers[base] = memory; |
| 96 | current_page_table->cached_res_count[base] = 0; | 44 | page_table.cached_res_count[base] = 0; |
| 97 | 45 | ||
| 98 | base += 1; | 46 | base += 1; |
| 99 | if (memory != nullptr) | 47 | if (memory != nullptr) |
| @@ -101,30 +49,24 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { | |||
| 101 | } | 49 | } |
| 102 | } | 50 | } |
| 103 | 51 | ||
| 104 | void InitMemoryMap() { | 52 | void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) { |
| 105 | main_page_table.pointers.fill(nullptr); | ||
| 106 | main_page_table.attributes.fill(PageType::Unmapped); | ||
| 107 | main_page_table.cached_res_count.fill(0); | ||
| 108 | } | ||
| 109 | |||
| 110 | void MapMemoryRegion(VAddr base, u32 size, u8* target) { | ||
| 111 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); | 53 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); |
| 112 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); | 54 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); |
| 113 | MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); | 55 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); |
| 114 | } | 56 | } |
| 115 | 57 | ||
| 116 | void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) { | 58 | void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler) { |
| 117 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); | 59 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); |
| 118 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); | 60 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); |
| 119 | MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); | 61 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); |
| 120 | 62 | ||
| 121 | current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); | 63 | page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); |
| 122 | } | 64 | } |
| 123 | 65 | ||
| 124 | void UnmapRegion(VAddr base, u32 size) { | 66 | void UnmapRegion(PageTable& page_table, VAddr base, u32 size) { |
| 125 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); | 67 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); |
| 126 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); | 68 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); |
| 127 | MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); | 69 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); |
| 128 | } | 70 | } |
| 129 | 71 | ||
| 130 | /** | 72 | /** |
| @@ -273,8 +215,7 @@ bool IsValidVirtualAddress(const VAddr vaddr) { | |||
| 273 | } | 215 | } |
| 274 | 216 | ||
| 275 | bool IsValidPhysicalAddress(const PAddr paddr) { | 217 | bool IsValidPhysicalAddress(const PAddr paddr) { |
| 276 | boost::optional<VAddr> vaddr = PhysicalToVirtualAddress(paddr); | 218 | return GetPhysicalPointer(paddr) != nullptr; |
| 277 | return vaddr && IsValidVirtualAddress(*vaddr); | ||
| 278 | } | 219 | } |
| 279 | 220 | ||
| 280 | u8* GetPointer(const VAddr vaddr) { | 221 | u8* GetPointer(const VAddr vaddr) { |
| @@ -306,9 +247,63 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) { | |||
| 306 | } | 247 | } |
| 307 | 248 | ||
| 308 | u8* GetPhysicalPointer(PAddr address) { | 249 | u8* GetPhysicalPointer(PAddr address) { |
| 309 | // TODO(Subv): This call should not go through the application's memory mapping. | 250 | struct MemoryArea { |
| 310 | boost::optional<VAddr> vaddr = PhysicalToVirtualAddress(address); | 251 | PAddr paddr_base; |
| 311 | return vaddr ? GetPointer(*vaddr) : nullptr; | 252 | u32 size; |
| 253 | }; | ||
| 254 | |||
| 255 | static constexpr MemoryArea memory_areas[] = { | ||
| 256 | {VRAM_PADDR, VRAM_SIZE}, | ||
| 257 | {IO_AREA_PADDR, IO_AREA_SIZE}, | ||
| 258 | {DSP_RAM_PADDR, DSP_RAM_SIZE}, | ||
| 259 | {FCRAM_PADDR, FCRAM_N3DS_SIZE}, | ||
| 260 | {N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE}, | ||
| 261 | }; | ||
| 262 | |||
| 263 | const auto area = | ||
| 264 | std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) { | ||
| 265 | return address >= area.paddr_base && address < area.paddr_base + area.size; | ||
| 266 | }); | ||
| 267 | |||
| 268 | if (area == std::end(memory_areas)) { | ||
| 269 | LOG_ERROR(HW_Memory, "unknown GetPhysicalPointer @ 0x%08X", address); | ||
| 270 | return nullptr; | ||
| 271 | } | ||
| 272 | |||
| 273 | if (area->paddr_base == IO_AREA_PADDR) { | ||
| 274 | LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr=0x%08X", address); | ||
| 275 | return nullptr; | ||
| 276 | } | ||
| 277 | |||
| 278 | u32 offset_into_region = address - area->paddr_base; | ||
| 279 | |||
| 280 | u8* target_pointer = nullptr; | ||
| 281 | switch (area->paddr_base) { | ||
| 282 | case VRAM_PADDR: | ||
| 283 | target_pointer = vram.data() + offset_into_region; | ||
| 284 | break; | ||
| 285 | case DSP_RAM_PADDR: | ||
| 286 | target_pointer = AudioCore::GetDspMemory().data() + offset_into_region; | ||
| 287 | break; | ||
| 288 | case FCRAM_PADDR: | ||
| 289 | for (const auto& region : Kernel::memory_regions) { | ||
| 290 | if (offset_into_region >= region.base && | ||
| 291 | offset_into_region < region.base + region.size) { | ||
| 292 | target_pointer = | ||
| 293 | region.linear_heap_memory->data() + offset_into_region - region.base; | ||
| 294 | break; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address"); | ||
| 298 | break; | ||
| 299 | case N3DS_EXTRA_RAM_PADDR: | ||
| 300 | target_pointer = n3ds_extra_ram.data() + offset_into_region; | ||
| 301 | break; | ||
| 302 | default: | ||
| 303 | UNREACHABLE(); | ||
| 304 | } | ||
| 305 | |||
| 306 | return target_pointer; | ||
| 312 | } | 307 | } |
| 313 | 308 | ||
| 314 | void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { | 309 | void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { |
diff --git a/src/core/memory.h b/src/core/memory.h index c8c56babd..b228a48c2 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -7,8 +7,10 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include <vector> | ||
| 10 | #include <boost/optional.hpp> | 11 | #include <boost/optional.hpp> |
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "core/mmio.h" | ||
| 12 | 14 | ||
| 13 | namespace Memory { | 15 | namespace Memory { |
| 14 | 16 | ||
| @@ -21,6 +23,59 @@ const u32 PAGE_MASK = PAGE_SIZE - 1; | |||
| 21 | const int PAGE_BITS = 12; | 23 | const int PAGE_BITS = 12; |
| 22 | const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); | 24 | const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); |
| 23 | 25 | ||
| 26 | enum class PageType { | ||
| 27 | /// Page is unmapped and should cause an access error. | ||
| 28 | Unmapped, | ||
| 29 | /// Page is mapped to regular memory. This is the only type you can get pointers to. | ||
| 30 | Memory, | ||
| 31 | /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and | ||
| 32 | /// invalidation | ||
| 33 | RasterizerCachedMemory, | ||
| 34 | /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. | ||
| 35 | Special, | ||
| 36 | /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and | ||
| 37 | /// invalidation | ||
| 38 | RasterizerCachedSpecial, | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct SpecialRegion { | ||
| 42 | VAddr base; | ||
| 43 | u32 size; | ||
| 44 | MMIORegionPointer handler; | ||
| 45 | }; | ||
| 46 | |||
| 47 | /** | ||
| 48 | * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely | ||
| 49 | * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and | ||
| 50 | * fetching requirements when accessing. In the usual case of an access to regular memory, it only | ||
| 51 | * requires an indexed fetch and a check for NULL. | ||
| 52 | */ | ||
| 53 | struct PageTable { | ||
| 54 | /** | ||
| 55 | * Array of memory pointers backing each page. An entry can only be non-null if the | ||
| 56 | * corresponding entry in the `attributes` array is of type `Memory`. | ||
| 57 | */ | ||
| 58 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers; | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of | ||
| 62 | * type `Special`. | ||
| 63 | */ | ||
| 64 | std::vector<SpecialRegion> special_regions; | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Array of fine grained page attributes. If it is set to any value other than `Memory`, then | ||
| 68 | * the corresponding entry in `pointers` MUST be set to null. | ||
| 69 | */ | ||
| 70 | std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Indicates the number of externally cached resources touching a page that should be | ||
| 74 | * flushed before the memory is accessed | ||
| 75 | */ | ||
| 76 | std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count; | ||
| 77 | }; | ||
| 78 | |||
| 24 | /// Physical memory regions as seen from the ARM11 | 79 | /// Physical memory regions as seen from the ARM11 |
| 25 | enum : PAddr { | 80 | enum : PAddr { |
| 26 | /// IO register area | 81 | /// IO register area |
| @@ -126,6 +181,9 @@ enum : VAddr { | |||
| 126 | NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, | 181 | NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, |
| 127 | }; | 182 | }; |
| 128 | 183 | ||
| 184 | /// Currently active page table | ||
| 185 | extern PageTable* current_page_table; | ||
| 186 | |||
| 129 | bool IsValidVirtualAddress(const VAddr addr); | 187 | bool IsValidVirtualAddress(const VAddr addr); |
| 130 | bool IsValidPhysicalAddress(const PAddr addr); | 188 | bool IsValidPhysicalAddress(const PAddr addr); |
| 131 | 189 | ||
| @@ -169,8 +227,6 @@ boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr); | |||
| 169 | 227 | ||
| 170 | /** | 228 | /** |
| 171 | * Gets a pointer to the memory region beginning at the specified physical address. | 229 | * Gets a pointer to the memory region beginning at the specified physical address. |
| 172 | * | ||
| 173 | * @note This is currently implemented using PhysicalToVirtualAddress(). | ||
| 174 | */ | 230 | */ |
| 175 | u8* GetPhysicalPointer(PAddr address); | 231 | u8* GetPhysicalPointer(PAddr address); |
| 176 | 232 | ||
| @@ -209,4 +265,4 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode); | |||
| 209 | * retrieve the current page table for that purpose. | 265 | * retrieve the current page table for that purpose. |
| 210 | */ | 266 | */ |
| 211 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers(); | 267 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers(); |
| 212 | } | 268 | } // namespace Memory |
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h index 3fdf3a87d..c58baa50b 100644 --- a/src/core/memory_setup.h +++ b/src/core/memory_setup.h | |||
| @@ -9,24 +9,24 @@ | |||
| 9 | 9 | ||
| 10 | namespace Memory { | 10 | namespace Memory { |
| 11 | 11 | ||
| 12 | void InitMemoryMap(); | ||
| 13 | |||
| 14 | /** | 12 | /** |
| 15 | * Maps an allocated buffer onto a region of the emulated process address space. | 13 | * Maps an allocated buffer onto a region of the emulated process address space. |
| 16 | * | 14 | * |
| 15 | * @param page_table The page table of the emulated process. | ||
| 17 | * @param base The address to start mapping at. Must be page-aligned. | 16 | * @param base The address to start mapping at. Must be page-aligned. |
| 18 | * @param size The amount of bytes to map. Must be page-aligned. | 17 | * @param size The amount of bytes to map. Must be page-aligned. |
| 19 | * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. | 18 | * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. |
| 20 | */ | 19 | */ |
| 21 | void MapMemoryRegion(VAddr base, u32 size, u8* target); | 20 | void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target); |
| 22 | 21 | ||
| 23 | /** | 22 | /** |
| 24 | * Maps a region of the emulated process address space as a IO region. | 23 | * Maps a region of the emulated process address space as a IO region. |
| 24 | * @param page_table The page table of the emulated process. | ||
| 25 | * @param base The address to start mapping at. Must be page-aligned. | 25 | * @param base The address to start mapping at. Must be page-aligned. |
| 26 | * @param size The amount of bytes to map. Must be page-aligned. | 26 | * @param size The amount of bytes to map. Must be page-aligned. |
| 27 | * @param mmio_handler The handler that backs the mapping. | 27 | * @param mmio_handler The handler that backs the mapping. |
| 28 | */ | 28 | */ |
| 29 | void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler); | 29 | void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler); |
| 30 | 30 | ||
| 31 | void UnmapRegion(VAddr base, u32 size); | 31 | void UnmapRegion(PageTable& page_table, VAddr base, u32 size); |
| 32 | } | 32 | } |
diff --git a/src/core/settings.h b/src/core/settings.h index 024f14666..8d78cb424 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -133,6 +133,7 @@ struct Values { | |||
| 133 | // WebService | 133 | // WebService |
| 134 | bool enable_telemetry; | 134 | bool enable_telemetry; |
| 135 | std::string telemetry_endpoint_url; | 135 | std::string telemetry_endpoint_url; |
| 136 | std::string verify_endpoint_url; | ||
| 136 | std::string citra_username; | 137 | std::string citra_username; |
| 137 | std::string citra_token; | 138 | std::string citra_token; |
| 138 | } extern values; | 139 | } extern values; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 104a16cc9..ca517ff44 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | #ifdef ENABLE_WEB_SERVICE | 16 | #ifdef ENABLE_WEB_SERVICE |
| 17 | #include "web_service/telemetry_json.h" | 17 | #include "web_service/telemetry_json.h" |
| 18 | #include "web_service/verify_login.h" | ||
| 18 | #endif | 19 | #endif |
| 19 | 20 | ||
| 20 | namespace Core { | 21 | namespace Core { |
| @@ -75,6 +76,17 @@ u64 RegenerateTelemetryId() { | |||
| 75 | return new_telemetry_id; | 76 | return new_telemetry_id; |
| 76 | } | 77 | } |
| 77 | 78 | ||
| 79 | std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func) { | ||
| 80 | #ifdef ENABLE_WEB_SERVICE | ||
| 81 | return WebService::VerifyLogin(username, token, Settings::values.verify_endpoint_url, func); | ||
| 82 | #else | ||
| 83 | return std::async(std::launch::async, [func{std::move(func)}]() { | ||
| 84 | func(); | ||
| 85 | return false; | ||
| 86 | }); | ||
| 87 | #endif | ||
| 88 | } | ||
| 89 | |||
| 78 | TelemetrySession::TelemetrySession() { | 90 | TelemetrySession::TelemetrySession() { |
| 79 | #ifdef ENABLE_WEB_SERVICE | 91 | #ifdef ENABLE_WEB_SERVICE |
| 80 | if (Settings::values.enable_telemetry) { | 92 | if (Settings::values.enable_telemetry) { |
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index 65613daae..550c6ea2d 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <future> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include "common/telemetry.h" | 9 | #include "common/telemetry.h" |
| 9 | 10 | ||
| @@ -47,4 +48,13 @@ u64 GetTelemetryId(); | |||
| 47 | */ | 48 | */ |
| 48 | u64 RegenerateTelemetryId(); | 49 | u64 RegenerateTelemetryId(); |
| 49 | 50 | ||
| 51 | /** | ||
| 52 | * Verifies the username and token. | ||
| 53 | * @param username Citra username to use for authentication. | ||
| 54 | * @param token Citra token to use for authentication. | ||
| 55 | * @param func A function that gets exectued when the verification is finished | ||
| 56 | * @returns Future with bool indicating whether the verification succeeded | ||
| 57 | */ | ||
| 58 | std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func); | ||
| 59 | |||
| 50 | } // namespace Core | 60 | } // namespace Core |
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 1df6c5677..8384ce744 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp | |||
| @@ -3,20 +3,30 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/core.h" | 5 | #include "core/core.h" |
| 6 | #include "core/memory.h" | ||
| 6 | #include "core/memory_setup.h" | 7 | #include "core/memory_setup.h" |
| 7 | #include "tests/core/arm/arm_test_common.h" | 8 | #include "tests/core/arm/arm_test_common.h" |
| 8 | 9 | ||
| 9 | namespace ArmTests { | 10 | namespace ArmTests { |
| 10 | 11 | ||
| 12 | static Memory::PageTable page_table; | ||
| 13 | |||
| 11 | TestEnvironment::TestEnvironment(bool mutable_memory_) | 14 | TestEnvironment::TestEnvironment(bool mutable_memory_) |
| 12 | : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { | 15 | : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { |
| 13 | Memory::MapIoRegion(0x00000000, 0x80000000, test_memory); | 16 | |
| 14 | Memory::MapIoRegion(0x80000000, 0x80000000, test_memory); | 17 | page_table.pointers.fill(nullptr); |
| 18 | page_table.attributes.fill(Memory::PageType::Unmapped); | ||
| 19 | page_table.cached_res_count.fill(0); | ||
| 20 | |||
| 21 | Memory::MapIoRegion(page_table, 0x00000000, 0x80000000, test_memory); | ||
| 22 | Memory::MapIoRegion(page_table, 0x80000000, 0x80000000, test_memory); | ||
| 23 | |||
| 24 | Memory::current_page_table = &page_table; | ||
| 15 | } | 25 | } |
| 16 | 26 | ||
| 17 | TestEnvironment::~TestEnvironment() { | 27 | TestEnvironment::~TestEnvironment() { |
| 18 | Memory::UnmapRegion(0x80000000, 0x80000000); | 28 | Memory::UnmapRegion(page_table, 0x80000000, 0x80000000); |
| 19 | Memory::UnmapRegion(0x00000000, 0x80000000); | 29 | Memory::UnmapRegion(page_table, 0x00000000, 0x80000000); |
| 20 | } | 30 | } |
| 21 | 31 | ||
| 22 | void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) { | 32 | void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) { |
diff --git a/src/video_core/regs_rasterizer.h b/src/video_core/regs_rasterizer.h index 2874fd127..4fef00d76 100644 --- a/src/video_core/regs_rasterizer.h +++ b/src/video_core/regs_rasterizer.h | |||
| @@ -5,10 +5,10 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | |||
| 9 | #include "common/bit_field.h" | 8 | #include "common/bit_field.h" |
| 10 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "video_core/pica_types.h" | ||
| 12 | 12 | ||
| 13 | namespace Pica { | 13 | namespace Pica { |
| 14 | 14 | ||
| @@ -31,7 +31,17 @@ struct RasterizerRegs { | |||
| 31 | 31 | ||
| 32 | BitField<0, 24, u32> viewport_size_y; | 32 | BitField<0, 24, u32> viewport_size_y; |
| 33 | 33 | ||
| 34 | INSERT_PADDING_WORDS(0x9); | 34 | INSERT_PADDING_WORDS(0x3); |
| 35 | |||
| 36 | BitField<0, 1, u32> clip_enable; | ||
| 37 | BitField<0, 24, u32> clip_coef[4]; // float24 | ||
| 38 | |||
| 39 | Math::Vec4<float24> GetClipCoef() const { | ||
| 40 | return {float24::FromRaw(clip_coef[0]), float24::FromRaw(clip_coef[1]), | ||
| 41 | float24::FromRaw(clip_coef[2]), float24::FromRaw(clip_coef[3])}; | ||
| 42 | } | ||
| 43 | |||
| 44 | INSERT_PADDING_WORDS(0x1); | ||
| 35 | 45 | ||
| 36 | BitField<0, 24, u32> viewport_depth_range; // float24 | 46 | BitField<0, 24, u32> viewport_depth_range; // float24 |
| 37 | BitField<0, 24, u32> viewport_depth_near_plane; // float24 | 47 | BitField<0, 24, u32> viewport_depth_near_plane; // float24 |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index aa95ef21d..7e09e4712 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -169,6 +169,8 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { | |||
| 169 | glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, proctex_diff_lut_buffer.handle); | 169 | glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, proctex_diff_lut_buffer.handle); |
| 170 | 170 | ||
| 171 | // Sync fixed function OpenGL state | 171 | // Sync fixed function OpenGL state |
| 172 | SyncClipEnabled(); | ||
| 173 | SyncClipCoef(); | ||
| 172 | SyncCullMode(); | 174 | SyncCullMode(); |
| 173 | SyncBlendEnabled(); | 175 | SyncBlendEnabled(); |
| 174 | SyncBlendFuncs(); | 176 | SyncBlendFuncs(); |
| @@ -235,13 +237,24 @@ void RasterizerOpenGL::DrawTriangles() { | |||
| 235 | 237 | ||
| 236 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | 238 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| 237 | color_surface != nullptr ? color_surface->texture.handle : 0, 0); | 239 | color_surface != nullptr ? color_surface->texture.handle : 0, 0); |
| 238 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, | 240 | if (depth_surface != nullptr) { |
| 239 | depth_surface != nullptr ? depth_surface->texture.handle : 0, 0); | 241 | if (regs.framebuffer.framebuffer.depth_format == |
| 240 | bool has_stencil = | 242 | Pica::FramebufferRegs::DepthFormat::D24S8) { |
| 241 | regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8; | 243 | // attach both depth and stencil |
| 242 | glFramebufferTexture2D( | 244 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, |
| 243 | GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | 245 | depth_surface->texture.handle, 0); |
| 244 | (has_stencil && depth_surface != nullptr) ? depth_surface->texture.handle : 0, 0); | 246 | } else { |
| 247 | // attach depth | ||
| 248 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, | ||
| 249 | depth_surface->texture.handle, 0); | ||
| 250 | // clear stencil attachment | ||
| 251 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||
| 252 | } | ||
| 253 | } else { | ||
| 254 | // clear both depth and stencil attachment | ||
| 255 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | ||
| 256 | 0); | ||
| 257 | } | ||
| 245 | 258 | ||
| 246 | // Sync the viewport | 259 | // Sync the viewport |
| 247 | // These registers hold half-width and half-height, so must be multiplied by 2 | 260 | // These registers hold half-width and half-height, so must be multiplied by 2 |
| @@ -401,6 +414,18 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | |||
| 401 | SyncCullMode(); | 414 | SyncCullMode(); |
| 402 | break; | 415 | break; |
| 403 | 416 | ||
| 417 | // Clipping plane | ||
| 418 | case PICA_REG_INDEX(rasterizer.clip_enable): | ||
| 419 | SyncClipEnabled(); | ||
| 420 | break; | ||
| 421 | |||
| 422 | case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[0], 0x48): | ||
| 423 | case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[1], 0x49): | ||
| 424 | case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[2], 0x4a): | ||
| 425 | case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[3], 0x4b): | ||
| 426 | SyncClipCoef(); | ||
| 427 | break; | ||
| 428 | |||
| 404 | // Depth modifiers | 429 | // Depth modifiers |
| 405 | case PICA_REG_INDEX(rasterizer.viewport_depth_range): | 430 | case PICA_REG_INDEX(rasterizer.viewport_depth_range): |
| 406 | SyncDepthScale(); | 431 | SyncDepthScale(); |
| @@ -1280,6 +1305,20 @@ void RasterizerOpenGL::SetShader() { | |||
| 1280 | } | 1305 | } |
| 1281 | } | 1306 | } |
| 1282 | 1307 | ||
| 1308 | void RasterizerOpenGL::SyncClipEnabled() { | ||
| 1309 | state.clip_distance[1] = Pica::g_state.regs.rasterizer.clip_enable != 0; | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | void RasterizerOpenGL::SyncClipCoef() { | ||
| 1313 | const auto raw_clip_coef = Pica::g_state.regs.rasterizer.GetClipCoef(); | ||
| 1314 | const GLvec4 new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(), | ||
| 1315 | raw_clip_coef.z.ToFloat32(), raw_clip_coef.w.ToFloat32()}; | ||
| 1316 | if (new_clip_coef != uniform_block_data.data.clip_coef) { | ||
| 1317 | uniform_block_data.data.clip_coef = new_clip_coef; | ||
| 1318 | uniform_block_data.dirty = true; | ||
| 1319 | } | ||
| 1320 | } | ||
| 1321 | |||
| 1283 | void RasterizerOpenGL::SyncCullMode() { | 1322 | void RasterizerOpenGL::SyncCullMode() { |
| 1284 | const auto& regs = Pica::g_state.regs; | 1323 | const auto& regs = Pica::g_state.regs; |
| 1285 | 1324 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 78e218efe..46c62961c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -151,14 +151,21 @@ private: | |||
| 151 | LightSrc light_src[8]; | 151 | LightSrc light_src[8]; |
| 152 | alignas(16) GLvec4 const_color[6]; // A vec4 color for each of the six tev stages | 152 | alignas(16) GLvec4 const_color[6]; // A vec4 color for each of the six tev stages |
| 153 | alignas(16) GLvec4 tev_combiner_buffer_color; | 153 | alignas(16) GLvec4 tev_combiner_buffer_color; |
| 154 | alignas(16) GLvec4 clip_coef; | ||
| 154 | }; | 155 | }; |
| 155 | 156 | ||
| 156 | static_assert( | 157 | static_assert( |
| 157 | sizeof(UniformData) == 0x460, | 158 | sizeof(UniformData) == 0x470, |
| 158 | "The size of the UniformData structure has changed, update the structure in the shader"); | 159 | "The size of the UniformData structure has changed, update the structure in the shader"); |
| 159 | static_assert(sizeof(UniformData) < 16384, | 160 | static_assert(sizeof(UniformData) < 16384, |
| 160 | "UniformData structure must be less than 16kb as per the OpenGL spec"); | 161 | "UniformData structure must be less than 16kb as per the OpenGL spec"); |
| 161 | 162 | ||
| 163 | /// Syncs the clip enabled status to match the PICA register | ||
| 164 | void SyncClipEnabled(); | ||
| 165 | |||
| 166 | /// Syncs the clip coefficients to match the PICA register | ||
| 167 | void SyncClipCoef(); | ||
| 168 | |||
| 162 | /// Sets the OpenGL shader in accordance with the current PICA register state | 169 | /// Sets the OpenGL shader in accordance with the current PICA register state |
| 163 | void SetShader(); | 170 | void SetShader(); |
| 164 | 171 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index c536e61e1..9fe183944 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -25,6 +25,42 @@ using TevStageConfig = TexturingRegs::TevStageConfig; | |||
| 25 | 25 | ||
| 26 | namespace GLShader { | 26 | namespace GLShader { |
| 27 | 27 | ||
| 28 | static const std::string UniformBlockDef = R"( | ||
| 29 | #define NUM_TEV_STAGES 6 | ||
| 30 | #define NUM_LIGHTS 8 | ||
| 31 | |||
| 32 | struct LightSrc { | ||
| 33 | vec3 specular_0; | ||
| 34 | vec3 specular_1; | ||
| 35 | vec3 diffuse; | ||
| 36 | vec3 ambient; | ||
| 37 | vec3 position; | ||
| 38 | vec3 spot_direction; | ||
| 39 | float dist_atten_bias; | ||
| 40 | float dist_atten_scale; | ||
| 41 | }; | ||
| 42 | |||
| 43 | layout (std140) uniform shader_data { | ||
| 44 | vec2 framebuffer_scale; | ||
| 45 | int alphatest_ref; | ||
| 46 | float depth_scale; | ||
| 47 | float depth_offset; | ||
| 48 | int scissor_x1; | ||
| 49 | int scissor_y1; | ||
| 50 | int scissor_x2; | ||
| 51 | int scissor_y2; | ||
| 52 | vec3 fog_color; | ||
| 53 | vec2 proctex_noise_f; | ||
| 54 | vec2 proctex_noise_a; | ||
| 55 | vec2 proctex_noise_p; | ||
| 56 | vec3 lighting_global_ambient; | ||
| 57 | LightSrc light_src[NUM_LIGHTS]; | ||
| 58 | vec4 const_color[NUM_TEV_STAGES]; | ||
| 59 | vec4 tev_combiner_buffer_color; | ||
| 60 | vec4 clip_coef; | ||
| 61 | }; | ||
| 62 | )"; | ||
| 63 | |||
| 28 | PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) { | 64 | PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) { |
| 29 | PicaShaderConfig res; | 65 | PicaShaderConfig res; |
| 30 | 66 | ||
| @@ -1010,8 +1046,6 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) { | |||
| 1010 | 1046 | ||
| 1011 | std::string out = R"( | 1047 | std::string out = R"( |
| 1012 | #version 330 core | 1048 | #version 330 core |
| 1013 | #define NUM_TEV_STAGES 6 | ||
| 1014 | #define NUM_LIGHTS 8 | ||
| 1015 | 1049 | ||
| 1016 | in vec4 primary_color; | 1050 | in vec4 primary_color; |
| 1017 | in vec2 texcoord[3]; | 1051 | in vec2 texcoord[3]; |
| @@ -1023,36 +1057,6 @@ in vec4 gl_FragCoord; | |||
| 1023 | 1057 | ||
| 1024 | out vec4 color; | 1058 | out vec4 color; |
| 1025 | 1059 | ||
| 1026 | struct LightSrc { | ||
| 1027 | vec3 specular_0; | ||
| 1028 | vec3 specular_1; | ||
| 1029 | vec3 diffuse; | ||
| 1030 | vec3 ambient; | ||
| 1031 | vec3 position; | ||
| 1032 | vec3 spot_direction; | ||
| 1033 | float dist_atten_bias; | ||
| 1034 | float dist_atten_scale; | ||
| 1035 | }; | ||
| 1036 | |||
| 1037 | layout (std140) uniform shader_data { | ||
| 1038 | vec2 framebuffer_scale; | ||
| 1039 | int alphatest_ref; | ||
| 1040 | float depth_scale; | ||
| 1041 | float depth_offset; | ||
| 1042 | int scissor_x1; | ||
| 1043 | int scissor_y1; | ||
| 1044 | int scissor_x2; | ||
| 1045 | int scissor_y2; | ||
| 1046 | vec3 fog_color; | ||
| 1047 | vec2 proctex_noise_f; | ||
| 1048 | vec2 proctex_noise_a; | ||
| 1049 | vec2 proctex_noise_p; | ||
| 1050 | vec3 lighting_global_ambient; | ||
| 1051 | LightSrc light_src[NUM_LIGHTS]; | ||
| 1052 | vec4 const_color[NUM_TEV_STAGES]; | ||
| 1053 | vec4 tev_combiner_buffer_color; | ||
| 1054 | }; | ||
| 1055 | |||
| 1056 | uniform sampler2D tex[3]; | 1060 | uniform sampler2D tex[3]; |
| 1057 | uniform samplerBuffer lighting_lut; | 1061 | uniform samplerBuffer lighting_lut; |
| 1058 | uniform samplerBuffer fog_lut; | 1062 | uniform samplerBuffer fog_lut; |
| @@ -1061,7 +1065,11 @@ uniform samplerBuffer proctex_color_map; | |||
| 1061 | uniform samplerBuffer proctex_alpha_map; | 1065 | uniform samplerBuffer proctex_alpha_map; |
| 1062 | uniform samplerBuffer proctex_lut; | 1066 | uniform samplerBuffer proctex_lut; |
| 1063 | uniform samplerBuffer proctex_diff_lut; | 1067 | uniform samplerBuffer proctex_diff_lut; |
| 1068 | )"; | ||
| 1069 | |||
| 1070 | out += UniformBlockDef; | ||
| 1064 | 1071 | ||
| 1072 | out += R"( | ||
| 1065 | // Rotate the vector v by the quaternion q | 1073 | // Rotate the vector v by the quaternion q |
| 1066 | vec3 quaternion_rotate(vec4 q, vec3 v) { | 1074 | vec3 quaternion_rotate(vec4 q, vec3 v) { |
| 1067 | return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); | 1075 | return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); |
| @@ -1197,6 +1205,12 @@ out float texcoord0_w; | |||
| 1197 | out vec4 normquat; | 1205 | out vec4 normquat; |
| 1198 | out vec3 view; | 1206 | out vec3 view; |
| 1199 | 1207 | ||
| 1208 | )"; | ||
| 1209 | |||
| 1210 | out += UniformBlockDef; | ||
| 1211 | |||
| 1212 | out += R"( | ||
| 1213 | |||
| 1200 | void main() { | 1214 | void main() { |
| 1201 | primary_color = vert_color; | 1215 | primary_color = vert_color; |
| 1202 | texcoord[0] = vert_texcoord0; | 1216 | texcoord[0] = vert_texcoord0; |
| @@ -1207,7 +1221,7 @@ void main() { | |||
| 1207 | view = vert_view; | 1221 | view = vert_view; |
| 1208 | gl_Position = vert_position; | 1222 | gl_Position = vert_position; |
| 1209 | gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 | 1223 | gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 |
| 1210 | // TODO (wwylele): calculate gl_ClipDistance[1] from user-defined clipping plane | 1224 | gl_ClipDistance[1] = dot(clip_coef, vert_position); |
| 1211 | } | 1225 | } |
| 1212 | )"; | 1226 | )"; |
| 1213 | 1227 | ||
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp index e9063e616..2857d2829 100644 --- a/src/video_core/shader/shader.cpp +++ b/src/video_core/shader/shader.cpp | |||
| @@ -52,7 +52,8 @@ OutputVertex OutputVertex::FromAttributeBuffer(const RasterizerRegs& regs, | |||
| 52 | // The hardware takes the absolute and saturates vertex colors like this, *before* doing | 52 | // The hardware takes the absolute and saturates vertex colors like this, *before* doing |
| 53 | // interpolation | 53 | // interpolation |
| 54 | for (unsigned i = 0; i < 4; ++i) { | 54 | for (unsigned i = 0; i < 4; ++i) { |
| 55 | ret.color[i] = float24::FromFloat32(std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f)); | 55 | float c = std::fabs(ret.color[i].ToFloat32()); |
| 56 | ret.color[i] = float24::FromFloat32(c < 1.0f ? c : 1.0f); | ||
| 56 | } | 57 | } |
| 57 | 58 | ||
| 58 | LOG_TRACE(HW_GPU, "Output vertex: pos(%.2f, %.2f, %.2f, %.2f), quat(%.2f, %.2f, %.2f, %.2f), " | 59 | LOG_TRACE(HW_GPU, "Output vertex: pos(%.2f, %.2f, %.2f, %.2f), quat(%.2f, %.2f, %.2f, %.2f), " |
diff --git a/src/video_core/swrasterizer/clipper.cpp b/src/video_core/swrasterizer/clipper.cpp index cdbc71502..a52129eb7 100644 --- a/src/video_core/swrasterizer/clipper.cpp +++ b/src/video_core/swrasterizer/clipper.cpp | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | : coeffs(coeffs), bias(bias) {} | 31 | : coeffs(coeffs), bias(bias) {} |
| 32 | 32 | ||
| 33 | bool IsInside(const Vertex& vertex) const { | 33 | bool IsInside(const Vertex& vertex) const { |
| 34 | return Math::Dot(vertex.pos + bias, coeffs) <= float24::FromFloat32(0); | 34 | return Math::Dot(vertex.pos + bias, coeffs) >= float24::FromFloat32(0); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | bool IsOutSide(const Vertex& vertex) const { | 37 | bool IsOutSide(const Vertex& vertex) const { |
| @@ -116,19 +116,18 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu | |||
| 116 | static const float24 f0 = float24::FromFloat32(0.0); | 116 | static const float24 f0 = float24::FromFloat32(0.0); |
| 117 | static const float24 f1 = float24::FromFloat32(1.0); | 117 | static const float24 f1 = float24::FromFloat32(1.0); |
| 118 | static const std::array<ClippingEdge, 7> clipping_edges = {{ | 118 | static const std::array<ClippingEdge, 7> clipping_edges = {{ |
| 119 | {Math::MakeVec(f1, f0, f0, -f1)}, // x = +w | 119 | {Math::MakeVec(-f1, f0, f0, f1)}, // x = +w |
| 120 | {Math::MakeVec(-f1, f0, f0, -f1)}, // x = -w | 120 | {Math::MakeVec(f1, f0, f0, f1)}, // x = -w |
| 121 | {Math::MakeVec(f0, f1, f0, -f1)}, // y = +w | 121 | {Math::MakeVec(f0, -f1, f0, f1)}, // y = +w |
| 122 | {Math::MakeVec(f0, -f1, f0, -f1)}, // y = -w | 122 | {Math::MakeVec(f0, f1, f0, f1)}, // y = -w |
| 123 | {Math::MakeVec(f0, f0, f1, f0)}, // z = 0 | 123 | {Math::MakeVec(f0, f0, -f1, f0)}, // z = 0 |
| 124 | {Math::MakeVec(f0, f0, -f1, -f1)}, // z = -w | 124 | {Math::MakeVec(f0, f0, f1, f1)}, // z = -w |
| 125 | {Math::MakeVec(f0, f0, f0, -f1), Math::Vec4<float24>(f0, f0, f0, EPSILON)}, // w = EPSILON | 125 | {Math::MakeVec(f0, f0, f0, f1), Math::Vec4<float24>(f0, f0, f0, EPSILON)}, // w = EPSILON |
| 126 | }}; | 126 | }}; |
| 127 | 127 | ||
| 128 | // Simple implementation of the Sutherland-Hodgman clipping algorithm. | 128 | // Simple implementation of the Sutherland-Hodgman clipping algorithm. |
| 129 | // TODO: Make this less inefficient (currently lots of useless buffering overhead happens here) | 129 | // TODO: Make this less inefficient (currently lots of useless buffering overhead happens here) |
| 130 | for (auto edge : clipping_edges) { | 130 | auto Clip = [&](const ClippingEdge& edge) { |
| 131 | |||
| 132 | std::swap(input_list, output_list); | 131 | std::swap(input_list, output_list); |
| 133 | output_list->clear(); | 132 | output_list->clear(); |
| 134 | 133 | ||
| @@ -147,12 +146,24 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu | |||
| 147 | } | 146 | } |
| 148 | reference_vertex = &vertex; | 147 | reference_vertex = &vertex; |
| 149 | } | 148 | } |
| 149 | }; | ||
| 150 | |||
| 151 | for (auto edge : clipping_edges) { | ||
| 152 | Clip(edge); | ||
| 150 | 153 | ||
| 151 | // Need to have at least a full triangle to continue... | 154 | // Need to have at least a full triangle to continue... |
| 152 | if (output_list->size() < 3) | 155 | if (output_list->size() < 3) |
| 153 | return; | 156 | return; |
| 154 | } | 157 | } |
| 155 | 158 | ||
| 159 | if (g_state.regs.rasterizer.clip_enable) { | ||
| 160 | ClippingEdge custom_edge{g_state.regs.rasterizer.GetClipCoef()}; | ||
| 161 | Clip(custom_edge); | ||
| 162 | |||
| 163 | if (output_list->size() < 3) | ||
| 164 | return; | ||
| 165 | } | ||
| 166 | |||
| 156 | InitScreenCoordinates((*output_list)[0]); | 167 | InitScreenCoordinates((*output_list)[0]); |
| 157 | InitScreenCoordinates((*output_list)[1]); | 168 | InitScreenCoordinates((*output_list)[1]); |
| 158 | 169 | ||
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt index 334d82a8a..c93811892 100644 --- a/src/web_service/CMakeLists.txt +++ b/src/web_service/CMakeLists.txt | |||
| @@ -1,10 +1,12 @@ | |||
| 1 | set(SRCS | 1 | set(SRCS |
| 2 | telemetry_json.cpp | 2 | telemetry_json.cpp |
| 3 | verify_login.cpp | ||
| 3 | web_backend.cpp | 4 | web_backend.cpp |
| 4 | ) | 5 | ) |
| 5 | 6 | ||
| 6 | set(HEADERS | 7 | set(HEADERS |
| 7 | telemetry_json.h | 8 | telemetry_json.h |
| 9 | verify_login.h | ||
| 8 | web_backend.h | 10 | web_backend.h |
| 9 | ) | 11 | ) |
| 10 | 12 | ||
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp new file mode 100644 index 000000000..1bc3b5afe --- /dev/null +++ b/src/web_service/verify_login.cpp | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <json.hpp> | ||
| 6 | #include "web_service/verify_login.h" | ||
| 7 | #include "web_service/web_backend.h" | ||
| 8 | |||
| 9 | namespace WebService { | ||
| 10 | |||
| 11 | std::future<bool> VerifyLogin(std::string& username, std::string& token, | ||
| 12 | const std::string& endpoint_url, std::function<void()> func) { | ||
| 13 | auto get_func = [func, username](const std::string& reply) -> bool { | ||
| 14 | func(); | ||
| 15 | if (reply.empty()) | ||
| 16 | return false; | ||
| 17 | nlohmann::json json = nlohmann::json::parse(reply); | ||
| 18 | std::string result; | ||
| 19 | try { | ||
| 20 | result = json["username"]; | ||
| 21 | } catch (const nlohmann::detail::out_of_range&) { | ||
| 22 | } | ||
| 23 | return result == username; | ||
| 24 | }; | ||
| 25 | return GetJson<bool>(get_func, endpoint_url, false, username, token); | ||
| 26 | } | ||
| 27 | |||
| 28 | } // namespace WebService | ||
diff --git a/src/web_service/verify_login.h b/src/web_service/verify_login.h new file mode 100644 index 000000000..303f5dbbc --- /dev/null +++ b/src/web_service/verify_login.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include <future> | ||
| 9 | #include <string> | ||
| 10 | |||
| 11 | namespace WebService { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Checks if username and token is valid | ||
| 15 | * @param username Citra username to use for authentication. | ||
| 16 | * @param token Citra token to use for authentication. | ||
| 17 | * @param endpoint_url URL of the services.citra-emu.org endpoint. | ||
| 18 | * @param func A function that gets exectued when the verification is finished | ||
| 19 | * @returns Future with bool indicating whether the verification succeeded | ||
| 20 | */ | ||
| 21 | std::future<bool> VerifyLogin(std::string& username, std::string& token, | ||
| 22 | const std::string& endpoint_url, std::function<void()> func); | ||
| 23 | |||
| 24 | } // namespace WebService | ||
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index d28a3f757..b17d82f9c 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp | |||
| @@ -18,6 +18,19 @@ static constexpr char API_VERSION[]{"1"}; | |||
| 18 | 18 | ||
| 19 | static std::unique_ptr<cpr::Session> g_session; | 19 | static std::unique_ptr<cpr::Session> g_session; |
| 20 | 20 | ||
| 21 | void Win32WSAStartup() { | ||
| 22 | #ifdef _WIN32 | ||
| 23 | // On Windows, CPR/libcurl does not properly initialize Winsock. The below code is used to | ||
| 24 | // initialize Winsock globally, which fixes this problem. Without this, only the first CPR | ||
| 25 | // session will properly be created, and subsequent ones will fail. | ||
| 26 | WSADATA wsa_data; | ||
| 27 | const int wsa_result{WSAStartup(MAKEWORD(2, 2), &wsa_data)}; | ||
| 28 | if (wsa_result) { | ||
| 29 | LOG_CRITICAL(WebService, "WSAStartup failed: %d", wsa_result); | ||
| 30 | } | ||
| 31 | #endif | ||
| 32 | } | ||
| 33 | |||
| 21 | void PostJson(const std::string& url, const std::string& data, bool allow_anonymous, | 34 | void PostJson(const std::string& url, const std::string& data, bool allow_anonymous, |
| 22 | const std::string& username, const std::string& token) { | 35 | const std::string& username, const std::string& token) { |
| 23 | if (url.empty()) { | 36 | if (url.empty()) { |
| @@ -31,16 +44,7 @@ void PostJson(const std::string& url, const std::string& data, bool allow_anonym | |||
| 31 | return; | 44 | return; |
| 32 | } | 45 | } |
| 33 | 46 | ||
| 34 | #ifdef _WIN32 | 47 | Win32WSAStartup(); |
| 35 | // On Windows, CPR/libcurl does not properly initialize Winsock. The below code is used to | ||
| 36 | // initialize Winsock globally, which fixes this problem. Without this, only the first CPR | ||
| 37 | // session will properly be created, and subsequent ones will fail. | ||
| 38 | WSADATA wsa_data; | ||
| 39 | const int wsa_result{WSAStartup(MAKEWORD(2, 2), &wsa_data)}; | ||
| 40 | if (wsa_result) { | ||
| 41 | LOG_CRITICAL(WebService, "WSAStartup failed: %d", wsa_result); | ||
| 42 | } | ||
| 43 | #endif | ||
| 44 | 48 | ||
| 45 | // Built request header | 49 | // Built request header |
| 46 | cpr::Header header; | 50 | cpr::Header header; |
| @@ -56,8 +60,81 @@ void PostJson(const std::string& url, const std::string& data, bool allow_anonym | |||
| 56 | } | 60 | } |
| 57 | 61 | ||
| 58 | // Post JSON asynchronously | 62 | // Post JSON asynchronously |
| 59 | static cpr::AsyncResponse future; | 63 | static std::future<void> future; |
| 60 | future = cpr::PostAsync(cpr::Url{url.c_str()}, cpr::Body{data.c_str()}, header); | 64 | future = cpr::PostCallback( |
| 65 | [](cpr::Response r) { | ||
| 66 | if (r.error) { | ||
| 67 | LOG_ERROR(WebService, "POST returned cpr error: %u:%s", | ||
| 68 | static_cast<u32>(r.error.code), r.error.message.c_str()); | ||
| 69 | return; | ||
| 70 | } | ||
| 71 | if (r.status_code >= 400) { | ||
| 72 | LOG_ERROR(WebService, "POST returned error status code: %u", r.status_code); | ||
| 73 | return; | ||
| 74 | } | ||
| 75 | if (r.header["content-type"].find("application/json") == std::string::npos) { | ||
| 76 | LOG_ERROR(WebService, "POST returned wrong content: %s", | ||
| 77 | r.header["content-type"].c_str()); | ||
| 78 | return; | ||
| 79 | } | ||
| 80 | }, | ||
| 81 | cpr::Url{url}, cpr::Body{data}, header); | ||
| 82 | } | ||
| 83 | |||
| 84 | template <typename T> | ||
| 85 | std::future<T> GetJson(std::function<T(const std::string&)> func, const std::string& url, | ||
| 86 | bool allow_anonymous, const std::string& username, | ||
| 87 | const std::string& token) { | ||
| 88 | if (url.empty()) { | ||
| 89 | LOG_ERROR(WebService, "URL is invalid"); | ||
| 90 | return std::async(std::launch::async, [func{std::move(func)}]() { return func(""); }); | ||
| 91 | } | ||
| 92 | |||
| 93 | const bool are_credentials_provided{!token.empty() && !username.empty()}; | ||
| 94 | if (!allow_anonymous && !are_credentials_provided) { | ||
| 95 | LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); | ||
| 96 | return std::async(std::launch::async, [func{std::move(func)}]() { return func(""); }); | ||
| 97 | } | ||
| 98 | |||
| 99 | Win32WSAStartup(); | ||
| 100 | |||
| 101 | // Built request header | ||
| 102 | cpr::Header header; | ||
| 103 | if (are_credentials_provided) { | ||
| 104 | // Authenticated request if credentials are provided | ||
| 105 | header = {{"Content-Type", "application/json"}, | ||
| 106 | {"x-username", username.c_str()}, | ||
| 107 | {"x-token", token.c_str()}, | ||
| 108 | {"api-version", API_VERSION}}; | ||
| 109 | } else { | ||
| 110 | // Otherwise, anonymous request | ||
| 111 | header = cpr::Header{{"Content-Type", "application/json"}, {"api-version", API_VERSION}}; | ||
| 112 | } | ||
| 113 | |||
| 114 | // Get JSON asynchronously | ||
| 115 | return cpr::GetCallback( | ||
| 116 | [func{std::move(func)}](cpr::Response r) { | ||
| 117 | if (r.error) { | ||
| 118 | LOG_ERROR(WebService, "GET returned cpr error: %u:%s", | ||
| 119 | static_cast<u32>(r.error.code), r.error.message.c_str()); | ||
| 120 | return func(""); | ||
| 121 | } | ||
| 122 | if (r.status_code >= 400) { | ||
| 123 | LOG_ERROR(WebService, "GET returned error code: %u", r.status_code); | ||
| 124 | return func(""); | ||
| 125 | } | ||
| 126 | if (r.header["content-type"].find("application/json") == std::string::npos) { | ||
| 127 | LOG_ERROR(WebService, "GET returned wrong content: %s", | ||
| 128 | r.header["content-type"].c_str()); | ||
| 129 | return func(""); | ||
| 130 | } | ||
| 131 | return func(r.text); | ||
| 132 | }, | ||
| 133 | cpr::Url{url}, header); | ||
| 61 | } | 134 | } |
| 62 | 135 | ||
| 136 | template std::future<bool> GetJson(std::function<bool(const std::string&)> func, | ||
| 137 | const std::string& url, bool allow_anonymous, | ||
| 138 | const std::string& username, const std::string& token); | ||
| 139 | |||
| 63 | } // namespace WebService | 140 | } // namespace WebService |
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h index d17100398..a63c75d13 100644 --- a/src/web_service/web_backend.h +++ b/src/web_service/web_backend.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <functional> | ||
| 8 | #include <future> | ||
| 7 | #include <string> | 9 | #include <string> |
| 8 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 9 | 11 | ||
| @@ -20,4 +22,18 @@ namespace WebService { | |||
| 20 | void PostJson(const std::string& url, const std::string& data, bool allow_anonymous, | 22 | void PostJson(const std::string& url, const std::string& data, bool allow_anonymous, |
| 21 | const std::string& username = {}, const std::string& token = {}); | 23 | const std::string& username = {}, const std::string& token = {}); |
| 22 | 24 | ||
| 25 | /** | ||
| 26 | * Gets JSON from services.citra-emu.org. | ||
| 27 | * @param func A function that gets exectued when the json as a string is received | ||
| 28 | * @param url URL of the services.citra-emu.org endpoint to post data to. | ||
| 29 | * @param allow_anonymous If true, allow anonymous unauthenticated requests. | ||
| 30 | * @param username Citra username to use for authentication. | ||
| 31 | * @param token Citra token to use for authentication. | ||
| 32 | * @return future that holds the return value T of the func | ||
| 33 | */ | ||
| 34 | template <typename T> | ||
| 35 | std::future<T> GetJson(std::function<T(const std::string&)> func, const std::string& url, | ||
| 36 | bool allow_anonymous, const std::string& username = {}, | ||
| 37 | const std::string& token = {}); | ||
| 38 | |||
| 23 | } // namespace WebService | 39 | } // namespace WebService |