summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/config.cpp2
-rw-r--r--src/citra/default_ini.h2
-rw-r--r--src/citra_qt/CMakeLists.txt5
-rw-r--r--src/citra_qt/configuration/config.cpp6
-rw-r--r--src/citra_qt/configuration/configure_web.cpp58
-rw-r--r--src/citra_qt/configuration/configure_web.h12
-rw-r--r--src/citra_qt/configuration/configure_web.ui75
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp4
-rw-r--r--src/core/core.cpp1
-rw-r--r--src/core/hle/kernel/memory.cpp30
-rw-r--r--src/core/hle/kernel/memory.h2
-rw-r--r--src/core/hle/kernel/thread.cpp12
-rw-r--r--src/core/hle/kernel/vm_manager.cpp13
-rw-r--r--src/core/hle/kernel/vm_manager.h6
-rw-r--r--src/core/hle/service/apt/apt.cpp286
-rw-r--r--src/core/hle/service/cfg/cfg.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg.h2
-rw-r--r--src/core/hle/service/ns/ns.cpp16
-rw-r--r--src/core/hle/service/ns/ns.h16
-rw-r--r--src/core/hle/service/ns/ns_s.cpp34
-rw-r--r--src/core/hle/service/ns/ns_s.h (renamed from src/core/hle/service/ns_s.h)9
-rw-r--r--src/core/hle/service/ns_s.cpp33
-rw-r--r--src/core/hle/service/service.cpp5
-rw-r--r--src/core/loader/3dsx.cpp1
-rw-r--r--src/core/loader/elf.cpp1
-rw-r--r--src/core/loader/ncch.cpp1
-rw-r--r--src/core/memory.cpp153
-rw-r--r--src/core/memory.h62
-rw-r--r--src/core/memory_setup.h10
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp12
-rw-r--r--src/core/telemetry_session.h10
-rw-r--r--src/tests/core/arm/arm_test_common.cpp18
-rw-r--r--src/video_core/regs_rasterizer.h14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp53
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h9
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp80
-rw-r--r--src/video_core/shader/shader.cpp3
-rw-r--r--src/video_core/swrasterizer/clipper.cpp31
-rw-r--r--src/web_service/CMakeLists.txt2
-rw-r--r--src/web_service/verify_login.cpp28
-rw-r--r--src/web_service/verify_login.h24
-rw-r--r--src/web_service/web_backend.cpp101
-rw-r--r--src/web_service/web_backend.h16
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
185enable_telemetry = 185enable_telemetry =
186# Endpoint URL for submitting telemetry data 186# Endpoint URL for submitting telemetry data
187telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry 187telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry
188# Endpoint URL to verify the username and token
189verify_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
190citra_username = 192citra_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
82file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*)
82file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) 83file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
83 84
84create_directory_groups(${SRCS} ${HEADERS} ${UIS}) 85create_directory_groups(${SRCS} ${HEADERS} ${UIS})
@@ -92,10 +93,10 @@ endif()
92if (APPLE) 93if (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)
97else() 98else()
98 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES}) 99 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${ICONS} ${THEMES})
99endif() 100endif()
100target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core) 101target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
101target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets) 102target_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
41void ConfigureWeb::applyConfiguration() { 48void 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
48void ConfigureWeb::refreshTelemetryID() { 61void 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
67void 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
79void 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
87void 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
23public slots: 24public slots:
24 void refreshTelemetryID(); 25 void RefreshTelemetryID();
26 void OnLoginChanged();
27 void VerifyLogin();
28 void OnLoginVerified();
29
30signals:
31 void LoginVerified();
25 32
26private: 33private:
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
139System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { 139System::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
25namespace Kernel { 24namespace Kernel {
26 25
27static MemoryRegionInfo memory_regions[3]; 26MemoryRegionInfo 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
99std::array<u8, Memory::VRAM_SIZE> vram;
100std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
101
102void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { 98void 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
27void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); 27void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
28void MapSharedPages(VMManager& address_space); 28void MapSharedPages(VMManager& address_space);
29
30extern 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) {
328void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { 332void 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
14namespace Kernel { 15namespace 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 */
104class VMManager final { 105class VMManager final {
105 // TODO(yuriks): Make page tables switchable to support multiple VMManagers
106public: 106public:
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
187private: 191private:
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
202static 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
256static 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
321static 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
201void GetSharedFont(Service::Interface* self) { 339void 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
866static 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
920static 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
967static 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
985void Init() { 1013void 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
171static u32 GetRegionValue() { 171u32 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 */
102void GetCountryCodeID(Service::Interface* self); 102void GetCountryCodeID(Service::Interface* self);
103 103
104u32 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
8namespace Service {
9namespace NS {
10
11void 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
9namespace Service {
10namespace NS {
11
12/// Registers all NS services with the specified service manager.
13void 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
7namespace Service {
8namespace NS {
9
10NS_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
31NS_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
9namespace Service { 10namespace Service {
10namespace NS { 11namespace NS {
11 12
12class NS_S final : public Interface { 13/// Interface to "ns:s" service
14class NS_S final : public ServiceFramework<NS_S> {
13public: 15public:
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
7namespace Service {
8namespace NS {
9
10const 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
28NS_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
19namespace Memory { 20namespace Memory {
20 21
21enum class PageType { 22static std::array<u8, Memory::VRAM_SIZE> vram;
22 /// Page is unmapped and should cause an access error. 23static 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
36struct SpecialRegion {
37 VAddr base;
38 u32 size;
39 MMIORegionPointer handler;
40};
41 24
42/** 25PageTable* 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 */
48struct 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
75static PageTable main_page_table;
76/// Currently active page table
77static PageTable* current_page_table = &main_page_table;
78 26
79std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { 27std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
80 return &current_page_table->pointers; 28 return &current_page_table->pointers;
81} 29}
82 30
83static void MapPages(u32 base, u32 size, u8* memory, PageType type) { 31static 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
104void InitMemoryMap() { 52void 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
110void 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
116void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) { 58void 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
124void UnmapRegion(VAddr base, u32 size) { 66void 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
275bool IsValidPhysicalAddress(const PAddr paddr) { 217bool 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
280u8* GetPointer(const VAddr vaddr) { 221u8* GetPointer(const VAddr vaddr) {
@@ -306,9 +247,63 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) {
306} 247}
307 248
308u8* GetPhysicalPointer(PAddr address) { 249u8* 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
314void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { 309void 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
13namespace Memory { 15namespace Memory {
14 16
@@ -21,6 +23,59 @@ const u32 PAGE_MASK = PAGE_SIZE - 1;
21const int PAGE_BITS = 12; 23const int PAGE_BITS = 12;
22const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); 24const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS);
23 25
26enum 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
41struct 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 */
53struct 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
25enum : PAddr { 80enum : 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
185extern PageTable* current_page_table;
186
129bool IsValidVirtualAddress(const VAddr addr); 187bool IsValidVirtualAddress(const VAddr addr);
130bool IsValidPhysicalAddress(const PAddr addr); 188bool 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 */
175u8* GetPhysicalPointer(PAddr address); 231u8* 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 */
211std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers(); 267std::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
10namespace Memory { 10namespace Memory {
11 11
12void 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 */
21void MapMemoryRegion(VAddr base, u32 size, u8* target); 20void 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 */
29void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler); 29void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler);
30 30
31void UnmapRegion(VAddr base, u32 size); 31void 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
20namespace Core { 21namespace Core {
@@ -75,6 +76,17 @@ u64 RegenerateTelemetryId() {
75 return new_telemetry_id; 76 return new_telemetry_id;
76} 77}
77 78
79std::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
78TelemetrySession::TelemetrySession() { 90TelemetrySession::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 */
48u64 RegenerateTelemetryId(); 49u64 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 */
58std::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
9namespace ArmTests { 10namespace ArmTests {
10 11
12static Memory::PageTable page_table;
13
11TestEnvironment::TestEnvironment(bool mutable_memory_) 14TestEnvironment::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
17TestEnvironment::~TestEnvironment() { 27TestEnvironment::~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
22void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) { 32void 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
13namespace Pica { 13namespace 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
1308void RasterizerOpenGL::SyncClipEnabled() {
1309 state.clip_distance[1] = Pica::g_state.regs.rasterizer.clip_enable != 0;
1310}
1311
1312void 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
1283void RasterizerOpenGL::SyncCullMode() { 1322void 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
26namespace GLShader { 26namespace GLShader {
27 27
28static const std::string UniformBlockDef = R"(
29#define NUM_TEV_STAGES 6
30#define NUM_LIGHTS 8
31
32struct 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
43layout (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
28PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) { 64PicaShaderConfig 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
1016in vec4 primary_color; 1050in vec4 primary_color;
1017in vec2 texcoord[3]; 1051in vec2 texcoord[3];
@@ -1023,36 +1057,6 @@ in vec4 gl_FragCoord;
1023 1057
1024out vec4 color; 1058out vec4 color;
1025 1059
1026struct 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
1037layout (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
1056uniform sampler2D tex[3]; 1060uniform sampler2D tex[3];
1057uniform samplerBuffer lighting_lut; 1061uniform samplerBuffer lighting_lut;
1058uniform samplerBuffer fog_lut; 1062uniform samplerBuffer fog_lut;
@@ -1061,7 +1065,11 @@ uniform samplerBuffer proctex_color_map;
1061uniform samplerBuffer proctex_alpha_map; 1065uniform samplerBuffer proctex_alpha_map;
1062uniform samplerBuffer proctex_lut; 1066uniform samplerBuffer proctex_lut;
1063uniform samplerBuffer proctex_diff_lut; 1067uniform 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
1066vec3 quaternion_rotate(vec4 q, vec3 v) { 1074vec3 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;
1197out vec4 normquat; 1205out vec4 normquat;
1198out vec3 view; 1206out vec3 view;
1199 1207
1208)";
1209
1210 out += UniformBlockDef;
1211
1212 out += R"(
1213
1200void main() { 1214void 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 @@
1set(SRCS 1set(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
6set(HEADERS 7set(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
9namespace WebService {
10
11std::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
11namespace 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 */
21std::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
19static std::unique_ptr<cpr::Session> g_session; 19static std::unique_ptr<cpr::Session> g_session;
20 20
21void 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
21void PostJson(const std::string& url, const std::string& data, bool allow_anonymous, 34void 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
84template <typename T>
85std::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
136template 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 {
20void PostJson(const std::string& url, const std::string& data, bool allow_anonymous, 22void 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 */
34template <typename T>
35std::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