summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rwxr-xr-x.travis-build.sh4
-rwxr-xr-x.travis-upload.sh4
-rw-r--r--src/audio_core/codec.cpp4
-rw-r--r--src/audio_core/codec.h4
-rw-r--r--src/audio_core/hle/source.cpp2
-rw-r--r--src/audio_core/hle/source.h2
-rw-r--r--src/audio_core/interpolate.cpp2
-rw-r--r--src/audio_core/interpolate.h4
-rw-r--r--src/citra_qt/configuration/configure_graphics.ui22
-rw-r--r--src/citra_qt/configuration/configure_system.cpp3
-rw-r--r--src/citra_qt/configuration/configure_web.cpp18
-rw-r--r--src/citra_qt/debugger/graphics/graphics_cmdlists.cpp4
-rw-r--r--src/citra_qt/debugger/graphics/graphics_surface.cpp3
-rw-r--r--src/common/string_util.cpp2
-rw-r--r--src/common/string_util.h2
-rw-r--r--src/common/vector_math.h24
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/arm/arm_interface.h12
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp31
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h12
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp12
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h3
-rw-r--r--src/core/core.cpp6
-rw-r--r--src/core/core_timing.cpp36
-rw-r--r--src/core/core_timing.h6
-rw-r--r--src/core/file_sys/archive_selfncch.cpp57
-rw-r--r--src/core/file_sys/archive_selfncch.h13
-rw-r--r--src/core/file_sys/ncch_container.cpp316
-rw-r--r--src/core/file_sys/ncch_container.h244
-rw-r--r--src/core/gdbstub/gdbstub.cpp4
-rw-r--r--src/core/hle/ipc.h8
-rw-r--r--src/core/hle/ipc_helpers.h12
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp2
-rw-r--r--src/core/hle/kernel/mutex.cpp2
-rw-r--r--src/core/hle/kernel/process.cpp2
-rw-r--r--src/core/hle/kernel/resource_limit.cpp2
-rw-r--r--src/core/hle/kernel/resource_limit.h2
-rw-r--r--src/core/hle/kernel/shared_memory.cpp30
-rw-r--r--src/core/hle/kernel/shared_memory.h2
-rw-r--r--src/core/hle/kernel/thread.cpp63
-rw-r--r--src/core/hle/kernel/thread.h48
-rw-r--r--src/core/hle/kernel/wait_object.cpp13
-rw-r--r--src/core/hle/service/apt/apt.cpp43
-rw-r--r--src/core/hle/service/apt/apt.h10
-rw-r--r--src/core/hle/service/apt/apt_s.cpp4
-rw-r--r--src/core/hle/service/cam/cam.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg.cpp2
-rw-r--r--src/core/hle/service/fs/archive.cpp20
-rw-r--r--src/core/hle/service/fs/archive.h7
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/ldr_ro/cro_helper.h6
-rw-r--r--src/core/hle/service/nim/nim.cpp18
-rw-r--r--src/core/hle/service/nim/nim.h11
-rw-r--r--src/core/hle/service/nim/nim_u.cpp2
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp402
-rw-r--r--src/core/hle/service/nwm/uds_beacon.cpp4
-rw-r--r--src/core/hle/service/nwm/uds_connection.cpp9
-rw-r--r--src/core/hle/service/nwm/uds_connection.h5
-rw-r--r--src/core/hle/service/nwm/uds_data.cpp103
-rw-r--r--src/core/hle/service/nwm/uds_data.h86
-rw-r--r--src/core/hle/service/sm/sm.cpp4
-rw-r--r--src/core/hle/service/sm/sm.h3
-rw-r--r--src/core/hle/service/sm/srv.cpp26
-rw-r--r--src/core/hle/service/sm/srv.h1
-rw-r--r--src/core/hle/svc.cpp84
-rw-r--r--src/core/loader/3dsx.cpp18
-rw-r--r--src/core/loader/3dsx.h2
-rw-r--r--src/core/loader/elf.cpp15
-rw-r--r--src/core/loader/elf.h2
-rw-r--r--src/core/loader/loader.h26
-rw-r--r--src/core/loader/ncch.cpp341
-rw-r--r--src/core/loader/ncch.h189
-rw-r--r--src/core/memory.cpp160
-rw-r--r--src/core/memory.h20
-rw-r--r--src/network/packet.cpp2
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/core/arm/arm_test_common.cpp22
-rw-r--r--src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp2
-rw-r--r--src/tests/core/memory/memory.cpp56
-rw-r--r--src/video_core/command_processor.cpp440
-rw-r--r--src/video_core/geometry_pipeline.cpp2
-rw-r--r--src/video_core/pica_types.h18
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp4
-rw-r--r--src/video_core/swrasterizer/clipper.cpp2
-rw-r--r--src/video_core/utils.h14
86 files changed, 2126 insertions, 1111 deletions
diff --git a/.gitignore b/.gitignore
index ec74b0fa4..7999a40e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,12 +9,16 @@ src/common/scm_rev.cpp
9# Project/editor files 9# Project/editor files
10*.swp 10*.swp
11.idea/ 11.idea/
12.vs/
12.vscode/ 13.vscode/
13 14
14# *nix related 15# *nix related
15# Common convention for backup or temporary files 16# Common convention for backup or temporary files
16*~ 17*~
17 18
19# Visual Studio CMake settings
20CMakeSettings.json
21
18# OSX global filetypes 22# OSX global filetypes
19# Created by Finder or Spotlight in directories for various OS functionality (indexing, etc) 23# Created by Finder or Spotlight in directories for various OS functionality (indexing, etc)
20.DS_Store 24.DS_Store
diff --git a/.travis-build.sh b/.travis-build.sh
index bb4e6fc47..fc5a5f8b2 100755
--- a/.travis-build.sh
+++ b/.travis-build.sh
@@ -52,8 +52,8 @@ elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
52 export Qt5_DIR=$(brew --prefix)/opt/qt5 52 export Qt5_DIR=$(brew --prefix)/opt/qt5
53 53
54 mkdir build && cd build 54 mkdir build && cd build
55 cmake .. -DUSE_SYSTEM_CURL=ON -GXcode 55 cmake .. -DUSE_SYSTEM_CURL=ON -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h" -DCMAKE_BUILD_TYPE=Release
56 xcodebuild -configuration Release 56 make -j4
57 57
58 ctest -VV -C Release 58 ctest -VV -C Release
59fi 59fi
diff --git a/.travis-upload.sh b/.travis-upload.sh
index 8c1fa21c5..edf195f7d 100755
--- a/.travis-upload.sh
+++ b/.travis-upload.sh
@@ -16,8 +16,8 @@ elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
16 COMPRESSION_FLAGS="-czvf" 16 COMPRESSION_FLAGS="-czvf"
17 mkdir "$REV_NAME" 17 mkdir "$REV_NAME"
18 18
19 cp build/src/citra/Release/citra "$REV_NAME" 19 cp build/src/citra/citra "$REV_NAME"
20 cp -r build/src/citra_qt/Release/citra-qt.app "$REV_NAME" 20 cp -r build/src/citra_qt/citra-qt.app "$REV_NAME"
21 21
22 # move qt libs into app bundle for deployment 22 # move qt libs into app bundle for deployment
23 $(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/citra-qt.app" 23 $(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/citra-qt.app"
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp
index 7a3bd7eb3..6fba9fdae 100644
--- a/src/audio_core/codec.cpp
+++ b/src/audio_core/codec.cpp
@@ -117,7 +117,9 @@ StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data,
117 ret[i].fill(sample); 117 ret[i].fill(sample);
118 } 118 }
119 } else { 119 } else {
120 std::memcpy(ret.data(), data, sample_count * 2 * sizeof(u16)); 120 for (size_t i = 0; i < sample_count; ++i) {
121 std::memcpy(&ret[i], data + i * sizeof(s16) * 2, 2 * sizeof(s16));
122 }
121 } 123 }
122 124
123 return ret; 125 return ret;
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
index 2b0c395e6..877b2202d 100644
--- a/src/audio_core/codec.h
+++ b/src/audio_core/codec.h
@@ -5,13 +5,13 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <deque>
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace Codec { 11namespace Codec {
12 12
13/// A variable length buffer of signed PCM16 stereo samples. 13/// A variable length buffer of signed PCM16 stereo samples.
14using StereoBuffer16 = std::vector<std::array<s16, 2>>; 14using StereoBuffer16 = std::deque<std::array<s16, 2>>;
15 15
16/// See: Codec::DecodeADPCM 16/// See: Codec::DecodeADPCM
17struct ADPCMState { 17struct ADPCMState {
diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp
index de4e88cae..c12287700 100644
--- a/src/audio_core/hle/source.cpp
+++ b/src/audio_core/hle/source.cpp
@@ -264,7 +264,7 @@ void Source::GenerateFrame() {
264 break; 264 break;
265 } 265 }
266 } 266 }
267 state.next_sample_number += frame_position; 267 state.next_sample_number += static_cast<u32>(frame_position);
268 268
269 state.filters.ProcessFrame(current_frame); 269 state.filters.ProcessFrame(current_frame);
270} 270}
diff --git a/src/audio_core/hle/source.h b/src/audio_core/hle/source.h
index ccb7f064f..c4d2debc2 100644
--- a/src/audio_core/hle/source.h
+++ b/src/audio_core/hle/source.h
@@ -108,7 +108,7 @@ private:
108 108
109 u32 current_sample_number = 0; 109 u32 current_sample_number = 0;
110 u32 next_sample_number = 0; 110 u32 next_sample_number = 0;
111 std::vector<std::array<s16, 2>> current_buffer; 111 AudioInterp::StereoBuffer16 current_buffer;
112 112
113 // buffer_id state 113 // buffer_id state
114 114
diff --git a/src/audio_core/interpolate.cpp b/src/audio_core/interpolate.cpp
index 16e68bc5c..83573d772 100644
--- a/src/audio_core/interpolate.cpp
+++ b/src/audio_core/interpolate.cpp
@@ -47,7 +47,7 @@ static void StepOverSamples(State& state, StereoBuffer16& input, float rate,
47 state.xn1 = input[inputi + 1]; 47 state.xn1 = input[inputi + 1];
48 state.fposition = fposition - inputi * scale_factor; 48 state.fposition = fposition - inputi * scale_factor;
49 49
50 input.erase(input.begin(), input.begin() + inputi + 2); 50 input.erase(input.begin(), std::next(input.begin(), inputi + 2));
51} 51}
52 52
53void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, 53void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output,
diff --git a/src/audio_core/interpolate.h b/src/audio_core/interpolate.h
index 59f59bc14..8dff6111a 100644
--- a/src/audio_core/interpolate.h
+++ b/src/audio_core/interpolate.h
@@ -5,14 +5,14 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <deque>
9#include "audio_core/hle/common.h" 9#include "audio_core/hle/common.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioInterp { 12namespace AudioInterp {
13 13
14/// A variable length buffer of signed PCM16 stereo samples. 14/// A variable length buffer of signed PCM16 stereo samples.
15using StereoBuffer16 = std::vector<std::array<s16, 2>>; 15using StereoBuffer16 = std::deque<std::array<s16, 2>>;
16 16
17struct State { 17struct State {
18 /// Two historical samples. 18 /// Two historical samples.
diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui
index b340149d5..5667b14b6 100644
--- a/src/citra_qt/configuration/configure_graphics.ui
+++ b/src/citra_qt/configuration/configure_graphics.ui
@@ -63,57 +63,57 @@
63 <widget class="QComboBox" name="resolution_factor_combobox"> 63 <widget class="QComboBox" name="resolution_factor_combobox">
64 <item> 64 <item>
65 <property name="text"> 65 <property name="text">
66 <string notr="true">Auto (Window Size)</string> 66 <string>Auto (Window Size)</string>
67 </property> 67 </property>
68 </item> 68 </item>
69 <item> 69 <item>
70 <property name="text"> 70 <property name="text">
71 <string notr="true">Native (400x240)</string> 71 <string>Native (400x240)</string>
72 </property> 72 </property>
73 </item> 73 </item>
74 <item> 74 <item>
75 <property name="text"> 75 <property name="text">
76 <string notr="true">2x Native (800x480)</string> 76 <string>2x Native (800x480)</string>
77 </property> 77 </property>
78 </item> 78 </item>
79 <item> 79 <item>
80 <property name="text"> 80 <property name="text">
81 <string notr="true">3x Native (1200x720)</string> 81 <string>3x Native (1200x720)</string>
82 </property> 82 </property>
83 </item> 83 </item>
84 <item> 84 <item>
85 <property name="text"> 85 <property name="text">
86 <string notr="true">4x Native (1600x960)</string> 86 <string>4x Native (1600x960)</string>
87 </property> 87 </property>
88 </item> 88 </item>
89 <item> 89 <item>
90 <property name="text"> 90 <property name="text">
91 <string notr="true">5x Native (2000x1200)</string> 91 <string>5x Native (2000x1200)</string>
92 </property> 92 </property>
93 </item> 93 </item>
94 <item> 94 <item>
95 <property name="text"> 95 <property name="text">
96 <string notr="true">6x Native (2400x1440)</string> 96 <string>6x Native (2400x1440)</string>
97 </property> 97 </property>
98 </item> 98 </item>
99 <item> 99 <item>
100 <property name="text"> 100 <property name="text">
101 <string notr="true">7x Native (2800x1680)</string> 101 <string>7x Native (2800x1680)</string>
102 </property> 102 </property>
103 </item> 103 </item>
104 <item> 104 <item>
105 <property name="text"> 105 <property name="text">
106 <string notr="true">8x Native (3200x1920)</string> 106 <string>8x Native (3200x1920)</string>
107 </property> 107 </property>
108 </item> 108 </item>
109 <item> 109 <item>
110 <property name="text"> 110 <property name="text">
111 <string notr="true">9x Native (3600x2160)</string> 111 <string>9x Native (3600x2160)</string>
112 </property> 112 </property>
113 </item> 113 </item>
114 <item> 114 <item>
115 <property name="text"> 115 <property name="text">
116 <string notr="true">10x Native (4000x2400)</string> 116 <string>10x Native (4000x2400)</string>
117 </property> 117 </property>
118 </item> 118 </item>
119 </widget> 119 </widget>
diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp
index 9b1e6711d..88a067c12 100644
--- a/src/citra_qt/configuration/configure_system.cpp
+++ b/src/citra_qt/configuration/configure_system.cpp
@@ -78,7 +78,8 @@ void ConfigureSystem::ReadSystemSettings() {
78 78
79 // set the console id 79 // set the console id
80 u64 console_id = Service::CFG::GetConsoleUniqueId(); 80 u64 console_id = Service::CFG::GetConsoleUniqueId();
81 ui->label_console_id->setText("Console ID: 0x" + QString::number(console_id, 16).toUpper()); 81 ui->label_console_id->setText(
82 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
82} 83}
83 84
84void ConfigureSystem::applyConfiguration() { 85void ConfigureSystem::applyConfiguration() {
diff --git a/src/citra_qt/configuration/configure_web.cpp b/src/citra_qt/configuration/configure_web.cpp
index 38ce19c0f..bf8c21ac7 100644
--- a/src/citra_qt/configuration/configure_web.cpp
+++ b/src/citra_qt/configuration/configure_web.cpp
@@ -24,15 +24,15 @@ ConfigureWeb::~ConfigureWeb() {}
24void ConfigureWeb::setConfiguration() { 24void ConfigureWeb::setConfiguration() {
25 ui->web_credentials_disclaimer->setWordWrap(true); 25 ui->web_credentials_disclaimer->setWordWrap(true);
26 ui->telemetry_learn_more->setOpenExternalLinks(true); 26 ui->telemetry_learn_more->setOpenExternalLinks(true);
27 ui->telemetry_learn_more->setText("<a " 27 ui->telemetry_learn_more->setText(tr("<a "
28 "href='https://citra-emu.org/entry/" 28 "href='https://citra-emu.org/entry/"
29 "telemetry-and-why-thats-a-good-thing/'>Learn more</a>"); 29 "telemetry-and-why-thats-a-good-thing/'>Learn more</a>"));
30 30
31 ui->web_signup_link->setOpenExternalLinks(true); 31 ui->web_signup_link->setOpenExternalLinks(true);
32 ui->web_signup_link->setText("<a href='https://services.citra-emu.org/'>Sign up</a>"); 32 ui->web_signup_link->setText(tr("<a href='https://services.citra-emu.org/'>Sign up</a>"));
33 ui->web_token_info_link->setOpenExternalLinks(true); 33 ui->web_token_info_link->setOpenExternalLinks(true);
34 ui->web_token_info_link->setText( 34 ui->web_token_info_link->setText(
35 "<a href='https://citra-emu.org/wiki/citra-web-service/'>What is my token?</a>"); 35 tr("<a href='https://citra-emu.org/wiki/citra-web-service/'>What is my token?</a>"));
36 36
37 ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry); 37 ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry);
38 ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username)); 38 ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username));
@@ -40,8 +40,8 @@ void ConfigureWeb::setConfiguration() {
40 // Connect after setting the values, to avoid calling OnLoginChanged now 40 // Connect after setting the values, to avoid calling OnLoginChanged now
41 connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged); 41 connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
42 connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged); 42 connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
43 ui->label_telemetry_id->setText("Telemetry ID: 0x" + 43 ui->label_telemetry_id->setText(
44 QString::number(Core::GetTelemetryId(), 16).toUpper()); 44 tr("Telemetry ID: 0x%1").arg(QString::number(Core::GetTelemetryId(), 16).toUpper()));
45 user_verified = true; 45 user_verified = true;
46} 46}
47 47
@@ -60,8 +60,8 @@ void ConfigureWeb::applyConfiguration() {
60 60
61void ConfigureWeb::RefreshTelemetryID() { 61void ConfigureWeb::RefreshTelemetryID() {
62 const u64 new_telemetry_id{Core::RegenerateTelemetryId()}; 62 const u64 new_telemetry_id{Core::RegenerateTelemetryId()};
63 ui->label_telemetry_id->setText("Telemetry ID: 0x" + 63 ui->label_telemetry_id->setText(
64 QString::number(new_telemetry_id, 16).toUpper()); 64 tr("Telemetry ID: 0x%1").arg(QString::number(new_telemetry_id, 16).toUpper()));
65} 65}
66 66
67void ConfigureWeb::OnLoginChanged() { 67void ConfigureWeb::OnLoginChanged() {
diff --git a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
index 7d06ec28a..ce2b9fa50 100644
--- a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
@@ -26,8 +26,8 @@
26namespace { 26namespace {
27QImage LoadTexture(const u8* src, const Pica::Texture::TextureInfo& info) { 27QImage LoadTexture(const u8* src, const Pica::Texture::TextureInfo& info) {
28 QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); 28 QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
29 for (int y = 0; y < info.height; ++y) { 29 for (u32 y = 0; y < info.height; ++y) {
30 for (int x = 0; x < info.width; ++x) { 30 for (u32 x = 0; x < info.width; ++x) {
31 Math::Vec4<u8> color = Pica::Texture::LookupTexture(src, x, y, info, true); 31 Math::Vec4<u8> color = Pica::Texture::LookupTexture(src, x, y, info, true);
32 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); 32 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
33 } 33 }
diff --git a/src/citra_qt/debugger/graphics/graphics_surface.cpp b/src/citra_qt/debugger/graphics/graphics_surface.cpp
index 47d9924e1..c974545ef 100644
--- a/src/citra_qt/debugger/graphics/graphics_surface.cpp
+++ b/src/citra_qt/debugger/graphics/graphics_surface.cpp
@@ -273,7 +273,8 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
273 surface_picker_x_control->setValue(x); 273 surface_picker_x_control->setValue(x);
274 surface_picker_y_control->setValue(y); 274 surface_picker_y_control->setValue(y);
275 275
276 if (x < 0 || x >= surface_width || y < 0 || y >= surface_height) { 276 if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 ||
277 y >= static_cast<int>(surface_height)) {
277 surface_info_label->setText(tr("Pixel out of bounds")); 278 surface_info_label->setText(tr("Pixel out of bounds"));
278 surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); 279 surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
279 return; 280 return;
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index bad311793..6959915fa 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -117,7 +117,7 @@ std::string StringFromFormat(const char* format, ...) {
117} 117}
118 118
119// For Debugging. Read out an u8 array. 119// For Debugging. Read out an u8 array.
120std::string ArrayToString(const u8* data, u32 size, int line_len, bool spaces) { 120std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces) {
121 std::ostringstream oss; 121 std::ostringstream oss;
122 oss << std::setfill('0') << std::hex; 122 oss << std::setfill('0') << std::hex;
123 123
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 075bf4ecb..259360aec 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -33,7 +33,7 @@ inline void CharArrayFromFormat(char (&out)[Count], const char* format, ...) {
33} 33}
34 34
35// Good 35// Good
36std::string ArrayToString(const u8* data, u32 size, int line_len = 20, bool spaces = true); 36std::string ArrayToString(const u8* data, size_t size, int line_len = 20, bool spaces = true);
37 37
38std::string StripSpaces(const std::string& s); 38std::string StripSpaces(const std::string& s);
39std::string StripQuotes(const std::string& s); 39std::string StripQuotes(const std::string& s);
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index 6e2a5ad60..3f0057d9e 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -31,6 +31,7 @@
31#pragma once 31#pragma once
32 32
33#include <cmath> 33#include <cmath>
34#include <type_traits>
34 35
35namespace Math { 36namespace Math {
36 37
@@ -90,7 +91,8 @@ public:
90 y -= other.y; 91 y -= other.y;
91 } 92 }
92 93
93 Vec2<decltype(-T{})> operator-() const { 94 template <typename U = T>
95 Vec2<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
94 return MakeVec(-x, -y); 96 return MakeVec(-x, -y);
95 } 97 }
96 Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const { 98 Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
@@ -102,8 +104,7 @@ public:
102 } 104 }
103 template <typename V> 105 template <typename V>
104 void operator*=(const V& f) { 106 void operator*=(const V& f) {
105 x *= f; 107 *this = *this * f;
106 y *= f;
107 } 108 }
108 template <typename V> 109 template <typename V>
109 Vec2<decltype(T{} / V{})> operator/(const V& f) const { 110 Vec2<decltype(T{} / V{})> operator/(const V& f) const {
@@ -247,7 +248,8 @@ public:
247 z -= other.z; 248 z -= other.z;
248 } 249 }
249 250
250 Vec3<decltype(-T{})> operator-() const { 251 template <typename U = T>
252 Vec3<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
251 return MakeVec(-x, -y, -z); 253 return MakeVec(-x, -y, -z);
252 } 254 }
253 Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const { 255 Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
@@ -259,9 +261,7 @@ public:
259 } 261 }
260 template <typename V> 262 template <typename V>
261 void operator*=(const V& f) { 263 void operator*=(const V& f) {
262 x *= f; 264 *this = *this * f;
263 y *= f;
264 z *= f;
265 } 265 }
266 template <typename V> 266 template <typename V>
267 Vec3<decltype(T{} / V{})> operator/(const V& f) const { 267 Vec3<decltype(T{} / V{})> operator/(const V& f) const {
@@ -462,7 +462,8 @@ public:
462 w -= other.w; 462 w -= other.w;
463 } 463 }
464 464
465 Vec4<decltype(-T{})> operator-() const { 465 template <typename U = T>
466 Vec4<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
466 return MakeVec(-x, -y, -z, -w); 467 return MakeVec(-x, -y, -z, -w);
467 } 468 }
468 Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const { 469 Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
@@ -474,10 +475,7 @@ public:
474 } 475 }
475 template <typename V> 476 template <typename V>
476 void operator*=(const V& f) { 477 void operator*=(const V& f) {
477 x *= f; 478 *this = *this * f;
478 y *= f;
479 z *= f;
480 w *= f;
481 } 479 }
482 template <typename V> 480 template <typename V>
483 Vec4<decltype(T{} / V{})> operator/(const V& f) const { 481 Vec4<decltype(T{} / V{})> operator/(const V& f) const {
@@ -720,4 +718,4 @@ static inline Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
720 return MakeVec(x, yzw[0], yzw[1], yzw[2]); 718 return MakeVec(x, yzw[0], yzw[1], yzw[2]);
721} 719}
722 720
723} // namespace 721} // namespace Math
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index cd1a8de2d..3ed619991 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -26,6 +26,7 @@ set(SRCS
26 file_sys/archive_systemsavedata.cpp 26 file_sys/archive_systemsavedata.cpp
27 file_sys/disk_archive.cpp 27 file_sys/disk_archive.cpp
28 file_sys/ivfc_archive.cpp 28 file_sys/ivfc_archive.cpp
29 file_sys/ncch_container.cpp
29 file_sys/path_parser.cpp 30 file_sys/path_parser.cpp
30 file_sys/savedata_archive.cpp 31 file_sys/savedata_archive.cpp
31 frontend/camera/blank_camera.cpp 32 frontend/camera/blank_camera.cpp
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index ccd43f431..ba528403c 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -41,6 +41,9 @@ public:
41 /// Clear all instruction cache 41 /// Clear all instruction cache
42 virtual void ClearInstructionCache() = 0; 42 virtual void ClearInstructionCache() = 0;
43 43
44 /// Notify CPU emulation that page tables have changed
45 virtual void PageTableChanged() = 0;
46
44 /** 47 /**
45 * Set the Program Counter to an address 48 * Set the Program Counter to an address
46 * @param addr Address to set PC to 49 * @param addr Address to set PC to
@@ -122,12 +125,6 @@ public:
122 virtual void SetCP15Register(CP15Register reg, u32 value) = 0; 125 virtual void SetCP15Register(CP15Register reg, u32 value) = 0;
123 126
124 /** 127 /**
125 * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
126 * @param ticks Number of ticks to advance the CPU core
127 */
128 virtual void AddTicks(u64 ticks) = 0;
129
130 /**
131 * Saves the current CPU context 128 * Saves the current CPU context
132 * @param ctx Thread context to save 129 * @param ctx Thread context to save
133 */ 130 */
@@ -147,9 +144,6 @@ public:
147 return num_instructions; 144 return num_instructions;
148 } 145 }
149 146
150 s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event,
151 /// decreased by the cpu run loop
152
153protected: 147protected:
154 /** 148 /**
155 * Executes the given number of instructions 149 * Executes the given number of instructions
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 34c5aa381..2cb56d12f 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -41,7 +41,7 @@ static bool IsReadOnlyMemory(u32 vaddr) {
41} 41}
42 42
43static Dynarmic::UserCallbacks GetUserCallbacks( 43static Dynarmic::UserCallbacks GetUserCallbacks(
44 const std::shared_ptr<ARMul_State>& interpeter_state) { 44 const std::shared_ptr<ARMul_State>& interpeter_state, Memory::PageTable* current_page_table) {
45 Dynarmic::UserCallbacks user_callbacks{}; 45 Dynarmic::UserCallbacks user_callbacks{};
46 user_callbacks.InterpreterFallback = &InterpreterFallback; 46 user_callbacks.InterpreterFallback = &InterpreterFallback;
47 user_callbacks.user_arg = static_cast<void*>(interpeter_state.get()); 47 user_callbacks.user_arg = static_cast<void*>(interpeter_state.get());
@@ -56,16 +56,14 @@ 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 // TODO(Subv): Re-add the page table pointers once dynarmic supports switching page tables at 59 user_callbacks.page_table = &current_page_table->pointers;
60 // runtime.
61 user_callbacks.page_table = nullptr;
62 user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state); 60 user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state);
63 return user_callbacks; 61 return user_callbacks;
64} 62}
65 63
66ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) { 64ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) {
67 interpreter_state = std::make_shared<ARMul_State>(initial_mode); 65 interpreter_state = std::make_shared<ARMul_State>(initial_mode);
68 jit = std::make_unique<Dynarmic::Jit>(GetUserCallbacks(interpreter_state)); 66 PageTableChanged();
69} 67}
70 68
71void ARM_Dynarmic::SetPC(u32 pc) { 69void ARM_Dynarmic::SetPC(u32 pc) {
@@ -126,21 +124,15 @@ void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) {
126 interpreter_state->CP15[reg] = value; 124 interpreter_state->CP15[reg] = value;
127} 125}
128 126
129void ARM_Dynarmic::AddTicks(u64 ticks) {
130 down_count -= ticks;
131 if (down_count < 0) {
132 CoreTiming::Advance();
133 }
134}
135
136MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); 127MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
137 128
138void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { 129void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
130 ASSERT(Memory::GetCurrentPageTable() == current_page_table);
139 MICROPROFILE_SCOPE(ARM_Jit); 131 MICROPROFILE_SCOPE(ARM_Jit);
140 132
141 std::size_t ticks_executed = jit->Run(static_cast<unsigned>(num_instructions)); 133 std::size_t ticks_executed = jit->Run(static_cast<unsigned>(num_instructions));
142 134
143 AddTicks(ticks_executed); 135 CoreTiming::AddTicks(ticks_executed);
144} 136}
145 137
146void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { 138void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
@@ -178,3 +170,16 @@ void ARM_Dynarmic::PrepareReschedule() {
178void ARM_Dynarmic::ClearInstructionCache() { 170void ARM_Dynarmic::ClearInstructionCache() {
179 jit->ClearCache(); 171 jit->ClearCache();
180} 172}
173
174void ARM_Dynarmic::PageTableChanged() {
175 current_page_table = Memory::GetCurrentPageTable();
176
177 auto iter = jits.find(current_page_table);
178 if (iter != jits.end()) {
179 jit = iter->second.get();
180 return;
181 }
182
183 jit = new Dynarmic::Jit(GetUserCallbacks(interpreter_state, current_page_table));
184 jits.emplace(current_page_table, std::unique_ptr<Dynarmic::Jit>(jit));
185}
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 834dc989e..0b00158a5 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -4,12 +4,17 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <map>
7#include <memory> 8#include <memory>
8#include <dynarmic/dynarmic.h> 9#include <dynarmic/dynarmic.h>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
11#include "core/arm/skyeye_common/armstate.h" 12#include "core/arm/skyeye_common/armstate.h"
12 13
14namespace Memory {
15struct PageTable;
16} // namespace Memory
17
13class ARM_Dynarmic final : public ARM_Interface { 18class ARM_Dynarmic final : public ARM_Interface {
14public: 19public:
15 ARM_Dynarmic(PrivilegeMode initial_mode); 20 ARM_Dynarmic(PrivilegeMode initial_mode);
@@ -27,8 +32,6 @@ public:
27 u32 GetCP15Register(CP15Register reg) override; 32 u32 GetCP15Register(CP15Register reg) override;
28 void SetCP15Register(CP15Register reg, u32 value) override; 33 void SetCP15Register(CP15Register reg, u32 value) override;
29 34
30 void AddTicks(u64 ticks) override;
31
32 void SaveContext(ThreadContext& ctx) override; 35 void SaveContext(ThreadContext& ctx) override;
33 void LoadContext(const ThreadContext& ctx) override; 36 void LoadContext(const ThreadContext& ctx) override;
34 37
@@ -36,8 +39,11 @@ public:
36 void ExecuteInstructions(int num_instructions) override; 39 void ExecuteInstructions(int num_instructions) override;
37 40
38 void ClearInstructionCache() override; 41 void ClearInstructionCache() override;
42 void PageTableChanged() override;
39 43
40private: 44private:
41 std::unique_ptr<Dynarmic::Jit> jit; 45 Dynarmic::Jit* jit = nullptr;
46 Memory::PageTable* current_page_table = nullptr;
47 std::map<Memory::PageTable*, std::unique_ptr<Dynarmic::Jit>> jits;
42 std::shared_ptr<ARMul_State> interpreter_state; 48 std::shared_ptr<ARMul_State> interpreter_state;
43}; 49};
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index 81f9bf99e..4d72aef77 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -25,6 +25,10 @@ void ARM_DynCom::ClearInstructionCache() {
25 trans_cache_buf_top = 0; 25 trans_cache_buf_top = 0;
26} 26}
27 27
28void ARM_DynCom::PageTableChanged() {
29 ClearInstructionCache();
30}
31
28void ARM_DynCom::SetPC(u32 pc) { 32void ARM_DynCom::SetPC(u32 pc) {
29 state->Reg[15] = pc; 33 state->Reg[15] = pc;
30} 34}
@@ -73,12 +77,6 @@ void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) {
73 state->CP15[reg] = value; 77 state->CP15[reg] = value;
74} 78}
75 79
76void ARM_DynCom::AddTicks(u64 ticks) {
77 down_count -= ticks;
78 if (down_count < 0)
79 CoreTiming::Advance();
80}
81
82void ARM_DynCom::ExecuteInstructions(int num_instructions) { 80void ARM_DynCom::ExecuteInstructions(int num_instructions) {
83 state->NumInstrsToExecute = num_instructions; 81 state->NumInstrsToExecute = num_instructions;
84 82
@@ -86,7 +84,7 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) {
86 // executing one instruction at a time. Otherwise, if a block is being executed, more 84 // executing one instruction at a time. Otherwise, if a block is being executed, more
87 // instructions may actually be executed than specified. 85 // instructions may actually be executed than specified.
88 unsigned ticks_executed = InterpreterMainLoop(state.get()); 86 unsigned ticks_executed = InterpreterMainLoop(state.get());
89 AddTicks(ticks_executed); 87 CoreTiming::AddTicks(ticks_executed);
90} 88}
91 89
92void ARM_DynCom::SaveContext(ThreadContext& ctx) { 90void ARM_DynCom::SaveContext(ThreadContext& ctx) {
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 62c174f3c..fc1ffed6a 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -16,6 +16,7 @@ public:
16 ~ARM_DynCom(); 16 ~ARM_DynCom();
17 17
18 void ClearInstructionCache() override; 18 void ClearInstructionCache() override;
19 void PageTableChanged() override;
19 20
20 void SetPC(u32 pc) override; 21 void SetPC(u32 pc) override;
21 u32 GetPC() const override; 22 u32 GetPC() const override;
@@ -30,8 +31,6 @@ public:
30 u32 GetCP15Register(CP15Register reg) override; 31 u32 GetCP15Register(CP15Register reg) override;
31 void SetCP15Register(CP15Register reg, u32 value) override; 32 void SetCP15Register(CP15Register reg, u32 value) override;
32 33
33 void AddTicks(u64 ticks) override;
34
35 void SaveContext(ThreadContext& ctx) override; 34 void SaveContext(ThreadContext& ctx) override;
36 void LoadContext(const ThreadContext& ctx) override; 35 void LoadContext(const ThreadContext& ctx) override;
37 36
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 59b8768e7..0c7a72987 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -13,6 +13,7 @@
13#include "core/core_timing.h" 13#include "core/core_timing.h"
14#include "core/gdbstub/gdbstub.h" 14#include "core/gdbstub/gdbstub.h"
15#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/process.h"
16#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
17#include "core/hle/service/service.h" 18#include "core/hle/service/service.h"
18#include "core/hw/hw.h" 19#include "core/hw/hw.h"
@@ -100,7 +101,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
100 return init_result; 101 return init_result;
101 } 102 }
102 103
103 const Loader::ResultStatus load_result{app_loader->Load()}; 104 const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)};
104 if (Loader::ResultStatus::Success != load_result) { 105 if (Loader::ResultStatus::Success != load_result) {
105 LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result); 106 LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result);
106 System::Shutdown(); 107 System::Shutdown();
@@ -114,6 +115,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
114 return ResultStatus::ErrorLoader; 115 return ResultStatus::ErrorLoader;
115 } 116 }
116 } 117 }
118 Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
117 status = ResultStatus::Success; 119 status = ResultStatus::Success;
118 return status; 120 return status;
119} 121}
@@ -196,4 +198,4 @@ void System::Shutdown() {
196 LOG_DEBUG(Core, "Shutdown OK"); 198 LOG_DEBUG(Core, "Shutdown OK");
197} 199}
198 200
199} // namespace 201} // namespace Core
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 276ecfdf6..5e2a5d00f 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -57,6 +57,9 @@ static s64 idled_cycles;
57static s64 last_global_time_ticks; 57static s64 last_global_time_ticks;
58static s64 last_global_time_us; 58static s64 last_global_time_us;
59 59
60static s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event,
61 /// decreased by the cpu run loop
62
60static std::recursive_mutex external_event_section; 63static std::recursive_mutex external_event_section;
61 64
62// Warning: not included in save state. 65// Warning: not included in save state.
@@ -146,7 +149,7 @@ void UnregisterAllEvents() {
146} 149}
147 150
148void Init() { 151void Init() {
149 Core::CPU().down_count = INITIAL_SLICE_LENGTH; 152 down_count = INITIAL_SLICE_LENGTH;
150 g_slice_length = INITIAL_SLICE_LENGTH; 153 g_slice_length = INITIAL_SLICE_LENGTH;
151 global_timer = 0; 154 global_timer = 0;
152 idled_cycles = 0; 155 idled_cycles = 0;
@@ -185,8 +188,15 @@ void Shutdown() {
185 } 188 }
186} 189}
187 190
191void AddTicks(u64 ticks) {
192 down_count -= ticks;
193 if (down_count < 0) {
194 Advance();
195 }
196}
197
188u64 GetTicks() { 198u64 GetTicks() {
189 return (u64)global_timer + g_slice_length - Core::CPU().down_count; 199 return (u64)global_timer + g_slice_length - down_count;
190} 200}
191 201
192u64 GetIdleTicks() { 202u64 GetIdleTicks() {
@@ -460,18 +470,18 @@ void MoveEvents() {
460} 470}
461 471
462void ForceCheck() { 472void ForceCheck() {
463 s64 cycles_executed = g_slice_length - Core::CPU().down_count; 473 s64 cycles_executed = g_slice_length - down_count;
464 global_timer += cycles_executed; 474 global_timer += cycles_executed;
465 // This will cause us to check for new events immediately. 475 // This will cause us to check for new events immediately.
466 Core::CPU().down_count = 0; 476 down_count = 0;
467 // But let's not eat a bunch more time in Advance() because of this. 477 // But let's not eat a bunch more time in Advance() because of this.
468 g_slice_length = 0; 478 g_slice_length = 0;
469} 479}
470 480
471void Advance() { 481void Advance() {
472 s64 cycles_executed = g_slice_length - Core::CPU().down_count; 482 s64 cycles_executed = g_slice_length - down_count;
473 global_timer += cycles_executed; 483 global_timer += cycles_executed;
474 Core::CPU().down_count = g_slice_length; 484 down_count = g_slice_length;
475 485
476 if (has_ts_events) 486 if (has_ts_events)
477 MoveEvents(); 487 MoveEvents();
@@ -480,7 +490,7 @@ void Advance() {
480 if (!first) { 490 if (!first) {
481 if (g_slice_length < 10000) { 491 if (g_slice_length < 10000) {
482 g_slice_length += 10000; 492 g_slice_length += 10000;
483 Core::CPU().down_count += g_slice_length; 493 down_count += g_slice_length;
484 } 494 }
485 } else { 495 } else {
486 // Note that events can eat cycles as well. 496 // Note that events can eat cycles as well.
@@ -490,7 +500,7 @@ void Advance() {
490 500
491 const int diff = target - g_slice_length; 501 const int diff = target - g_slice_length;
492 g_slice_length += diff; 502 g_slice_length += diff;
493 Core::CPU().down_count += diff; 503 down_count += diff;
494 } 504 }
495 if (advance_callback) 505 if (advance_callback)
496 advance_callback(static_cast<int>(cycles_executed)); 506 advance_callback(static_cast<int>(cycles_executed));
@@ -506,12 +516,12 @@ void LogPendingEvents() {
506} 516}
507 517
508void Idle(int max_idle) { 518void Idle(int max_idle) {
509 s64 cycles_down = Core::CPU().down_count; 519 s64 cycles_down = down_count;
510 if (max_idle != 0 && cycles_down > max_idle) 520 if (max_idle != 0 && cycles_down > max_idle)
511 cycles_down = max_idle; 521 cycles_down = max_idle;
512 522
513 if (first && cycles_down > 0) { 523 if (first && cycles_down > 0) {
514 s64 cycles_executed = g_slice_length - Core::CPU().down_count; 524 s64 cycles_executed = g_slice_length - down_count;
515 s64 cycles_next_event = first->time - global_timer; 525 s64 cycles_next_event = first->time - global_timer;
516 526
517 if (cycles_next_event < cycles_executed + cycles_down) { 527 if (cycles_next_event < cycles_executed + cycles_down) {
@@ -526,9 +536,9 @@ void Idle(int max_idle) {
526 cycles_down / (float)(g_clock_rate_arm11 * 0.001f)); 536 cycles_down / (float)(g_clock_rate_arm11 * 0.001f));
527 537
528 idled_cycles += cycles_down; 538 idled_cycles += cycles_down;
529 Core::CPU().down_count -= cycles_down; 539 down_count -= cycles_down;
530 if (Core::CPU().down_count == 0) 540 if (down_count == 0)
531 Core::CPU().down_count = -1; 541 down_count = -1;
532} 542}
533 543
534std::string GetScheduledEventsSummary() { 544std::string GetScheduledEventsSummary() {
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index d2f85cd4d..897350801 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -67,6 +67,12 @@ void Shutdown();
67typedef void (*MHzChangeCallback)(); 67typedef void (*MHzChangeCallback)();
68typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; 68typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
69 69
70/**
71* Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
72* @param ticks Number of ticks to advance the CPU core
73*/
74void AddTicks(u64 ticks);
75
70u64 GetTicks(); 76u64 GetTicks();
71u64 GetIdleTicks(); 77u64 GetIdleTicks();
72u64 GetGlobalTimeUs(); 78u64 GetGlobalTimeUs();
diff --git a/src/core/file_sys/archive_selfncch.cpp b/src/core/file_sys/archive_selfncch.cpp
index 298a37a44..a16941c70 100644
--- a/src/core/file_sys/archive_selfncch.cpp
+++ b/src/core/file_sys/archive_selfncch.cpp
@@ -3,12 +3,14 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array> 5#include <array>
6#include <cinttypes>
6#include "common/common_types.h" 7#include "common/common_types.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
8#include "common/swap.h" 9#include "common/swap.h"
9#include "core/file_sys/archive_selfncch.h" 10#include "core/file_sys/archive_selfncch.h"
10#include "core/file_sys/errors.h" 11#include "core/file_sys/errors.h"
11#include "core/file_sys/ivfc_archive.h" 12#include "core/file_sys/ivfc_archive.h"
13#include "core/hle/kernel/process.h"
12 14
13//////////////////////////////////////////////////////////////////////////////////////////////////// 15////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace 16// FileSys namespace
@@ -102,8 +104,7 @@ public:
102 104
103 switch (static_cast<SelfNCCHFilePathType>(file_path.type)) { 105 switch (static_cast<SelfNCCHFilePathType>(file_path.type)) {
104 case SelfNCCHFilePathType::UpdateRomFS: 106 case SelfNCCHFilePathType::UpdateRomFS:
105 LOG_WARNING(Service_FS, "(STUBBED) open update RomFS"); 107 return OpenUpdateRomFS();
106 return OpenRomFS();
107 108
108 case SelfNCCHFilePathType::RomFS: 109 case SelfNCCHFilePathType::RomFS:
109 return OpenRomFS(); 110 return OpenRomFS();
@@ -179,6 +180,17 @@ private:
179 } 180 }
180 } 181 }
181 182
183 ResultVal<std::unique_ptr<FileBackend>> OpenUpdateRomFS() const {
184 if (ncch_data.update_romfs_file) {
185 return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(
186 ncch_data.update_romfs_file, ncch_data.update_romfs_offset,
187 ncch_data.update_romfs_size));
188 } else {
189 LOG_INFO(Service_FS, "Unable to read update RomFS");
190 return ERROR_ROMFS_NOT_FOUND;
191 }
192 }
193
182 ResultVal<std::unique_ptr<FileBackend>> OpenExeFS(const std::string& filename) const { 194 ResultVal<std::unique_ptr<FileBackend>> OpenExeFS(const std::string& filename) const {
183 if (filename == "icon") { 195 if (filename == "icon") {
184 if (ncch_data.icon) { 196 if (ncch_data.icon) {
@@ -217,30 +229,57 @@ private:
217 NCCHData ncch_data; 229 NCCHData ncch_data;
218}; 230};
219 231
220ArchiveFactory_SelfNCCH::ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader) { 232void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) {
233 u64 program_id = 0;
234 if (app_loader.ReadProgramId(program_id) != Loader::ResultStatus::Success) {
235 LOG_WARNING(
236 Service_FS,
237 "Could not read program id when registering with SelfNCCH, this might be a 3dsx file");
238 }
239
240 LOG_DEBUG(Service_FS, "Registering program %016" PRIX64 " with the SelfNCCH archive factory",
241 program_id);
242
243 if (ncch_data.find(program_id) != ncch_data.end()) {
244 LOG_WARNING(Service_FS, "Registering program %016" PRIX64
245 " with SelfNCCH will override existing mapping",
246 program_id);
247 }
248
249 NCCHData& data = ncch_data[program_id];
250
221 std::shared_ptr<FileUtil::IOFile> romfs_file_; 251 std::shared_ptr<FileUtil::IOFile> romfs_file_;
222 if (Loader::ResultStatus::Success == 252 if (Loader::ResultStatus::Success ==
223 app_loader.ReadRomFS(romfs_file_, ncch_data.romfs_offset, ncch_data.romfs_size)) { 253 app_loader.ReadRomFS(romfs_file_, data.romfs_offset, data.romfs_size)) {
254
255 data.romfs_file = std::move(romfs_file_);
256 }
257
258 std::shared_ptr<FileUtil::IOFile> update_romfs_file;
259 if (Loader::ResultStatus::Success ==
260 app_loader.ReadUpdateRomFS(update_romfs_file, data.update_romfs_offset,
261 data.update_romfs_size)) {
224 262
225 ncch_data.romfs_file = std::move(romfs_file_); 263 data.update_romfs_file = std::move(update_romfs_file);
226 } 264 }
227 265
228 std::vector<u8> buffer; 266 std::vector<u8> buffer;
229 267
230 if (Loader::ResultStatus::Success == app_loader.ReadIcon(buffer)) 268 if (Loader::ResultStatus::Success == app_loader.ReadIcon(buffer))
231 ncch_data.icon = std::make_shared<std::vector<u8>>(std::move(buffer)); 269 data.icon = std::make_shared<std::vector<u8>>(std::move(buffer));
232 270
233 buffer.clear(); 271 buffer.clear();
234 if (Loader::ResultStatus::Success == app_loader.ReadLogo(buffer)) 272 if (Loader::ResultStatus::Success == app_loader.ReadLogo(buffer))
235 ncch_data.logo = std::make_shared<std::vector<u8>>(std::move(buffer)); 273 data.logo = std::make_shared<std::vector<u8>>(std::move(buffer));
236 274
237 buffer.clear(); 275 buffer.clear();
238 if (Loader::ResultStatus::Success == app_loader.ReadBanner(buffer)) 276 if (Loader::ResultStatus::Success == app_loader.ReadBanner(buffer))
239 ncch_data.banner = std::make_shared<std::vector<u8>>(std::move(buffer)); 277 data.banner = std::make_shared<std::vector<u8>>(std::move(buffer));
240} 278}
241 279
242ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) { 280ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) {
243 auto archive = std::make_unique<SelfNCCHArchive>(ncch_data); 281 auto archive = std::make_unique<SelfNCCHArchive>(
282 ncch_data[Kernel::g_current_process->codeset->program_id]);
244 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 283 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
245} 284}
246 285
diff --git a/src/core/file_sys/archive_selfncch.h b/src/core/file_sys/archive_selfncch.h
index f1b971296..0d6d6766e 100644
--- a/src/core/file_sys/archive_selfncch.h
+++ b/src/core/file_sys/archive_selfncch.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <unordered_map>
9#include <vector> 10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "core/file_sys/archive_backend.h" 12#include "core/file_sys/archive_backend.h"
@@ -24,12 +25,19 @@ struct NCCHData {
24 std::shared_ptr<FileUtil::IOFile> romfs_file; 25 std::shared_ptr<FileUtil::IOFile> romfs_file;
25 u64 romfs_offset = 0; 26 u64 romfs_offset = 0;
26 u64 romfs_size = 0; 27 u64 romfs_size = 0;
28
29 std::shared_ptr<FileUtil::IOFile> update_romfs_file;
30 u64 update_romfs_offset = 0;
31 u64 update_romfs_size = 0;
27}; 32};
28 33
29/// File system interface to the SelfNCCH archive 34/// File system interface to the SelfNCCH archive
30class ArchiveFactory_SelfNCCH final : public ArchiveFactory { 35class ArchiveFactory_SelfNCCH final : public ArchiveFactory {
31public: 36public:
32 explicit ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader); 37 ArchiveFactory_SelfNCCH() = default;
38
39 /// Registers a loaded application so that we can open its SelfNCCH archive when requested.
40 void Register(Loader::AppLoader& app_loader);
33 41
34 std::string GetName() const override { 42 std::string GetName() const override {
35 return "SelfNCCH"; 43 return "SelfNCCH";
@@ -39,7 +47,8 @@ public:
39 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; 47 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
40 48
41private: 49private:
42 NCCHData ncch_data; 50 /// Mapping of ProgramId -> NCCHData
51 std::unordered_map<u64, NCCHData> ncch_data;
43}; 52};
44 53
45} // namespace FileSys 54} // namespace FileSys
diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp
new file mode 100644
index 000000000..59c72f3e9
--- /dev/null
+++ b/src/core/file_sys/ncch_container.cpp
@@ -0,0 +1,316 @@
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 <cinttypes>
6#include <cstring>
7#include <memory>
8#include "common/common_types.h"
9#include "common/logging/log.h"
10#include "core/core.h"
11#include "core/file_sys/ncch_container.h"
12#include "core/loader/loader.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// FileSys namespace
16
17namespace FileSys {
18
19static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs
20static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
21
22/**
23 * Get the decompressed size of an LZSS compressed ExeFS file
24 * @param buffer Buffer of compressed file
25 * @param size Size of compressed buffer
26 * @return Size of decompressed buffer
27 */
28static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) {
29 u32 offset_size = *(u32*)(buffer + size - 4);
30 return offset_size + size;
31}
32
33/**
34 * Decompress ExeFS file (compressed with LZSS)
35 * @param compressed Compressed buffer
36 * @param compressed_size Size of compressed buffer
37 * @param decompressed Decompressed buffer
38 * @param decompressed_size Size of decompressed buffer
39 * @return True on success, otherwise false
40 */
41static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed,
42 u32 decompressed_size) {
43 const u8* footer = compressed + compressed_size - 8;
44 u32 buffer_top_and_bottom = *reinterpret_cast<const u32*>(footer);
45 u32 out = decompressed_size;
46 u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF);
47 u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF);
48
49 memset(decompressed, 0, decompressed_size);
50 memcpy(decompressed, compressed, compressed_size);
51
52 while (index > stop_index) {
53 u8 control = compressed[--index];
54
55 for (unsigned i = 0; i < 8; i++) {
56 if (index <= stop_index)
57 break;
58 if (index <= 0)
59 break;
60 if (out <= 0)
61 break;
62
63 if (control & 0x80) {
64 // Check if compression is out of bounds
65 if (index < 2)
66 return false;
67 index -= 2;
68
69 u32 segment_offset = compressed[index] | (compressed[index + 1] << 8);
70 u32 segment_size = ((segment_offset >> 12) & 15) + 3;
71 segment_offset &= 0x0FFF;
72 segment_offset += 2;
73
74 // Check if compression is out of bounds
75 if (out < segment_size)
76 return false;
77
78 for (unsigned j = 0; j < segment_size; j++) {
79 // Check if compression is out of bounds
80 if (out + segment_offset >= decompressed_size)
81 return false;
82
83 u8 data = decompressed[out + segment_offset];
84 decompressed[--out] = data;
85 }
86 } else {
87 // Check if compression is out of bounds
88 if (out < 1)
89 return false;
90 decompressed[--out] = compressed[--index];
91 }
92 control <<= 1;
93 }
94 }
95 return true;
96}
97
98NCCHContainer::NCCHContainer(const std::string& filepath) : filepath(filepath) {
99 file = FileUtil::IOFile(filepath, "rb");
100}
101
102Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath) {
103 this->filepath = filepath;
104 file = FileUtil::IOFile(filepath, "rb");
105
106 if (!file.IsOpen()) {
107 LOG_WARNING(Service_FS, "Failed to open %s", filepath.c_str());
108 return Loader::ResultStatus::Error;
109 }
110
111 LOG_DEBUG(Service_FS, "Opened %s", filepath.c_str());
112 return Loader::ResultStatus::Success;
113}
114
115Loader::ResultStatus NCCHContainer::Load() {
116 if (is_loaded)
117 return Loader::ResultStatus::Success;
118
119 // Reset read pointer in case this file has been read before.
120 file.Seek(0, SEEK_SET);
121
122 if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header))
123 return Loader::ResultStatus::Error;
124
125 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
126 if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
127 LOG_DEBUG(Service_FS, "Only loading the first (bootable) NCCH within the NCSD file!");
128 ncch_offset = 0x4000;
129 file.Seek(ncch_offset, SEEK_SET);
130 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
131 }
132
133 // Verify we are loading the correct file type...
134 if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
135 return Loader::ResultStatus::ErrorInvalidFormat;
136
137 // System archives and DLC don't have an extended header but have RomFS
138 if (ncch_header.extended_header_size) {
139 if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header))
140 return Loader::ResultStatus::Error;
141
142 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
143 u32 entry_point = exheader_header.codeset_info.text.address;
144 u32 code_size = exheader_header.codeset_info.text.code_size;
145 u32 stack_size = exheader_header.codeset_info.stack_size;
146 u32 bss_size = exheader_header.codeset_info.bss_size;
147 u32 core_version = exheader_header.arm11_system_local_caps.core_version;
148 u8 priority = exheader_header.arm11_system_local_caps.priority;
149 u8 resource_limit_category =
150 exheader_header.arm11_system_local_caps.resource_limit_category;
151
152 LOG_DEBUG(Service_FS, "Name: %s", exheader_header.codeset_info.name);
153 LOG_DEBUG(Service_FS, "Program ID: %016" PRIX64, ncch_header.program_id);
154 LOG_DEBUG(Service_FS, "Code compressed: %s", is_compressed ? "yes" : "no");
155 LOG_DEBUG(Service_FS, "Entry point: 0x%08X", entry_point);
156 LOG_DEBUG(Service_FS, "Code size: 0x%08X", code_size);
157 LOG_DEBUG(Service_FS, "Stack size: 0x%08X", stack_size);
158 LOG_DEBUG(Service_FS, "Bss size: 0x%08X", bss_size);
159 LOG_DEBUG(Service_FS, "Core version: %d", core_version);
160 LOG_DEBUG(Service_FS, "Thread priority: 0x%X", priority);
161 LOG_DEBUG(Service_FS, "Resource limit category: %d", resource_limit_category);
162 LOG_DEBUG(Service_FS, "System Mode: %d",
163 static_cast<int>(exheader_header.arm11_system_local_caps.system_mode));
164
165 if (exheader_header.system_info.jump_id != ncch_header.program_id) {
166 LOG_ERROR(Service_FS, "ExHeader Program ID mismatch: the ROM is probably encrypted.");
167 return Loader::ResultStatus::ErrorEncrypted;
168 }
169
170 has_exheader = true;
171 }
172
173 // DLC can have an ExeFS and a RomFS but no extended header
174 if (ncch_header.exefs_size) {
175 exefs_offset = ncch_header.exefs_offset * kBlockSize;
176 u32 exefs_size = ncch_header.exefs_size * kBlockSize;
177
178 LOG_DEBUG(Service_FS, "ExeFS offset: 0x%08X", exefs_offset);
179 LOG_DEBUG(Service_FS, "ExeFS size: 0x%08X", exefs_size);
180
181 file.Seek(exefs_offset + ncch_offset, SEEK_SET);
182 if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
183 return Loader::ResultStatus::Error;
184
185 has_exefs = true;
186 }
187
188 if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0)
189 has_romfs = true;
190
191 is_loaded = true;
192 return Loader::ResultStatus::Success;
193}
194
195Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) {
196 if (!file.IsOpen())
197 return Loader::ResultStatus::Error;
198
199 Loader::ResultStatus result = Load();
200 if (result != Loader::ResultStatus::Success)
201 return result;
202
203 if (!has_exefs)
204 return Loader::ResultStatus::ErrorNotUsed;
205
206 LOG_DEBUG(Service_FS, "%d sections:", kMaxSections);
207 // Iterate through the ExeFs archive until we find a section with the specified name...
208 for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
209 const auto& section = exefs_header.section[section_number];
210
211 // Load the specified section...
212 if (strcmp(section.name, name) == 0) {
213 LOG_DEBUG(Service_FS, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number,
214 section.offset, section.size, section.name);
215
216 s64 section_offset =
217 (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
218 file.Seek(section_offset, SEEK_SET);
219
220 if (strcmp(section.name, ".code") == 0 && is_compressed) {
221 // Section is compressed, read compressed .code section...
222 std::unique_ptr<u8[]> temp_buffer;
223 try {
224 temp_buffer.reset(new u8[section.size]);
225 } catch (std::bad_alloc&) {
226 return Loader::ResultStatus::ErrorMemoryAllocationFailed;
227 }
228
229 if (file.ReadBytes(&temp_buffer[0], section.size) != section.size)
230 return Loader::ResultStatus::Error;
231
232 // Decompress .code section...
233 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size);
234 buffer.resize(decompressed_size);
235 if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size))
236 return Loader::ResultStatus::ErrorInvalidFormat;
237 } else {
238 // Section is uncompressed...
239 buffer.resize(section.size);
240 if (file.ReadBytes(&buffer[0], section.size) != section.size)
241 return Loader::ResultStatus::Error;
242 }
243 return Loader::ResultStatus::Success;
244 }
245 }
246 return Loader::ResultStatus::ErrorNotUsed;
247}
248
249Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file,
250 u64& offset, u64& size) {
251 if (!file.IsOpen())
252 return Loader::ResultStatus::Error;
253
254 Loader::ResultStatus result = Load();
255 if (result != Loader::ResultStatus::Success)
256 return result;
257
258 if (!has_romfs) {
259 LOG_DEBUG(Service_FS, "RomFS requested from NCCH which has no RomFS");
260 return Loader::ResultStatus::ErrorNotUsed;
261 }
262
263 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
264 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
265
266 LOG_DEBUG(Service_FS, "RomFS offset: 0x%08X", romfs_offset);
267 LOG_DEBUG(Service_FS, "RomFS size: 0x%08X", romfs_size);
268
269 if (file.GetSize() < romfs_offset + romfs_size)
270 return Loader::ResultStatus::Error;
271
272 // We reopen the file, to allow its position to be independent from file's
273 romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb");
274 if (!romfs_file->IsOpen())
275 return Loader::ResultStatus::Error;
276
277 offset = romfs_offset;
278 size = romfs_size;
279
280 return Loader::ResultStatus::Success;
281}
282
283Loader::ResultStatus NCCHContainer::ReadProgramId(u64_le& program_id) {
284 Loader::ResultStatus result = Load();
285 if (result != Loader::ResultStatus::Success)
286 return result;
287
288 program_id = ncch_header.program_id;
289 return Loader::ResultStatus::Success;
290}
291
292bool NCCHContainer::HasExeFS() {
293 Loader::ResultStatus result = Load();
294 if (result != Loader::ResultStatus::Success)
295 return false;
296
297 return has_exefs;
298}
299
300bool NCCHContainer::HasRomFS() {
301 Loader::ResultStatus result = Load();
302 if (result != Loader::ResultStatus::Success)
303 return false;
304
305 return has_romfs;
306}
307
308bool NCCHContainer::HasExHeader() {
309 Loader::ResultStatus result = Load();
310 if (result != Loader::ResultStatus::Success)
311 return false;
312
313 return has_exheader;
314}
315
316} // namespace FileSys
diff --git a/src/core/file_sys/ncch_container.h b/src/core/file_sys/ncch_container.h
new file mode 100644
index 000000000..8af9032b4
--- /dev/null
+++ b/src/core/file_sys/ncch_container.h
@@ -0,0 +1,244 @@
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 <cstddef>
8#include <memory>
9#include <string>
10#include <vector>
11#include "common/bit_field.h"
12#include "common/common_types.h"
13#include "common/file_util.h"
14#include "common/swap.h"
15#include "core/core.h"
16
17////////////////////////////////////////////////////////////////////////////////////////////////////
18/// NCCH header (Note: "NCCH" appears to be a publicly unknown acronym)
19
20struct NCCH_Header {
21 u8 signature[0x100];
22 u32_le magic;
23 u32_le content_size;
24 u8 partition_id[8];
25 u16_le maker_code;
26 u16_le version;
27 u8 reserved_0[4];
28 u64_le program_id;
29 u8 reserved_1[0x10];
30 u8 logo_region_hash[0x20];
31 u8 product_code[0x10];
32 u8 extended_header_hash[0x20];
33 u32_le extended_header_size;
34 u8 reserved_2[4];
35 u8 flags[8];
36 u32_le plain_region_offset;
37 u32_le plain_region_size;
38 u32_le logo_region_offset;
39 u32_le logo_region_size;
40 u32_le exefs_offset;
41 u32_le exefs_size;
42 u32_le exefs_hash_region_size;
43 u8 reserved_3[4];
44 u32_le romfs_offset;
45 u32_le romfs_size;
46 u32_le romfs_hash_region_size;
47 u8 reserved_4[4];
48 u8 exefs_super_block_hash[0x20];
49 u8 romfs_super_block_hash[0x20];
50};
51
52static_assert(sizeof(NCCH_Header) == 0x200, "NCCH header structure size is wrong");
53
54////////////////////////////////////////////////////////////////////////////////////////////////////
55// ExeFS (executable file system) headers
56
57struct ExeFs_SectionHeader {
58 char name[8];
59 u32 offset;
60 u32 size;
61};
62
63struct ExeFs_Header {
64 ExeFs_SectionHeader section[8];
65 u8 reserved[0x80];
66 u8 hashes[8][0x20];
67};
68
69////////////////////////////////////////////////////////////////////////////////////////////////////
70// ExHeader (executable file system header) headers
71
72struct ExHeader_SystemInfoFlags {
73 u8 reserved[5];
74 u8 flag;
75 u8 remaster_version[2];
76};
77
78struct ExHeader_CodeSegmentInfo {
79 u32 address;
80 u32 num_max_pages;
81 u32 code_size;
82};
83
84struct ExHeader_CodeSetInfo {
85 u8 name[8];
86 ExHeader_SystemInfoFlags flags;
87 ExHeader_CodeSegmentInfo text;
88 u32 stack_size;
89 ExHeader_CodeSegmentInfo ro;
90 u8 reserved[4];
91 ExHeader_CodeSegmentInfo data;
92 u32 bss_size;
93};
94
95struct ExHeader_DependencyList {
96 u8 program_id[0x30][8];
97};
98
99struct ExHeader_SystemInfo {
100 u64 save_data_size;
101 u64_le jump_id;
102 u8 reserved_2[0x30];
103};
104
105struct ExHeader_StorageInfo {
106 u8 ext_save_data_id[8];
107 u8 system_save_data_id[8];
108 u8 reserved[8];
109 u8 access_info[7];
110 u8 other_attributes;
111};
112
113struct ExHeader_ARM11_SystemLocalCaps {
114 u64_le program_id;
115 u32_le core_version;
116 u8 reserved_flags[2];
117 union {
118 u8 flags0;
119 BitField<0, 2, u8> ideal_processor;
120 BitField<2, 2, u8> affinity_mask;
121 BitField<4, 4, u8> system_mode;
122 };
123 u8 priority;
124 u8 resource_limit_descriptor[0x10][2];
125 ExHeader_StorageInfo storage_info;
126 u8 service_access_control[0x20][8];
127 u8 ex_service_access_control[0x2][8];
128 u8 reserved[0xf];
129 u8 resource_limit_category;
130};
131
132struct ExHeader_ARM11_KernelCaps {
133 u32_le descriptors[28];
134 u8 reserved[0x10];
135};
136
137struct ExHeader_ARM9_AccessControl {
138 u8 descriptors[15];
139 u8 descversion;
140};
141
142struct ExHeader_Header {
143 ExHeader_CodeSetInfo codeset_info;
144 ExHeader_DependencyList dependency_list;
145 ExHeader_SystemInfo system_info;
146 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
147 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
148 ExHeader_ARM9_AccessControl arm9_access_control;
149 struct {
150 u8 signature[0x100];
151 u8 ncch_public_key_modulus[0x100];
152 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
153 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
154 ExHeader_ARM9_AccessControl arm9_access_control;
155 } access_desc;
156};
157
158static_assert(sizeof(ExHeader_Header) == 0x800, "ExHeader structure size is wrong");
159
160////////////////////////////////////////////////////////////////////////////////////////////////////
161// FileSys namespace
162
163namespace FileSys {
164
165/**
166 * Helper which implements an interface to deal with NCCH containers which can
167 * contain ExeFS archives or RomFS archives for games or other applications.
168 */
169class NCCHContainer {
170public:
171 NCCHContainer(const std::string& filepath);
172 NCCHContainer() {}
173
174 Loader::ResultStatus OpenFile(const std::string& filepath);
175
176 /**
177 * Ensure ExeFS and exheader is loaded and ready for reading sections
178 * @return ResultStatus result of function
179 */
180 Loader::ResultStatus Load();
181
182 /**
183 * Reads an application ExeFS section of an NCCH file (e.g. .code, .logo, etc.)
184 * @param name Name of section to read out of NCCH file
185 * @param buffer Vector to read data into
186 * @return ResultStatus result of function
187 */
188 Loader::ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer);
189
190 /**
191 * Get the RomFS of the NCCH container
192 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
193 * @param romfs_file The file containing the RomFS
194 * @param offset The offset the romfs begins on
195 * @param size The size of the romfs
196 * @return ResultStatus result of function
197 */
198 Loader::ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
199 u64& size);
200
201 /**
202 * Get the Program ID of the NCCH container
203 * @return ResultStatus result of function
204 */
205 Loader::ResultStatus ReadProgramId(u64_le& program_id);
206
207 /**
208 * Checks whether the NCCH container contains an ExeFS
209 * @return bool check result
210 */
211 bool HasExeFS();
212
213 /**
214 * Checks whether the NCCH container contains a RomFS
215 * @return bool check result
216 */
217 bool HasRomFS();
218
219 /**
220 * Checks whether the NCCH container contains an ExHeader
221 * @return bool check result
222 */
223 bool HasExHeader();
224
225 NCCH_Header ncch_header;
226 ExeFs_Header exefs_header;
227 ExHeader_Header exheader_header;
228
229private:
230 bool has_exheader = false;
231 bool has_exefs = false;
232 bool has_romfs = false;
233
234 bool is_loaded = false;
235 bool is_compressed = false;
236
237 u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header
238 u32 exefs_offset = 0;
239
240 std::string filepath;
241 FileUtil::IOFile file;
242};
243
244} // namespace FileSys
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 123fe7cd4..be2b2e25f 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -946,7 +946,7 @@ static void Init(u16 port) {
946 WSAStartup(MAKEWORD(2, 2), &InitData); 946 WSAStartup(MAKEWORD(2, 2), &InitData);
947#endif 947#endif
948 948
949 int tmpsock = socket(PF_INET, SOCK_STREAM, 0); 949 int tmpsock = static_cast<int>(socket(PF_INET, SOCK_STREAM, 0));
950 if (tmpsock == -1) { 950 if (tmpsock == -1) {
951 LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); 951 LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket");
952 } 952 }
@@ -973,7 +973,7 @@ static void Init(u16 port) {
973 sockaddr_in saddr_client; 973 sockaddr_in saddr_client;
974 sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client); 974 sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client);
975 socklen_t client_addrlen = sizeof(saddr_client); 975 socklen_t client_addrlen = sizeof(saddr_client);
976 gdbserver_socket = accept(tmpsock, client_addr, &client_addrlen); 976 gdbserver_socket = static_cast<int>(accept(tmpsock, client_addr, &client_addrlen));
977 if (gdbserver_socket < 0) { 977 if (gdbserver_socket < 0) {
978 // In the case that we couldn't start the server for whatever reason, just start CPU 978 // In the case that we couldn't start the server for whatever reason, just start CPU
979 // execution like normal. 979 // execution like normal.
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index f7f96125a..87ed85df6 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -122,11 +122,11 @@ union StaticBufferDescInfo {
122 BitField<14, 18, u32> size; 122 BitField<14, 18, u32> size;
123}; 123};
124 124
125inline u32 StaticBufferDesc(u32 size, u8 buffer_id) { 125inline u32 StaticBufferDesc(size_t size, u8 buffer_id) {
126 StaticBufferDescInfo info{}; 126 StaticBufferDescInfo info{};
127 info.descriptor_type.Assign(StaticBuffer); 127 info.descriptor_type.Assign(StaticBuffer);
128 info.buffer_id.Assign(buffer_id); 128 info.buffer_id.Assign(buffer_id);
129 info.size.Assign(size); 129 info.size.Assign(static_cast<u32>(size));
130 return info.raw; 130 return info.raw;
131} 131}
132 132
@@ -160,11 +160,11 @@ union MappedBufferDescInfo {
160 BitField<4, 28, u32> size; 160 BitField<4, 28, u32> size;
161}; 161};
162 162
163inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { 163inline u32 MappedBufferDesc(size_t size, MappedBufferPermissions perms) {
164 MappedBufferDescInfo info{}; 164 MappedBufferDescInfo info{};
165 info.flags.Assign(MappedBuffer); 165 info.flags.Assign(MappedBuffer);
166 info.perms.Assign(perms); 166 info.perms.Assign(perms);
167 info.size.Assign(size); 167 info.size.Assign(static_cast<u32>(size));
168 return info.raw; 168 return info.raw;
169} 169}
170 170
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index f0d89cffe..7cb95cbac 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -117,9 +117,9 @@ public:
117 117
118 void PushCurrentPIDHandle(); 118 void PushCurrentPIDHandle();
119 119
120 void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id); 120 void PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id);
121 121
122 void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms); 122 void PushMappedBuffer(VAddr buffer_vaddr, size_t size, MappedBufferPermissions perms);
123}; 123};
124 124
125/// Push /// 125/// Push ///
@@ -190,12 +190,12 @@ inline void RequestBuilder::PushCurrentPIDHandle() {
190 Push(u32(0)); 190 Push(u32(0));
191} 191}
192 192
193inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { 193inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id) {
194 Push(StaticBufferDesc(size, buffer_id)); 194 Push(StaticBufferDesc(size, buffer_id));
195 Push(buffer_vaddr); 195 Push(buffer_vaddr);
196} 196}
197 197
198inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size, 198inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, size_t size,
199 MappedBufferPermissions perms) { 199 MappedBufferPermissions perms) {
200 Push(MappedBufferDesc(size, perms)); 200 Push(MappedBufferDesc(size, perms));
201 Push(buffer_vaddr); 201 Push(buffer_vaddr);
@@ -227,8 +227,8 @@ public:
227 bool validateHeader = true) { 227 bool validateHeader = true) {
228 if (validateHeader) 228 if (validateHeader)
229 ValidateHeader(); 229 ValidateHeader();
230 Header builderHeader{ 230 Header builderHeader{MakeHeader(static_cast<u16>(header.command_id), normal_params_size,
231 MakeHeader(header.command_id, normal_params_size, translate_params_size)}; 231 translate_params_size)};
232 if (context != nullptr) 232 if (context != nullptr)
233 return {*context, builderHeader}; 233 return {*context, builderHeader};
234 else 234 else
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 5ebe2eca4..6020e9764 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -37,7 +37,7 @@ SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const
37 37
38u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { 38u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) {
39 request_handles.push_back(std::move(object)); 39 request_handles.push_back(std::move(object));
40 return request_handles.size() - 1; 40 return static_cast<u32>(request_handles.size() - 1);
41} 41}
42 42
43void HLERequestContext::ClearIncomingObjects() { 43void HLERequestContext::ClearIncomingObjects() {
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index cef961289..2cbca5e5b 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -90,7 +90,7 @@ void Mutex::UpdatePriority() {
90 if (!holding_thread) 90 if (!holding_thread)
91 return; 91 return;
92 92
93 s32 best_priority = THREADPRIO_LOWEST; 93 u32 best_priority = THREADPRIO_LOWEST;
94 for (auto& waiter : GetWaitingThreads()) { 94 for (auto& waiter : GetWaitingThreads()) {
95 if (waiter->current_priority < best_priority) 95 if (waiter->current_priority < best_priority)
96 best_priority = waiter->current_priority; 96 best_priority = waiter->current_priority;
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 522ad2333..cf3163e0f 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -147,7 +147,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
147 } 147 }
148 148
149 vm_manager.LogLayout(Log::Level::Debug); 149 vm_manager.LogLayout(Log::Level::Debug);
150 Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); 150 Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority, this);
151} 151}
152 152
153VAddr Process::GetLinearHeapAreaAddress() const { 153VAddr Process::GetLinearHeapAreaAddress() const {
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index a8f10a3ee..517dc47a8 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -61,7 +61,7 @@ s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const {
61 } 61 }
62} 62}
63 63
64s32 ResourceLimit::GetMaxResourceValue(u32 resource) const { 64u32 ResourceLimit::GetMaxResourceValue(u32 resource) const {
65 switch (resource) { 65 switch (resource) {
66 case PRIORITY: 66 case PRIORITY:
67 return max_priority; 67 return max_priority;
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 6cdfbcf8d..42874eb8d 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -67,7 +67,7 @@ public:
67 * @param resource Requested resource type 67 * @param resource Requested resource type
68 * @returns The max value of the resource type 68 * @returns The max value of the resource type
69 */ 69 */
70 s32 GetMaxResourceValue(u32 resource) const; 70 u32 GetMaxResourceValue(u32 resource) const;
71 71
72 /// Name of resource limit object. 72 /// Name of resource limit object.
73 std::string name; 73 std::string name;
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index a7b66142f..d45daca35 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -42,7 +42,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
42 memory_region->used += size; 42 memory_region->used += size;
43 43
44 shared_memory->linear_heap_phys_address = 44 shared_memory->linear_heap_phys_address =
45 Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset; 45 Memory::FCRAM_PADDR + memory_region->base +
46 static_cast<PAddr>(shared_memory->backing_block_offset);
46 47
47 // Increase the amount of used linear heap memory for the owner process. 48 // Increase the amount of used linear heap memory for the owner process.
48 if (shared_memory->owner_process != nullptr) { 49 if (shared_memory->owner_process != nullptr) {
@@ -54,22 +55,19 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
54 Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); 55 Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
55 } 56 }
56 } else { 57 } else {
57 // TODO(Subv): What happens if an application tries to create multiple memory blocks
58 // pointing to the same address?
59 auto& vm_manager = shared_memory->owner_process->vm_manager; 58 auto& vm_manager = shared_memory->owner_process->vm_manager;
60 // The memory is already available and mapped in the owner process. 59 // The memory is already available and mapped in the owner process.
61 auto vma = vm_manager.FindVMA(address)->second; 60 auto vma = vm_manager.FindVMA(address);
62 // Copy it over to our own storage 61 ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
63 shared_memory->backing_block = std::make_shared<std::vector<u8>>( 62 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
64 vma.backing_block->data() + vma.offset, vma.backing_block->data() + vma.offset + size); 63
65 shared_memory->backing_block_offset = 0; 64 // The returned VMA might be a bigger one encompassing the desired address.
66 // Unmap the existing pages 65 auto vma_offset = address - vma->first;
67 vm_manager.UnmapRange(address, size); 66 ASSERT_MSG(vma_offset + size <= vma->second.size,
68 // Map our own block into the address space 67 "Shared memory exceeds bounds of mapped block");
69 vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, 68
70 MemoryState::Shared); 69 shared_memory->backing_block = vma->second.backing_block;
71 // Reprotect the block with the new permissions 70 shared_memory->backing_block_offset = vma->second.offset + vma_offset;
72 vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions));
73 } 71 }
74 72
75 shared_memory->base_address = address; 73 shared_memory->base_address = address;
@@ -183,4 +181,4 @@ u8* SharedMemory::GetPointer(u32 offset) {
183 return backing_block->data() + backing_block_offset + offset; 181 return backing_block->data() + backing_block_offset + offset;
184} 182}
185 183
186} // namespace 184} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 94b335ed1..93a6f2182 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -114,7 +114,7 @@ public:
114 /// Backing memory for this shared memory block. 114 /// Backing memory for this shared memory block.
115 std::shared_ptr<std::vector<u8>> backing_block; 115 std::shared_ptr<std::vector<u8>> backing_block;
116 /// Offset into the backing block for this shared memory. 116 /// Offset into the backing block for this shared memory.
117 u32 backing_block_offset; 117 size_t backing_block_offset;
118 /// Size of the memory block. Page-aligned. 118 /// Size of the memory block. Page-aligned.
119 u32 size; 119 u32 size;
120 /// Permission restrictions applied to the process which created the block. 120 /// Permission restrictions applied to the process which created the block.
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 324415a36..0f7970ebe 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -111,7 +111,7 @@ void Thread::Stop() {
111 111
112Thread* ArbitrateHighestPriorityThread(u32 address) { 112Thread* ArbitrateHighestPriorityThread(u32 address) {
113 Thread* highest_priority_thread = nullptr; 113 Thread* highest_priority_thread = nullptr;
114 s32 priority = THREADPRIO_LOWEST; 114 u32 priority = THREADPRIO_LOWEST;
115 115
116 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 116 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
117 for (auto& thread : thread_list) { 117 for (auto& thread : thread_list) {
@@ -178,16 +178,13 @@ static void SwitchContext(Thread* new_thread) {
178 ready_queue.remove(new_thread->current_priority, new_thread); 178 ready_queue.remove(new_thread->current_priority, new_thread);
179 new_thread->status = THREADSTATUS_RUNNING; 179 new_thread->status = THREADSTATUS_RUNNING;
180 180
181 Core::CPU().LoadContext(new_thread->context);
182 Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress());
183
184 if (previous_process != current_thread->owner_process) { 181 if (previous_process != current_thread->owner_process) {
185 Kernel::g_current_process = current_thread->owner_process; 182 Kernel::g_current_process = current_thread->owner_process;
186 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; 183 SetCurrentPageTable(&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 } 184 }
185
186 Core::CPU().LoadContext(new_thread->context);
187 Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress());
191 } else { 188 } else {
192 current_thread = nullptr; 189 current_thread = nullptr;
193 // Note: We do not reset the current process and current page table when idling because 190 // Note: We do not reset the current process and current page table when idling because
@@ -250,12 +247,15 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
250 247
251 if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || 248 if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
252 thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { 249 thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
253 thread->wait_set_output = false; 250
251 // Invoke the wakeup callback before clearing the wait objects
252 if (thread->wakeup_callback)
253 thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr);
254
254 // Remove the thread from each of its waiting objects' waitlists 255 // Remove the thread from each of its waiting objects' waitlists
255 for (auto& object : thread->wait_objects) 256 for (auto& object : thread->wait_objects)
256 object->RemoveWaitingThread(thread.get()); 257 object->RemoveWaitingThread(thread.get());
257 thread->wait_objects.clear(); 258 thread->wait_objects.clear();
258 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
259 } 259 }
260 260
261 thread->ResumeFromWait(); 261 thread->ResumeFromWait();
@@ -281,6 +281,9 @@ void Thread::ResumeFromWait() {
281 break; 281 break;
282 282
283 case THREADSTATUS_READY: 283 case THREADSTATUS_READY:
284 // The thread's wakeup callback must have already been cleared when the thread was first
285 // awoken.
286 ASSERT(wakeup_callback == nullptr);
284 // If the thread is waiting on multiple wait objects, it might be awoken more than once 287 // If the thread is waiting on multiple wait objects, it might be awoken more than once
285 // before actually resuming. We can ignore subsequent wakeups if the thread status has 288 // before actually resuming. We can ignore subsequent wakeups if the thread status has
286 // already been set to THREADSTATUS_READY. 289 // already been set to THREADSTATUS_READY.
@@ -296,6 +299,8 @@ void Thread::ResumeFromWait() {
296 return; 299 return;
297 } 300 }
298 301
302 wakeup_callback = nullptr;
303
299 ready_queue.push_back(current_priority, this); 304 ready_queue.push_back(current_priority, this);
300 status = THREADSTATUS_READY; 305 status = THREADSTATUS_READY;
301 Core::System::GetInstance().PrepareReschedule(); 306 Core::System::GetInstance().PrepareReschedule();
@@ -314,7 +319,7 @@ static void DebugThreadQueue() {
314 } 319 }
315 320
316 for (auto& t : thread_list) { 321 for (auto& t : thread_list) {
317 s32 priority = ready_queue.contains(t.get()); 322 u32 priority = ready_queue.contains(t.get());
318 if (priority != -1) { 323 if (priority != -1) {
319 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); 324 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
320 } 325 }
@@ -364,7 +369,8 @@ static void ResetThreadContext(ARM_Interface::ThreadContext& context, u32 stack_
364} 369}
365 370
366ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority, 371ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority,
367 u32 arg, s32 processor_id, VAddr stack_top) { 372 u32 arg, s32 processor_id, VAddr stack_top,
373 SharedPtr<Process> owner_process) {
368 // Check if priority is in ranged. Lowest priority -> highest priority id. 374 // Check if priority is in ranged. Lowest priority -> highest priority id.
369 if (priority > THREADPRIO_LOWEST) { 375 if (priority > THREADPRIO_LOWEST) {
370 LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority); 376 LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority);
@@ -378,7 +384,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
378 384
379 // TODO(yuriks): Other checks, returning 0xD9001BEA 385 // TODO(yuriks): Other checks, returning 0xD9001BEA
380 386
381 if (!Memory::IsValidVirtualAddress(entry_point)) { 387 if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
382 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); 388 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
383 // TODO: Verify error 389 // TODO: Verify error
384 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, 390 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
@@ -397,15 +403,14 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
397 thread->nominal_priority = thread->current_priority = priority; 403 thread->nominal_priority = thread->current_priority = priority;
398 thread->last_running_ticks = CoreTiming::GetTicks(); 404 thread->last_running_ticks = CoreTiming::GetTicks();
399 thread->processor_id = processor_id; 405 thread->processor_id = processor_id;
400 thread->wait_set_output = false;
401 thread->wait_objects.clear(); 406 thread->wait_objects.clear();
402 thread->wait_address = 0; 407 thread->wait_address = 0;
403 thread->name = std::move(name); 408 thread->name = std::move(name);
404 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); 409 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
405 thread->owner_process = g_current_process; 410 thread->owner_process = owner_process;
406 411
407 // Find the next available TLS index, and mark it as used 412 // Find the next available TLS index, and mark it as used
408 auto& tls_slots = Kernel::g_current_process->tls_slots; 413 auto& tls_slots = owner_process->tls_slots;
409 bool needs_allocation = true; 414 bool needs_allocation = true;
410 u32 available_page; // Which allocated page has free space 415 u32 available_page; // Which allocated page has free space
411 u32 available_slot; // Which slot within the page is free 416 u32 available_slot; // Which slot within the page is free
@@ -424,18 +429,18 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
424 return ERR_OUT_OF_MEMORY; 429 return ERR_OUT_OF_MEMORY;
425 } 430 }
426 431
427 u32 offset = linheap_memory->size(); 432 size_t offset = linheap_memory->size();
428 433
429 // Allocate some memory from the end of the linear heap for this region. 434 // Allocate some memory from the end of the linear heap for this region.
430 linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); 435 linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0);
431 memory_region->used += Memory::PAGE_SIZE; 436 memory_region->used += Memory::PAGE_SIZE;
432 Kernel::g_current_process->linear_heap_used += Memory::PAGE_SIZE; 437 owner_process->linear_heap_used += Memory::PAGE_SIZE;
433 438
434 tls_slots.emplace_back(0); // The page is completely available at the start 439 tls_slots.emplace_back(0); // The page is completely available at the start
435 available_page = tls_slots.size() - 1; 440 available_page = static_cast<u32>(tls_slots.size() - 1);
436 available_slot = 0; // Use the first slot in the new page 441 available_slot = 0; // Use the first slot in the new page
437 442
438 auto& vm_manager = Kernel::g_current_process->vm_manager; 443 auto& vm_manager = owner_process->vm_manager;
439 vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); 444 vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
440 445
441 // Map the page to the current process' address space. 446 // Map the page to the current process' address space.
@@ -459,7 +464,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
459 return MakeResult<SharedPtr<Thread>>(std::move(thread)); 464 return MakeResult<SharedPtr<Thread>>(std::move(thread));
460} 465}
461 466
462void Thread::SetPriority(s32 priority) { 467void Thread::SetPriority(u32 priority) {
463 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, 468 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
464 "Invalid priority value."); 469 "Invalid priority value.");
465 // If thread was ready, adjust queues 470 // If thread was ready, adjust queues
@@ -472,7 +477,7 @@ void Thread::SetPriority(s32 priority) {
472} 477}
473 478
474void Thread::UpdatePriority() { 479void Thread::UpdatePriority() {
475 s32 best_priority = nominal_priority; 480 u32 best_priority = nominal_priority;
476 for (auto& mutex : held_mutexes) { 481 for (auto& mutex : held_mutexes) {
477 if (mutex->priority < best_priority) 482 if (mutex->priority < best_priority)
478 best_priority = mutex->priority; 483 best_priority = mutex->priority;
@@ -480,7 +485,7 @@ void Thread::UpdatePriority() {
480 BoostPriority(best_priority); 485 BoostPriority(best_priority);
481} 486}
482 487
483void Thread::BoostPriority(s32 priority) { 488void Thread::BoostPriority(u32 priority) {
484 // If thread was ready, adjust queues 489 // If thread was ready, adjust queues
485 if (status == THREADSTATUS_READY) 490 if (status == THREADSTATUS_READY)
486 ready_queue.move(this, current_priority, priority); 491 ready_queue.move(this, current_priority, priority);
@@ -489,10 +494,10 @@ void Thread::BoostPriority(s32 priority) {
489 current_priority = priority; 494 current_priority = priority;
490} 495}
491 496
492SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) { 497SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process) {
493 // Initialize new "main" thread 498 // Initialize new "main" thread
494 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, 499 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
495 Memory::HEAP_VADDR_END); 500 Memory::HEAP_VADDR_END, owner_process);
496 501
497 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 502 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
498 503
@@ -533,7 +538,13 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
533s32 Thread::GetWaitObjectIndex(WaitObject* object) const { 538s32 Thread::GetWaitObjectIndex(WaitObject* object) const {
534 ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); 539 ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything");
535 auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); 540 auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
536 return std::distance(match, wait_objects.rend()) - 1; 541 return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1);
542}
543
544VAddr Thread::GetCommandBufferAddress() const {
545 // Offset from the start of TLS at which the IPC command buffer begins.
546 static constexpr int CommandHeaderOffset = 0x80;
547 return GetTLSAddress() + CommandHeaderOffset;
537} 548}
538 549
539//////////////////////////////////////////////////////////////////////////////////////////////////// 550////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 6a3566f15..314fba81f 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -15,7 +15,7 @@
15#include "core/hle/kernel/wait_object.h" 15#include "core/hle/kernel/wait_object.h"
16#include "core/hle/result.h" 16#include "core/hle/result.h"
17 17
18enum ThreadPriority : s32 { 18enum ThreadPriority : u32 {
19 THREADPRIO_HIGHEST = 0, ///< Highest thread priority 19 THREADPRIO_HIGHEST = 0, ///< Highest thread priority
20 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps 20 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
21 THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps 21 THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps
@@ -41,6 +41,11 @@ enum ThreadStatus {
41 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated 41 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
42}; 42};
43 43
44enum class ThreadWakeupReason {
45 Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal.
46 Timeout // The thread was woken up due to a wait timeout.
47};
48
44namespace Kernel { 49namespace Kernel {
45 50
46class Mutex; 51class Mutex;
@@ -56,10 +61,12 @@ public:
56 * @param arg User data to pass to the thread 61 * @param arg User data to pass to the thread
57 * @param processor_id The ID(s) of the processors on which the thread is desired to be run 62 * @param processor_id The ID(s) of the processors on which the thread is desired to be run
58 * @param stack_top The address of the thread's stack top 63 * @param stack_top The address of the thread's stack top
64 * @param owner_process The parent process for the thread
59 * @return A shared pointer to the newly created thread 65 * @return A shared pointer to the newly created thread
60 */ 66 */
61 static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority, 67 static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority,
62 u32 arg, s32 processor_id, VAddr stack_top); 68 u32 arg, s32 processor_id, VAddr stack_top,
69 SharedPtr<Process> owner_process);
63 70
64 std::string GetName() const override { 71 std::string GetName() const override {
65 return name; 72 return name;
@@ -80,7 +87,7 @@ public:
80 * Gets the thread's current priority 87 * Gets the thread's current priority
81 * @return The current thread's priority 88 * @return The current thread's priority
82 */ 89 */
83 s32 GetPriority() const { 90 u32 GetPriority() const {
84 return current_priority; 91 return current_priority;
85 } 92 }
86 93
@@ -88,7 +95,7 @@ public:
88 * Sets the thread's current priority 95 * Sets the thread's current priority
89 * @param priority The new priority 96 * @param priority The new priority
90 */ 97 */
91 void SetPriority(s32 priority); 98 void SetPriority(u32 priority);
92 99
93 /** 100 /**
94 * Boost's a thread's priority to the best priority among the thread's held mutexes. 101 * Boost's a thread's priority to the best priority among the thread's held mutexes.
@@ -100,7 +107,7 @@ public:
100 * Temporarily boosts the thread's priority until the next time it is scheduled 107 * Temporarily boosts the thread's priority until the next time it is scheduled
101 * @param priority The new priority 108 * @param priority The new priority
102 */ 109 */
103 void BoostPriority(s32 priority); 110 void BoostPriority(u32 priority);
104 111
105 /** 112 /**
106 * Gets the thread's thread ID 113 * Gets the thread's thread ID
@@ -116,9 +123,9 @@ public:
116 void ResumeFromWait(); 123 void ResumeFromWait();
117 124
118 /** 125 /**
119 * Schedules an event to wake up the specified thread after the specified delay 126 * Schedules an event to wake up the specified thread after the specified delay
120 * @param nanoseconds The time this thread will be allowed to sleep for 127 * @param nanoseconds The time this thread will be allowed to sleep for
121 */ 128 */
122 void WakeAfterDelay(s64 nanoseconds); 129 void WakeAfterDelay(s64 nanoseconds);
123 130
124 /** 131 /**
@@ -157,6 +164,12 @@ public:
157 return tls_address; 164 return tls_address;
158 } 165 }
159 166
167 /*
168 * Returns the address of the current thread's command buffer, located in the TLS.
169 * @returns VAddr of the thread's command buffer.
170 */
171 VAddr GetCommandBufferAddress() const;
172
160 /** 173 /**
161 * Returns whether this thread is waiting for all the objects in 174 * Returns whether this thread is waiting for all the objects in
162 * its wait list to become ready, as a result of a WaitSynchronizationN call 175 * its wait list to become ready, as a result of a WaitSynchronizationN call
@@ -174,8 +187,8 @@ public:
174 u32 entry_point; 187 u32 entry_point;
175 u32 stack_top; 188 u32 stack_top;
176 189
177 s32 nominal_priority; ///< Nominal thread priority, as set by the emulated application 190 u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application
178 s32 current_priority; ///< Current thread priority, can be temporarily changed 191 u32 current_priority; ///< Current thread priority, can be temporarily changed
179 192
180 u64 last_running_ticks; ///< CPU tick when thread was last running 193 u64 last_running_ticks; ///< CPU tick when thread was last running
181 194
@@ -197,14 +210,18 @@ public:
197 210
198 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address 211 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address
199 212
200 /// True if the WaitSynchronizationN output parameter should be set on thread wakeup.
201 bool wait_set_output;
202
203 std::string name; 213 std::string name;
204 214
205 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. 215 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
206 Handle callback_handle; 216 Handle callback_handle;
207 217
218 using WakeupCallback = void(ThreadWakeupReason reason, SharedPtr<Thread> thread,
219 SharedPtr<WaitObject> object);
220 // Callback that will be invoked when the thread is resumed from a waiting state. If the thread
221 // was waiting via WaitSynchronizationN then the object will be the last object that became
222 // available. In case of a timeout, the object will be nullptr.
223 std::function<WakeupCallback> wakeup_callback;
224
208private: 225private:
209 Thread(); 226 Thread();
210 ~Thread() override; 227 ~Thread() override;
@@ -214,9 +231,10 @@ private:
214 * Sets up the primary application thread 231 * Sets up the primary application thread
215 * @param entry_point The address at which the thread should start execution 232 * @param entry_point The address at which the thread should start execution
216 * @param priority The priority to give the main thread 233 * @param priority The priority to give the main thread
234 * @param owner_process The parent process for the main thread
217 * @return A shared pointer to the main thread 235 * @return A shared pointer to the main thread
218 */ 236 */
219SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority); 237SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process);
220 238
221/** 239/**
222 * Returns whether there are any threads that are ready to run. 240 * Returns whether there are any threads that are ready to run.
@@ -276,4 +294,4 @@ void ThreadingShutdown();
276 */ 294 */
277const std::vector<SharedPtr<Thread>>& GetThreadList(); 295const std::vector<SharedPtr<Thread>>& GetThreadList();
278 296
279} // namespace 297} // namespace Kernel
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index f245eda6c..469554908 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -34,7 +34,7 @@ void WaitObject::RemoveWaitingThread(Thread* thread) {
34 34
35SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { 35SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
36 Thread* candidate = nullptr; 36 Thread* candidate = nullptr;
37 s32 candidate_priority = THREADPRIO_LOWEST + 1; 37 u32 candidate_priority = THREADPRIO_LOWEST + 1;
38 38
39 for (const auto& thread : waiting_threads) { 39 for (const auto& thread : waiting_threads) {
40 // The list of waiting threads must not contain threads that are not waiting to be awakened. 40 // The list of waiting threads must not contain threads that are not waiting to be awakened.
@@ -71,23 +71,20 @@ void WaitObject::WakeupAllWaitingThreads() {
71 while (auto thread = GetHighestPriorityReadyThread()) { 71 while (auto thread = GetHighestPriorityReadyThread()) {
72 if (!thread->IsSleepingOnWaitAll()) { 72 if (!thread->IsSleepingOnWaitAll()) {
73 Acquire(thread.get()); 73 Acquire(thread.get());
74 // Set the output index of the WaitSynchronizationN call to the index of this object.
75 if (thread->wait_set_output) {
76 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this));
77 thread->wait_set_output = false;
78 }
79 } else { 74 } else {
80 for (auto& object : thread->wait_objects) { 75 for (auto& object : thread->wait_objects) {
81 object->Acquire(thread.get()); 76 object->Acquire(thread.get());
82 } 77 }
83 // Note: This case doesn't update the output index of WaitSynchronizationN.
84 } 78 }
85 79
80 // Invoke the wakeup callback before clearing the wait objects
81 if (thread->wakeup_callback)
82 thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this);
83
86 for (auto& object : thread->wait_objects) 84 for (auto& object : thread->wait_objects)
87 object->RemoveWaitingThread(thread.get()); 85 object->RemoveWaitingThread(thread.get());
88 thread->wait_objects.clear(); 86 thread->wait_objects.clear();
89 87
90 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
91 thread->ResumeFromWait(); 88 thread->ResumeFromWait();
92 } 89 }
93} 90}
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index c36775473..59ea9823d 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -65,6 +65,7 @@ union AppletAttributes {
65 u32 raw; 65 u32 raw;
66 66
67 BitField<0, 3, u32> applet_pos; 67 BitField<0, 3, u32> applet_pos;
68 BitField<29, 1, u32> is_home_menu;
68 69
69 AppletAttributes() : raw(0) {} 70 AppletAttributes() : raw(0) {}
70 AppletAttributes(u32 attributes) : raw(attributes) {} 71 AppletAttributes(u32 attributes) : raw(attributes) {}
@@ -158,6 +159,11 @@ static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) {
158 if (slot == AppletSlot::Error) 159 if (slot == AppletSlot::Error)
159 return nullptr; 160 return nullptr;
160 161
162 // The Home Menu is a system applet, however, it has its own applet slot so that it can run
163 // concurrently with other system applets.
164 if (slot == AppletSlot::SystemApplet && attributes.is_home_menu)
165 return &applet_slots[static_cast<size_t>(AppletSlot::HomeMenu)];
166
161 return &applet_slots[static_cast<size_t>(slot)]; 167 return &applet_slots[static_cast<size_t>(slot)];
162} 168}
163 169
@@ -201,6 +207,19 @@ void Initialize(Service::Interface* self) {
201 rb.Push(RESULT_SUCCESS); 207 rb.Push(RESULT_SUCCESS);
202 rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(), 208 rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(),
203 Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); 209 Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());
210
211 if (slot_data->applet_id == AppletId::Application ||
212 slot_data->applet_id == AppletId::HomeMenu) {
213 // Initialize the APT parameter to wake up the application.
214 next_parameter.emplace();
215 next_parameter->signal = static_cast<u32>(SignalType::Wakeup);
216 next_parameter->sender_id = static_cast<u32>(AppletId::None);
217 next_parameter->destination_id = app_id;
218 // Not signaling the parameter event will cause the application (or Home Menu) to hang
219 // during startup. In the real console, it is usually the Kernel and Home Menu who cause NS
220 // to signal the HomeMenu and Application parameter events, respectively.
221 slot_data->parameter_event->Signal();
222 }
204} 223}
205 224
206static u32 DecompressLZ11(const u8* in, u8* out) { 225static u32 DecompressLZ11(const u8* in, u8* out) {
@@ -563,7 +582,7 @@ void ReceiveParameter(Service::Interface* self) {
563 ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap() 582 ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap()
564 : 0); 583 : 0);
565 584
566 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter->buffer.size()), 0); 585 rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
567 586
568 Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size()); 587 Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
569 588
@@ -611,7 +630,7 @@ void GlanceParameter(Service::Interface* self) {
611 ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap() 630 ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap()
612 : 0); 631 : 0);
613 632
614 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter->buffer.size()), 0); 633 rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
615 634
616 Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size()); 635 Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
617 636
@@ -763,6 +782,20 @@ void PrepareToStartLibraryApplet(Service::Interface* self) {
763 } 782 }
764} 783}
765 784
785void PrepareToStartNewestHomeMenu(Service::Interface* self) {
786 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); // 0x1A0000
787 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
788
789 // TODO(Subv): This command can only be called by a System Applet (return 0xC8A0CC04 otherwise).
790
791 // This command must return an error when called, otherwise the Home Menu will try to reboot the
792 // system.
793 rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
794 ErrorSummary::InvalidState, ErrorLevel::Status));
795
796 LOG_DEBUG(Service_APT, "called");
797}
798
766void PreloadLibraryApplet(Service::Interface* self) { 799void PreloadLibraryApplet(Service::Interface* self) {
767 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 800 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040
768 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); 801 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
@@ -1058,12 +1091,6 @@ void Init() {
1058 slot_data.parameter_event = 1091 slot_data.parameter_event =
1059 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter"); 1092 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter");
1060 } 1093 }
1061
1062 // Initialize the parameter to wake up the application.
1063 next_parameter.emplace();
1064 next_parameter->signal = static_cast<u32>(SignalType::Wakeup);
1065 next_parameter->destination_id = static_cast<u32>(AppletId::Application);
1066 applet_slots[static_cast<size_t>(AppletSlot::Application)].parameter_event->Signal();
1067} 1094}
1068 1095
1069void Shutdown() { 1096void Shutdown() {
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 96b28b438..7b79e1f3e 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -420,6 +420,16 @@ void GetAppCpuTimeLimit(Service::Interface* self);
420void PrepareToStartLibraryApplet(Service::Interface* self); 420void PrepareToStartLibraryApplet(Service::Interface* self);
421 421
422/** 422/**
423 * APT::PrepareToStartNewestHomeMenu service function
424 * Inputs:
425 * 0 : Command header [0x001A0000]
426 * Outputs:
427 * 0 : Return header
428 * 1 : Result of function
429 */
430void PrepareToStartNewestHomeMenu(Service::Interface* self);
431
432/**
423 * APT::PreloadLibraryApplet service function 433 * APT::PreloadLibraryApplet service function
424 * Inputs: 434 * Inputs:
425 * 0 : Command header [0x00160040] 435 * 0 : Command header [0x00160040]
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index cf74c2a36..bb78ee7d7 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -17,7 +17,7 @@ const Interface::FunctionInfo FunctionTable[] = {
17 {0x00060040, GetAppletInfo, "GetAppletInfo"}, 17 {0x00060040, GetAppletInfo, "GetAppletInfo"},
18 {0x00070000, nullptr, "GetLastSignaledAppletId"}, 18 {0x00070000, nullptr, "GetLastSignaledAppletId"},
19 {0x00080000, nullptr, "CountRegisteredApplet"}, 19 {0x00080000, nullptr, "CountRegisteredApplet"},
20 {0x00090040, nullptr, "IsRegistered"}, 20 {0x00090040, IsRegistered, "IsRegistered"},
21 {0x000A0040, nullptr, "GetAttribute"}, 21 {0x000A0040, nullptr, "GetAttribute"},
22 {0x000B0040, InquireNotification, "InquireNotification"}, 22 {0x000B0040, InquireNotification, "InquireNotification"},
23 {0x000C0104, SendParameter, "SendParameter"}, 23 {0x000C0104, SendParameter, "SendParameter"},
@@ -34,7 +34,7 @@ const Interface::FunctionInfo FunctionTable[] = {
34 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, 34 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
35 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, 35 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
36 {0x00190040, nullptr, "PrepareToStartSystemApplet"}, 36 {0x00190040, nullptr, "PrepareToStartSystemApplet"},
37 {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, 37 {0x001A0000, PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"},
38 {0x001B00C4, nullptr, "StartApplication"}, 38 {0x001B00C4, nullptr, "StartApplication"},
39 {0x001C0000, nullptr, "WakeupApplication"}, 39 {0x001C0000, nullptr, "WakeupApplication"},
40 {0x001D0000, nullptr, "CancelApplication"}, 40 {0x001D0000, nullptr, "CancelApplication"},
diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp
index c9f9e9d95..8172edae8 100644
--- a/src/core/hle/service/cam/cam.cpp
+++ b/src/core/hle/service/cam/cam.cpp
@@ -177,7 +177,7 @@ void CompletionEventCallBack(u64 port_id, int) {
177 LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!", 177 LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!",
178 port.dest_size, buffer_size); 178 port.dest_size, buffer_size);
179 } 179 }
180 Memory::WriteBlock(port.dest, buffer.data(), std::min<u32>(port.dest_size, buffer_size)); 180 Memory::WriteBlock(port.dest, buffer.data(), std::min<size_t>(port.dest_size, buffer_size));
181 } 181 }
182 182
183 port.is_receiving = false; 183 port.is_receiving = false;
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index f26a1f65f..f78c25fb2 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -141,7 +141,7 @@ void GetCountryCodeString(Service::Interface* self) {
141 141
142void GetCountryCodeID(Service::Interface* self) { 142void GetCountryCodeID(Service::Interface* self) {
143 u32* cmd_buff = Kernel::GetCommandBuffer(); 143 u32* cmd_buff = Kernel::GetCommandBuffer();
144 u16 country_code = cmd_buff[1]; 144 u16 country_code = static_cast<u16>(cmd_buff[1]);
145 u16 country_code_id = 0; 145 u16 country_code_id = 0;
146 146
147 // The following algorithm will fail if the first country code isn't 0. 147 // The following algorithm will fail if the first country code isn't 0.
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 033fbc9aa..4ee7df73c 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -20,6 +20,7 @@
20#include "core/file_sys/archive_savedata.h" 20#include "core/file_sys/archive_savedata.h"
21#include "core/file_sys/archive_sdmc.h" 21#include "core/file_sys/archive_sdmc.h"
22#include "core/file_sys/archive_sdmcwriteonly.h" 22#include "core/file_sys/archive_sdmcwriteonly.h"
23#include "core/file_sys/archive_selfncch.h"
23#include "core/file_sys/archive_systemsavedata.h" 24#include "core/file_sys/archive_systemsavedata.h"
24#include "core/file_sys/directory_backend.h" 25#include "core/file_sys/directory_backend.h"
25#include "core/file_sys/errors.h" 26#include "core/file_sys/errors.h"
@@ -48,7 +49,7 @@ struct hash<Service::FS::ArchiveIdCode> {
48 return std::hash<Type>()(static_cast<Type>(id_code)); 49 return std::hash<Type>()(static_cast<Type>(id_code));
49 } 50 }
50}; 51};
51} 52} // namespace std
52 53
53static constexpr Kernel::Handle INVALID_HANDLE{}; 54static constexpr Kernel::Handle INVALID_HANDLE{};
54 55
@@ -216,7 +217,7 @@ void Directory::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> serve
216 LOG_TRACE(Service_FS, "Read %s: count=%d", GetName().c_str(), count); 217 LOG_TRACE(Service_FS, "Read %s: count=%d", GetName().c_str(), count);
217 218
218 // Number of entries actually read 219 // Number of entries actually read
219 u32 read = backend->Read(entries.size(), entries.data()); 220 u32 read = backend->Read(static_cast<u32>(entries.size()), entries.data());
220 cmd_buff[2] = read; 221 cmd_buff[2] = read;
221 Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry)); 222 Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry));
222 break; 223 break;
@@ -564,6 +565,21 @@ void RegisterArchiveTypes() {
564 auto systemsavedata_factory = 565 auto systemsavedata_factory =
565 std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); 566 std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
566 RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); 567 RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
568
569 auto selfncch_factory = std::make_unique<FileSys::ArchiveFactory_SelfNCCH>();
570 RegisterArchiveType(std::move(selfncch_factory), ArchiveIdCode::SelfNCCH);
571}
572
573void RegisterSelfNCCH(Loader::AppLoader& app_loader) {
574 auto itr = id_code_map.find(ArchiveIdCode::SelfNCCH);
575 if (itr == id_code_map.end()) {
576 LOG_ERROR(Service_FS,
577 "Could not register a new NCCH because the SelfNCCH archive hasn't been created");
578 return;
579 }
580
581 auto* factory = static_cast<FileSys::ArchiveFactory_SelfNCCH*>(itr->second.get());
582 factory->Register(app_loader);
567} 583}
568 584
569void UnregisterArchiveTypes() { 585void UnregisterArchiveTypes() {
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 3a3371c88..e3c8fc2ef 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -21,6 +21,10 @@ static constexpr char SYSTEM_ID[]{"00000000000000000000000000000000"};
21/// The scrambled SD card CID, also known as ID1 21/// The scrambled SD card CID, also known as ID1
22static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"}; 22static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"};
23 23
24namespace Loader {
25class AppLoader;
26}
27
24namespace Service { 28namespace Service {
25namespace FS { 29namespace FS {
26 30
@@ -259,6 +263,9 @@ void ArchiveInit();
259/// Shutdown archives 263/// Shutdown archives
260void ArchiveShutdown(); 264void ArchiveShutdown();
261 265
266/// Registers a new NCCH file with the SelfNCCH archive factory
267void RegisterSelfNCCH(Loader::AppLoader& app_loader);
268
262/// Register all archive types 269/// Register all archive types
263void RegisterArchiveTypes(); 270void RegisterArchiveTypes();
264 271
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index aa5d821f9..379fbd71c 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -251,7 +251,7 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
251 Math::Vec3<float> gyro; 251 Math::Vec3<float> gyro;
252 std::tie(std::ignore, gyro) = motion_device->GetStatus(); 252 std::tie(std::ignore, gyro) = motion_device->GetStatus();
253 double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale(); 253 double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
254 gyro *= gyroscope_coef * stretch; 254 gyro *= gyroscope_coef * static_cast<float>(stretch);
255 gyroscope_entry.x = static_cast<s16>(gyro.x); 255 gyroscope_entry.x = static_cast<s16>(gyro.x);
256 gyroscope_entry.y = static_cast<s16>(gyro.y); 256 gyroscope_entry.y = static_cast<s16>(gyro.y);
257 gyroscope_entry.z = static_cast<s16>(gyro.z); 257 gyroscope_entry.z = static_cast<s16>(gyro.z);
diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h
index 3bc10dbdc..57b4fb6df 100644
--- a/src/core/hle/service/ldr_ro/cro_helper.h
+++ b/src/core/hle/service/ldr_ro/cro_helper.h
@@ -413,7 +413,8 @@ private:
413 */ 413 */
414 template <typename T> 414 template <typename T>
415 void GetEntry(std::size_t index, T& data) const { 415 void GetEntry(std::size_t index, T& data) const {
416 Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); 416 Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast<u32>(index * sizeof(T)),
417 &data, sizeof(T));
417 } 418 }
418 419
419 /** 420 /**
@@ -425,7 +426,8 @@ private:
425 */ 426 */
426 template <typename T> 427 template <typename T>
427 void SetEntry(std::size_t index, const T& data) { 428 void SetEntry(std::size_t index, const T& data) {
428 Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); 429 Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast<u32>(index * sizeof(T)),
430 &data, sizeof(T));
429 } 431 }
430 432
431 /** 433 /**
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index d5624fe54..b10d5852b 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -5,6 +5,8 @@
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/hle/ipc.h" 7#include "core/hle/ipc.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/event.h"
8#include "core/hle/service/nim/nim.h" 10#include "core/hle/service/nim/nim.h"
9#include "core/hle/service/nim/nim_aoc.h" 11#include "core/hle/service/nim/nim_aoc.h"
10#include "core/hle/service/nim/nim_s.h" 12#include "core/hle/service/nim/nim_s.h"
@@ -14,6 +16,16 @@
14namespace Service { 16namespace Service {
15namespace NIM { 17namespace NIM {
16 18
19static Kernel::SharedPtr<Kernel::Event> nim_system_update_event;
20
21void CheckForSysUpdateEvent(Service::Interface* self) {
22 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 0, 0); // 0x50000
23 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
24 rb.Push(RESULT_SUCCESS);
25 rb.PushCopyHandles(Kernel::g_handle_table.Create(nim_system_update_event).Unwrap());
26 LOG_TRACE(Service_NIM, "called");
27}
28
17void CheckSysUpdateAvailable(Service::Interface* self) { 29void CheckSysUpdateAvailable(Service::Interface* self) {
18 u32* cmd_buff = Kernel::GetCommandBuffer(); 30 u32* cmd_buff = Kernel::GetCommandBuffer();
19 31
@@ -29,9 +41,13 @@ void Init() {
29 AddService(new NIM_AOC_Interface); 41 AddService(new NIM_AOC_Interface);
30 AddService(new NIM_S_Interface); 42 AddService(new NIM_S_Interface);
31 AddService(new NIM_U_Interface); 43 AddService(new NIM_U_Interface);
44
45 nim_system_update_event = Kernel::Event::Create(ResetType::OneShot, "NIM System Update Event");
32} 46}
33 47
34void Shutdown() {} 48void Shutdown() {
49 nim_system_update_event = nullptr;
50}
35 51
36} // namespace NIM 52} // namespace NIM
37 53
diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h
index c3106f18b..dbf605e5a 100644
--- a/src/core/hle/service/nim/nim.h
+++ b/src/core/hle/service/nim/nim.h
@@ -11,6 +11,17 @@ class Interface;
11namespace NIM { 11namespace NIM {
12 12
13/** 13/**
14 * NIM::CheckForSysUpdateEvent service function
15 * Inputs:
16 * 1 : None
17 * Outputs:
18 * 1 : Result of function, 0 on success, otherwise error code
19 * 2 : Copy handle descriptor
20 * 3 : System Update event handle
21 */
22void CheckForSysUpdateEvent(Service::Interface* self);
23
24/**
14 * NIM::CheckSysUpdateAvailable service function 25 * NIM::CheckSysUpdateAvailable service function
15 * Inputs: 26 * Inputs:
16 * 1 : None 27 * 1 : None
diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp
index 7664bad60..569660278 100644
--- a/src/core/hle/service/nim/nim_u.cpp
+++ b/src/core/hle/service/nim/nim_u.cpp
@@ -12,7 +12,7 @@ const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010000, nullptr, "StartSysUpdate"}, 12 {0x00010000, nullptr, "StartSysUpdate"},
13 {0x00020000, nullptr, "GetUpdateDownloadProgress"}, 13 {0x00020000, nullptr, "GetUpdateDownloadProgress"},
14 {0x00040000, nullptr, "FinishTitlesInstall"}, 14 {0x00040000, nullptr, "FinishTitlesInstall"},
15 {0x00050000, nullptr, "CheckForSysUpdateEvent"}, 15 {0x00050000, CheckForSysUpdateEvent, "CheckForSysUpdateEvent"},
16 {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, 16 {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"},
17 {0x000A0000, nullptr, "GetState"}, 17 {0x000A0000, nullptr, "GetState"},
18 {0x000B0000, nullptr, "GetSystemTitleHash"}, 18 {0x000B0000, nullptr, "GetSystemTitleHash"},
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index 893bbb1e7..0aa63cc1e 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -2,8 +2,10 @@
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 <algorithm>
5#include <array> 6#include <array>
6#include <cstring> 7#include <cstring>
8#include <list>
7#include <mutex> 9#include <mutex>
8#include <unordered_map> 10#include <unordered_map>
9#include <vector> 11#include <vector>
@@ -13,6 +15,7 @@
13#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/event.h" 16#include "core/hle/kernel/event.h"
15#include "core/hle/kernel/shared_memory.h" 17#include "core/hle/kernel/shared_memory.h"
18#include "core/hle/lock.h"
16#include "core/hle/result.h" 19#include "core/hle/result.h"
17#include "core/hle/service/nwm/nwm_uds.h" 20#include "core/hle/service/nwm/nwm_uds.h"
18#include "core/hle/service/nwm/uds_beacon.h" 21#include "core/hle/service/nwm/uds_beacon.h"
@@ -37,9 +40,12 @@ static ConnectionStatus connection_status{};
37/* Node information about the current network. 40/* Node information about the current network.
38 * The amount of elements in this vector is always the maximum number 41 * The amount of elements in this vector is always the maximum number
39 * of nodes specified in the network configuration. 42 * of nodes specified in the network configuration.
40 * The first node is always the host, so this always contains at least 1 entry. 43 * The first node is always the host.
41 */ 44 */
42static NodeList node_info(1); 45static NodeList node_info;
46
47// Node information about our own system.
48static NodeInfo current_node;
43 49
44// Mapping of bind node ids to their respective events. 50// Mapping of bind node ids to their respective events.
45static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; 51static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events;
@@ -54,6 +60,10 @@ static NetworkInfo network_info;
54// Event that will generate and send the 802.11 beacon frames. 60// Event that will generate and send the 802.11 beacon frames.
55static int beacon_broadcast_event; 61static int beacon_broadcast_event;
56 62
63// Mutex to synchronize access to the connection status between the emulation thread and the
64// network thread.
65static std::mutex connection_status_mutex;
66
57// Mutex to synchronize access to the list of received beacons between the emulation thread and the 67// Mutex to synchronize access to the list of received beacons between the emulation thread and the
58// network thread. 68// network thread.
59static std::mutex beacon_mutex; 69static std::mutex beacon_mutex;
@@ -63,14 +73,26 @@ static std::mutex beacon_mutex;
63constexpr size_t MaxBeaconFrames = 15; 73constexpr size_t MaxBeaconFrames = 15;
64 74
65// List of the last <MaxBeaconFrames> beacons received from the network. 75// List of the last <MaxBeaconFrames> beacons received from the network.
66static std::deque<Network::WifiPacket> received_beacons; 76static std::list<Network::WifiPacket> received_beacons;
67 77
68/** 78/**
69 * Returns a list of received 802.11 beacon frames from the specified sender since the last call. 79 * Returns a list of received 802.11 beacon frames from the specified sender since the last call.
70 */ 80 */
71std::deque<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { 81std::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) {
72 std::lock_guard<std::mutex> lock(beacon_mutex); 82 std::lock_guard<std::mutex> lock(beacon_mutex);
73 // TODO(Subv): Filter by sender. 83 if (sender != Network::BroadcastMac) {
84 std::list<Network::WifiPacket> filtered_list;
85 const auto beacon = std::find_if(received_beacons.begin(), received_beacons.end(),
86 [&sender](const Network::WifiPacket& packet) {
87 return packet.transmitter_address == sender;
88 });
89 if (beacon != received_beacons.end()) {
90 filtered_list.push_back(*beacon);
91 // TODO(B3N30): Check if the complete deque is cleared or just the fetched entries
92 received_beacons.erase(beacon);
93 }
94 return filtered_list;
95 }
74 return std::move(received_beacons); 96 return std::move(received_beacons);
75} 97}
76 98
@@ -79,10 +101,33 @@ void SendPacket(Network::WifiPacket& packet) {
79 // TODO(Subv): Implement. 101 // TODO(Subv): Implement.
80} 102}
81 103
104/*
105 * Returns an available index in the nodes array for the
106 * currently-hosted UDS network.
107 */
108static u16 GetNextAvailableNodeId() {
109 for (u16 index = 0; index < connection_status.max_nodes; ++index) {
110 if ((connection_status.node_bitmask & (1 << index)) == 0)
111 return index;
112 }
113
114 // Any connection attempts to an already full network should have been refused.
115 ASSERT_MSG(false, "No available connection slots in the network");
116}
117
82// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size 118// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size
83// limit is exceeded. 119// limit is exceeded.
84void HandleBeaconFrame(const Network::WifiPacket& packet) { 120void HandleBeaconFrame(const Network::WifiPacket& packet) {
85 std::lock_guard<std::mutex> lock(beacon_mutex); 121 std::lock_guard<std::mutex> lock(beacon_mutex);
122 const auto unique_beacon =
123 std::find_if(received_beacons.begin(), received_beacons.end(),
124 [&packet](const Network::WifiPacket& new_packet) {
125 return new_packet.transmitter_address == packet.transmitter_address;
126 });
127 if (unique_beacon != received_beacons.end()) {
128 // We already have a beacon from the same mac in the deque, remove the old one;
129 received_beacons.erase(unique_beacon);
130 }
86 131
87 received_beacons.emplace_back(packet); 132 received_beacons.emplace_back(packet);
88 133
@@ -91,21 +136,110 @@ void HandleBeaconFrame(const Network::WifiPacket& packet) {
91 received_beacons.pop_front(); 136 received_beacons.pop_front();
92} 137}
93 138
94/* 139void HandleAssociationResponseFrame(const Network::WifiPacket& packet) {
95 * Returns an available index in the nodes array for the 140 auto assoc_result = GetAssociationResult(packet.data);
96 * currently-hosted UDS network.
97 */
98static u16 GetNextAvailableNodeId() {
99 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
100 "Can not accept clients if we're not hosting a network");
101 141
102 for (u16 index = 0; index < connection_status.max_nodes; ++index) { 142 ASSERT_MSG(std::get<AssocStatus>(assoc_result) == AssocStatus::Successful,
103 if ((connection_status.node_bitmask & (1 << index)) == 0) 143 "Could not join network");
104 return index; 144 {
145 std::lock_guard<std::mutex> lock(connection_status_mutex);
146 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::Connecting));
105 } 147 }
106 148
107 // Any connection attempts to an already full network should have been refused. 149 // Send the EAPoL-Start packet to the server.
108 ASSERT_MSG(false, "No available connection slots in the network"); 150 using Network::WifiPacket;
151 WifiPacket eapol_start;
152 eapol_start.channel = network_channel;
153 eapol_start.data = GenerateEAPoLStartFrame(std::get<u16>(assoc_result), current_node);
154 // TODO(B3N30): Encrypt the packet.
155 eapol_start.destination_address = packet.transmitter_address;
156 eapol_start.type = WifiPacket::PacketType::Data;
157
158 SendPacket(eapol_start);
159}
160
161static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
162 std::lock_guard<std::mutex> lock(connection_status_mutex);
163
164 if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) {
165 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
166 LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u",
167 connection_status.status);
168 return;
169 }
170
171 auto node = DeserializeNodeInfoFromFrame(packet.data);
172
173 if (connection_status.max_nodes == connection_status.total_nodes) {
174 // Reject connection attempt
175 LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent.");
176 // TODO(B3N30): Figure out what packet is sent here
177 return;
178 }
179
180 // Get an unused network node id
181 u16 node_id = GetNextAvailableNodeId();
182 node.network_node_id = node_id + 1;
183
184 connection_status.node_bitmask |= 1 << node_id;
185 connection_status.changed_nodes |= 1 << node_id;
186 connection_status.nodes[node_id] = node.network_node_id;
187 connection_status.total_nodes++;
188
189 u8 current_nodes = network_info.total_nodes;
190 node_info[current_nodes] = node;
191
192 network_info.total_nodes++;
193
194 // Send the EAPoL-Logoff packet.
195 using Network::WifiPacket;
196 WifiPacket eapol_logoff;
197 eapol_logoff.channel = network_channel;
198 eapol_logoff.data =
199 GenerateEAPoLLogoffFrame(packet.transmitter_address, node.network_node_id, node_info,
200 network_info.max_nodes, network_info.total_nodes);
201 // TODO(Subv): Encrypt the packet.
202 eapol_logoff.destination_address = packet.transmitter_address;
203 eapol_logoff.type = WifiPacket::PacketType::Data;
204
205 SendPacket(eapol_logoff);
206 // TODO(B3N30): Broadcast updated node list
207 // The 3ds does this presumably to support spectators.
208 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
209 connection_status_event->Signal();
210 } else {
211 if (connection_status.status != static_cast<u32>(NetworkStatus::NotConnected)) {
212 LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u",
213 connection_status.status);
214 return;
215 }
216 auto logoff = ParseEAPoLLogoffFrame(packet.data);
217
218 network_info.total_nodes = logoff.connected_nodes;
219 network_info.max_nodes = logoff.max_nodes;
220
221 connection_status.network_node_id = logoff.assigned_node_id;
222 connection_status.total_nodes = logoff.connected_nodes;
223 connection_status.max_nodes = logoff.max_nodes;
224
225 node_info.clear();
226 node_info.reserve(network_info.max_nodes);
227 for (size_t index = 0; index < logoff.connected_nodes; ++index) {
228 connection_status.node_bitmask |= 1 << index;
229 connection_status.changed_nodes |= 1 << index;
230 connection_status.nodes[index] = logoff.nodes[index].network_node_id;
231
232 node_info.emplace_back(DeserializeNodeInfo(logoff.nodes[index]));
233 }
234
235 // We're now connected, signal the application
236 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsClient);
237 // Some games require ConnectToNetwork to block, for now it doesn't
238 // If blocking is implemented this lock needs to be changed,
239 // otherwise it might cause deadlocks
240 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
241 connection_status_event->Signal();
242 }
109} 243}
110 244
111/* 245/*
@@ -113,35 +247,46 @@ static u16 GetNextAvailableNodeId() {
113 * authentication frame with SEQ1. 247 * authentication frame with SEQ1.
114 */ 248 */
115void StartConnectionSequence(const MacAddress& server) { 249void StartConnectionSequence(const MacAddress& server) {
116 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected));
117
118 // TODO(Subv): Handle timeout.
119
120 // Send an authentication frame with SEQ1
121 using Network::WifiPacket; 250 using Network::WifiPacket;
122 WifiPacket auth_request; 251 WifiPacket auth_request;
123 auth_request.channel = network_channel; 252 {
124 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); 253 std::lock_guard<std::mutex> lock(connection_status_mutex);
125 auth_request.destination_address = server; 254 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected));
126 auth_request.type = WifiPacket::PacketType::Authentication; 255
256 // TODO(Subv): Handle timeout.
257
258 // Send an authentication frame with SEQ1
259 auth_request.channel = network_channel;
260 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1);
261 auth_request.destination_address = server;
262 auth_request.type = WifiPacket::PacketType::Authentication;
263 }
127 264
128 SendPacket(auth_request); 265 SendPacket(auth_request);
129} 266}
130 267
131/// Sends an Association Response frame to the specified mac address 268/// Sends an Association Response frame to the specified mac address
132void SendAssociationResponseFrame(const MacAddress& address) { 269void SendAssociationResponseFrame(const MacAddress& address) {
133 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost));
134
135 using Network::WifiPacket; 270 using Network::WifiPacket;
136 WifiPacket assoc_response; 271 WifiPacket assoc_response;
137 assoc_response.channel = network_channel; 272
138 // TODO(Subv): This will cause multiple clients to end up with the same association id, but 273 {
139 // we're not using that for anything. 274 std::lock_guard<std::mutex> lock(connection_status_mutex);
140 u16 association_id = 1; 275 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
141 assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, 276 LOG_ERROR(Service_NWM, "Connection sequence aborted, because connection status is %u",
142 network_info.network_id); 277 connection_status.status);
143 assoc_response.destination_address = address; 278 return;
144 assoc_response.type = WifiPacket::PacketType::AssociationResponse; 279 }
280
281 assoc_response.channel = network_channel;
282 // TODO(Subv): This will cause multiple clients to end up with the same association id, but
283 // we're not using that for anything.
284 u16 association_id = 1;
285 assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id,
286 network_info.network_id);
287 assoc_response.destination_address = address;
288 assoc_response.type = WifiPacket::PacketType::AssociationResponse;
289 }
145 290
146 SendPacket(assoc_response); 291 SendPacket(assoc_response);
147} 292}
@@ -155,22 +300,40 @@ void SendAssociationResponseFrame(const MacAddress& address) {
155void HandleAuthenticationFrame(const Network::WifiPacket& packet) { 300void HandleAuthenticationFrame(const Network::WifiPacket& packet) {
156 // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior 301 // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
157 if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) { 302 if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
158 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost));
159
160 // Respond with an authentication response frame with SEQ2
161 using Network::WifiPacket; 303 using Network::WifiPacket;
162 WifiPacket auth_request; 304 WifiPacket auth_request;
163 auth_request.channel = network_channel; 305 {
164 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); 306 std::lock_guard<std::mutex> lock(connection_status_mutex);
165 auth_request.destination_address = packet.transmitter_address; 307 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
166 auth_request.type = WifiPacket::PacketType::Authentication; 308 LOG_ERROR(Service_NWM,
167 309 "Connection sequence aborted, because connection status is %u",
310 connection_status.status);
311 return;
312 }
313
314 // Respond with an authentication response frame with SEQ2
315 auth_request.channel = network_channel;
316 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
317 auth_request.destination_address = packet.transmitter_address;
318 auth_request.type = WifiPacket::PacketType::Authentication;
319 }
168 SendPacket(auth_request); 320 SendPacket(auth_request);
169 321
170 SendAssociationResponseFrame(packet.transmitter_address); 322 SendAssociationResponseFrame(packet.transmitter_address);
171 } 323 }
172} 324}
173 325
326static void HandleDataFrame(const Network::WifiPacket& packet) {
327 switch (GetFrameEtherType(packet.data)) {
328 case EtherType::EAPoL:
329 HandleEAPoLPacket(packet);
330 break;
331 case EtherType::SecureData:
332 // TODO(B3N30): Handle SecureData packets
333 break;
334 }
335}
336
174/// Callback to parse and handle a received wifi packet. 337/// Callback to parse and handle a received wifi packet.
175void OnWifiPacketReceived(const Network::WifiPacket& packet) { 338void OnWifiPacketReceived(const Network::WifiPacket& packet) {
176 switch (packet.type) { 339 switch (packet.type) {
@@ -180,6 +343,12 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) {
180 case Network::WifiPacket::PacketType::Authentication: 343 case Network::WifiPacket::PacketType::Authentication:
181 HandleAuthenticationFrame(packet); 344 HandleAuthenticationFrame(packet);
182 break; 345 break;
346 case Network::WifiPacket::PacketType::AssociationResponse:
347 HandleAssociationResponseFrame(packet);
348 break;
349 case Network::WifiPacket::PacketType::Data:
350 HandleDataFrame(packet);
351 break;
183 } 352 }
184} 353}
185 354
@@ -246,7 +415,7 @@ static void RecvBeaconBroadcastData(Interface* self) {
246 auto beacons = GetReceivedBeacons(mac_address); 415 auto beacons = GetReceivedBeacons(mac_address);
247 416
248 BeaconDataReplyHeader data_reply_header{}; 417 BeaconDataReplyHeader data_reply_header{};
249 data_reply_header.total_entries = beacons.size(); 418 data_reply_header.total_entries = static_cast<u32>(beacons.size());
250 data_reply_header.max_output_size = out_buffer_size; 419 data_reply_header.max_output_size = out_buffer_size;
251 420
252 Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader)); 421 Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader));
@@ -256,8 +425,8 @@ static void RecvBeaconBroadcastData(Interface* self) {
256 for (const auto& beacon : beacons) { 425 for (const auto& beacon : beacons) {
257 BeaconEntryHeader entry{}; 426 BeaconEntryHeader entry{};
258 // TODO(Subv): Figure out what this size is used for. 427 // TODO(Subv): Figure out what this size is used for.
259 entry.unk_size = sizeof(BeaconEntryHeader) + beacon.data.size(); 428 entry.unk_size = static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
260 entry.total_size = sizeof(BeaconEntryHeader) + beacon.data.size(); 429 entry.total_size = static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
261 entry.wifi_channel = beacon.channel; 430 entry.wifi_channel = beacon.channel;
262 entry.header_size = sizeof(BeaconEntryHeader); 431 entry.header_size = sizeof(BeaconEntryHeader);
263 entry.mac_address = beacon.transmitter_address; 432 entry.mac_address = beacon.transmitter_address;
@@ -268,9 +437,9 @@ static void RecvBeaconBroadcastData(Interface* self) {
268 current_buffer_pos += sizeof(BeaconEntryHeader); 437 current_buffer_pos += sizeof(BeaconEntryHeader);
269 438
270 Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size()); 439 Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size());
271 current_buffer_pos += beacon.data.size(); 440 current_buffer_pos += static_cast<VAddr>(beacon.data.size());
272 441
273 total_size += sizeof(BeaconEntryHeader) + beacon.data.size(); 442 total_size += static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
274 } 443 }
275 444
276 // Update the total size in the structure and write it to the buffer again. 445 // Update the total size in the structure and write it to the buffer again.
@@ -305,7 +474,7 @@ static void InitializeWithVersion(Interface* self) {
305 u32 sharedmem_size = rp.Pop<u32>(); 474 u32 sharedmem_size = rp.Pop<u32>();
306 475
307 // Update the node information with the data the game gave us. 476 // Update the node information with the data the game gave us.
308 rp.PopRaw(node_info[0]); 477 rp.PopRaw(current_node);
309 478
310 u16 version = rp.Pop<u16>(); 479 u16 version = rp.Pop<u16>();
311 480
@@ -315,10 +484,14 @@ static void InitializeWithVersion(Interface* self) {
315 484
316 ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); 485 ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size.");
317 486
318 // Reset the connection status, it contains all zeros after initialization, 487 {
319 // except for the actual status value. 488 std::lock_guard<std::mutex> lock(connection_status_mutex);
320 connection_status = {}; 489
321 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); 490 // Reset the connection status, it contains all zeros after initialization,
491 // except for the actual status value.
492 connection_status = {};
493 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
494 }
322 495
323 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); 496 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
324 rb.Push(RESULT_SUCCESS); 497 rb.Push(RESULT_SUCCESS);
@@ -348,12 +521,16 @@ static void GetConnectionStatus(Interface* self) {
348 IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); 521 IPC::RequestBuilder rb = rp.MakeBuilder(13, 0);
349 522
350 rb.Push(RESULT_SUCCESS); 523 rb.Push(RESULT_SUCCESS);
351 rb.PushRaw(connection_status); 524 {
352 525 std::lock_guard<std::mutex> lock(connection_status_mutex);
353 // Reset the bitmask of changed nodes after each call to this 526 rb.PushRaw(connection_status);
354 // function to prevent falsely informing games of outstanding 527
355 // changes in subsequent calls. 528 // Reset the bitmask of changed nodes after each call to this
356 connection_status.changed_nodes = 0; 529 // function to prevent falsely informing games of outstanding
530 // changes in subsequent calls.
531 // TODO(Subv): Find exactly where the NWM module resets this value.
532 connection_status.changed_nodes = 0;
533 }
357 534
358 LOG_DEBUG(Service_NWM, "called"); 535 LOG_DEBUG(Service_NWM, "called");
359} 536}
@@ -434,31 +611,36 @@ static void BeginHostingNetwork(Interface* self) {
434 // The real UDS module throws a fatal error if this assert fails. 611 // The real UDS module throws a fatal error if this assert fails.
435 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); 612 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member.");
436 613
437 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); 614 {
438 615 std::lock_guard<std::mutex> lock(connection_status_mutex);
439 // Ensure the application data size is less than the maximum value. 616 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost);
440 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); 617
441 618 // Ensure the application data size is less than the maximum value.
442 // Set up basic information for this network. 619 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize,
443 network_info.oui_value = NintendoOUI; 620 "Data size is too big.");
444 network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); 621
445 622 // Set up basic information for this network.
446 connection_status.max_nodes = network_info.max_nodes; 623 network_info.oui_value = NintendoOUI;
447 624 network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo);
448 // Resize the nodes list to hold max_nodes. 625
449 node_info.resize(network_info.max_nodes); 626 connection_status.max_nodes = network_info.max_nodes;
450 627
451 // There's currently only one node in the network (the host). 628 // Resize the nodes list to hold max_nodes.
452 connection_status.total_nodes = 1; 629 node_info.resize(network_info.max_nodes);
453 network_info.total_nodes = 1; 630
454 // The host is always the first node 631 // There's currently only one node in the network (the host).
455 connection_status.network_node_id = 1; 632 connection_status.total_nodes = 1;
456 node_info[0].network_node_id = 1; 633 network_info.total_nodes = 1;
457 connection_status.nodes[0] = connection_status.network_node_id; 634 // The host is always the first node
458 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. 635 connection_status.network_node_id = 1;
459 connection_status.node_bitmask |= 1; 636 current_node.network_node_id = 1;
460 // Notify the application that the first node was set. 637 connection_status.nodes[0] = connection_status.network_node_id;
461 connection_status.changed_nodes |= 1; 638 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken.
639 connection_status.node_bitmask |= 1;
640 // Notify the application that the first node was set.
641 connection_status.changed_nodes |= 1;
642 node_info[0] = current_node;
643 }
462 644
463 // If the game has a preferred channel, use that instead. 645 // If the game has a preferred channel, use that instead.
464 if (network_info.channel != 0) 646 if (network_info.channel != 0)
@@ -495,9 +677,13 @@ static void DestroyNetwork(Interface* self) {
495 // Unschedule the beacon broadcast event. 677 // Unschedule the beacon broadcast event.
496 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); 678 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
497 679
498 // TODO(Subv): Check if connection_status is indeed reset after this call. 680 {
499 connection_status = {}; 681 std::lock_guard<std::mutex> lock(connection_status_mutex);
500 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); 682
683 // TODO(Subv): Check if connection_status is indeed reset after this call.
684 connection_status = {};
685 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected);
686 }
501 connection_status_event->Signal(); 687 connection_status_event->Signal();
502 688
503 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 689 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@@ -540,17 +726,24 @@ static void SendTo(Interface* self) {
540 726
541 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 727 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
542 728
543 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && 729 u16 network_node_id;
544 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
545 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
546 ErrorSummary::InvalidState, ErrorLevel::Status));
547 return;
548 }
549 730
550 if (dest_node_id == connection_status.network_node_id) { 731 {
551 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, 732 std::lock_guard<std::mutex> lock(connection_status_mutex);
552 ErrorSummary::WrongArgument, ErrorLevel::Status)); 733 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) &&
553 return; 734 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
735 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
736 ErrorSummary::InvalidState, ErrorLevel::Status));
737 return;
738 }
739
740 if (dest_node_id == connection_status.network_node_id) {
741 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
742 ErrorSummary::WrongArgument, ErrorLevel::Status));
743 return;
744 }
745
746 network_node_id = connection_status.network_node_id;
554 } 747 }
555 748
556 // TODO(Subv): Do something with the flags. 749 // TODO(Subv): Do something with the flags.
@@ -567,8 +760,8 @@ static void SendTo(Interface* self) {
567 760
568 // TODO(Subv): Increment the sequence number after each sent packet. 761 // TODO(Subv): Increment the sequence number after each sent packet.
569 u16 sequence_number = 0; 762 u16 sequence_number = 0;
570 std::vector<u8> data_payload = GenerateDataPayload( 763 std::vector<u8> data_payload =
571 data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); 764 GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number);
572 765
573 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt 766 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt
574 // and encapsulate the payload. 767 // and encapsulate the payload.
@@ -595,6 +788,7 @@ static void GetChannel(Interface* self) {
595 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); 788 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0);
596 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); 789 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
597 790
791 std::lock_guard<std::mutex> lock(connection_status_mutex);
598 bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected); 792 bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected);
599 793
600 u8 channel = is_connected ? network_channel : 0; 794 u8 channel = is_connected ? network_channel : 0;
@@ -766,6 +960,7 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
766 * @param network_node_id Network Node Id of the connecting client. 960 * @param network_node_id Network Node Id of the connecting client.
767 */ 961 */
768void OnClientConnected(u16 network_node_id) { 962void OnClientConnected(u16 network_node_id) {
963 std::lock_guard<std::mutex> lock(connection_status_mutex);
769 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), 964 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
770 "Can not accept clients if we're not hosting a network"); 965 "Can not accept clients if we're not hosting a network");
771 ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, 966 ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes,
@@ -827,8 +1022,11 @@ NWM_UDS::~NWM_UDS() {
827 connection_status_event = nullptr; 1022 connection_status_event = nullptr;
828 recv_buffer_memory = nullptr; 1023 recv_buffer_memory = nullptr;
829 1024
830 connection_status = {}; 1025 {
831 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); 1026 std::lock_guard<std::mutex> lock(connection_status_mutex);
1027 connection_status = {};
1028 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
1029 }
832 1030
833 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); 1031 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
834} 1032}
diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp
index 552eaf65e..73a80d940 100644
--- a/src/core/hle/service/nwm/uds_beacon.cpp
+++ b/src/core/hle/service/nwm/uds_beacon.cpp
@@ -243,7 +243,7 @@ std::vector<u8> GenerateNintendoFirstEncryptedDataTag(const NetworkInfo& network
243 243
244 EncryptedDataTag tag{}; 244 EncryptedDataTag tag{};
245 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); 245 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
246 tag.header.length = sizeof(tag) - sizeof(TagHeader) + payload_size; 246 tag.header.length = static_cast<u8>(sizeof(tag) - sizeof(TagHeader) + payload_size);
247 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData0); 247 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData0);
248 tag.oui = NintendoOUI; 248 tag.oui = NintendoOUI;
249 249
@@ -279,7 +279,7 @@ std::vector<u8> GenerateNintendoSecondEncryptedDataTag(const NetworkInfo& networ
279 279
280 EncryptedDataTag tag{}; 280 EncryptedDataTag tag{};
281 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); 281 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
282 tag.header.length = tag_length; 282 tag.header.length = static_cast<u8>(tag_length);
283 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData1); 283 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData1);
284 tag.oui = NintendoOUI; 284 tag.oui = NintendoOUI;
285 285
diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp
index c8a76ec2a..c74f51253 100644
--- a/src/core/hle/service/nwm/uds_connection.cpp
+++ b/src/core/hle/service/nwm/uds_connection.cpp
@@ -75,5 +75,14 @@ std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_i
75 return data; 75 return data;
76} 76}
77 77
78std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body) {
79 AssociationResponseFrame frame;
80 memcpy(&frame, body.data(), sizeof(frame));
81
82 constexpr u16 AssociationIdMask = 0x3FFF;
83 return std::make_tuple(static_cast<AssocStatus>(frame.status_code),
84 frame.assoc_id & AssociationIdMask);
85}
86
78} // namespace NWM 87} // namespace NWM
79} // namespace Service 88} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h
index 73f55a4fd..a664f8471 100644
--- a/src/core/hle/service/nwm/uds_connection.h
+++ b/src/core/hle/service/nwm/uds_connection.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <tuple>
7#include <vector> 8#include <vector>
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/swap.h" 10#include "common/swap.h"
@@ -47,5 +48,9 @@ AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body);
47/// network id, starting at the frame body. 48/// network id, starting at the frame body.
48std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); 49std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id);
49 50
51/// Returns a tuple of (association status, association id) from the body of an AssociationResponse
52/// frame.
53std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body);
54
50} // namespace NWM 55} // namespace NWM
51} // namespace Service 56} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp
index 8c6742dba..4b389710f 100644
--- a/src/core/hle/service/nwm/uds_data.cpp
+++ b/src/core/hle/service/nwm/uds_data.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 <algorithm>
5#include <cstring> 6#include <cstring>
6#include <cryptopp/aes.h> 7#include <cryptopp/aes.h>
7#include <cryptopp/ccm.h> 8#include <cryptopp/ccm.h>
@@ -197,7 +198,7 @@ static std::vector<u8> DecryptDataFrame(const std::vector<u8>& encrypted_payload
197 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); 198 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL);
198 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); 199 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
199 200
200 int size = df.MaxRetrievable(); 201 size_t size = df.MaxRetrievable();
201 202
202 std::vector<u8> pdata(size); 203 std::vector<u8> pdata(size);
203 df.Get(pdata.data(), size); 204 df.Get(pdata.data(), size);
@@ -251,7 +252,7 @@ static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload,
251 252
252 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); 253 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
253 254
254 int size = df.MaxRetrievable(); 255 size_t size = df.MaxRetrievable();
255 256
256 std::vector<u8> cipher(size); 257 std::vector<u8> cipher(size);
257 df.Get(cipher.data(), size); 258 df.Get(cipher.data(), size);
@@ -266,13 +267,107 @@ static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload,
266std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, 267std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
267 u16 src_node, u16 sequence_number) { 268 u16 src_node, u16 sequence_number) {
268 std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData); 269 std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData);
269 std::vector<u8> securedata_header = 270 std::vector<u8> securedata_header = GenerateSecureDataHeader(
270 GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number); 271 static_cast<u16>(data.size()), channel, dest_node, src_node, sequence_number);
271 272
272 buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end()); 273 buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end());
273 buffer.insert(buffer.end(), data.begin(), data.end()); 274 buffer.insert(buffer.end(), data.begin(), data.end());
274 return buffer; 275 return buffer;
275} 276}
276 277
278std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) {
279 EAPoLStartPacket eapol_start{};
280 eapol_start.association_id = association_id;
281 eapol_start.node.friend_code_seed = node_info.friend_code_seed;
282
283 std::copy(node_info.username.begin(), node_info.username.end(),
284 eapol_start.node.username.begin());
285
286 // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module.
287 // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in
288 // EAPoL-Start packets from different 3DSs to the same host during a Super Smash Bros. 4 game.
289 // Find out what that means.
290
291 std::vector<u8> eapol_buffer(sizeof(EAPoLStartPacket));
292 std::memcpy(eapol_buffer.data(), &eapol_start, sizeof(eapol_start));
293
294 std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL);
295 buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end());
296 return buffer;
297}
298
299EtherType GetFrameEtherType(const std::vector<u8>& frame) {
300 LLCHeader header;
301 std::memcpy(&header, frame.data(), sizeof(header));
302
303 u16 ethertype = header.protocol;
304 return static_cast<EtherType>(ethertype);
305}
306
307u16 GetEAPoLFrameType(const std::vector<u8>& frame) {
308 // Ignore the LLC header
309 u16_be eapol_type;
310 std::memcpy(&eapol_type, frame.data() + sizeof(LLCHeader), sizeof(eapol_type));
311 return eapol_type;
312}
313
314NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame) {
315 EAPoLStartPacket eapol_start;
316
317 // Skip the LLC header
318 std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start));
319
320 NodeInfo node{};
321 node.friend_code_seed = eapol_start.node.friend_code_seed;
322
323 std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(),
324 node.username.begin());
325
326 return node;
327}
328
329NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) {
330 NodeInfo node_info{};
331 node_info.friend_code_seed = node.friend_code_seed;
332 node_info.network_node_id = node.network_node_id;
333
334 std::copy(node.username.begin(), node.username.end(), node_info.username.begin());
335
336 return node_info;
337}
338
339std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id,
340 const NodeList& nodes, u8 max_nodes, u8 total_nodes) {
341 EAPoLLogoffPacket eapol_logoff{};
342 eapol_logoff.assigned_node_id = network_node_id;
343 eapol_logoff.connected_nodes = total_nodes;
344 eapol_logoff.max_nodes = max_nodes;
345
346 for (size_t index = 0; index < total_nodes; ++index) {
347 const auto& node_info = nodes[index];
348 auto& node = eapol_logoff.nodes[index];
349
350 node.friend_code_seed = node_info.friend_code_seed;
351 node.network_node_id = node_info.network_node_id;
352
353 std::copy(node_info.username.begin(), node_info.username.end(), node.username.begin());
354 }
355
356 std::vector<u8> eapol_buffer(sizeof(EAPoLLogoffPacket));
357 std::memcpy(eapol_buffer.data(), &eapol_logoff, sizeof(eapol_logoff));
358
359 std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL);
360 buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end());
361 return buffer;
362}
363
364EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame) {
365 EAPoLLogoffPacket eapol_logoff;
366
367 // Skip the LLC header
368 std::memcpy(&eapol_logoff, frame.data() + sizeof(LLCHeader), sizeof(eapol_logoff));
369 return eapol_logoff;
370}
371
277} // namespace NWM 372} // namespace NWM
278} // namespace Service 373} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h
index a23520a41..76bccb1bf 100644
--- a/src/core/hle/service/nwm/uds_data.h
+++ b/src/core/hle/service/nwm/uds_data.h
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hle/service/nwm/uds_beacon.h"
11#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
12 13
13namespace Service { 14namespace Service {
@@ -67,6 +68,49 @@ struct DataFrameCryptoCTR {
67 68
68static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); 69static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size");
69 70
71struct EAPoLNodeInfo {
72 u64_be friend_code_seed;
73 std::array<u16_be, 10> username;
74 INSERT_PADDING_BYTES(4);
75 u16_be network_node_id;
76 INSERT_PADDING_BYTES(6);
77};
78
79static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size");
80
81constexpr u16 EAPoLStartMagic = 0x201;
82
83/*
84 * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host
85 */
86struct EAPoLStartPacket {
87 u16_be magic = EAPoLStartMagic;
88 u16_be association_id;
89 // This value is hardcoded to 1 in the NWM module.
90 u16_be unknown = 1;
91 INSERT_PADDING_BYTES(2);
92 EAPoLNodeInfo node;
93};
94
95static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size");
96
97constexpr u16 EAPoLLogoffMagic = 0x202;
98
99struct EAPoLLogoffPacket {
100 u16_be magic = EAPoLLogoffMagic;
101 INSERT_PADDING_BYTES(2);
102 u16_be assigned_node_id;
103 MacAddress client_mac_address;
104 INSERT_PADDING_BYTES(6);
105 u8 connected_nodes;
106 u8 max_nodes;
107 INSERT_PADDING_BYTES(4);
108
109 std::array<EAPoLNodeInfo, UDSMaxNodes> nodes;
110};
111
112static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wrong size");
113
70/** 114/**
71 * Generates an unencrypted 802.11 data payload. 115 * Generates an unencrypted 802.11 data payload.
72 * @returns The generated frame payload. 116 * @returns The generated frame payload.
@@ -74,5 +118,47 @@ static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wron
74std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, 118std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
75 u16 src_node, u16 sequence_number); 119 u16 src_node, u16 sequence_number);
76 120
121/*
122 * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS
123 * communication.
124 * @returns The generated frame body.
125 */
126std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info);
127
128/*
129 * Returns the EtherType of the specified 802.11 frame.
130 */
131EtherType GetFrameEtherType(const std::vector<u8>& frame);
132
133/*
134 * Returns the EAPoL type (Start / Logoff) of the specified 802.11 frame.
135 * Note: The frame *must* be an EAPoL frame.
136 */
137u16 GetEAPoLFrameType(const std::vector<u8>& frame);
138
139/*
140 * Returns a deserialized NodeInfo structure from the information inside an EAPoL-Start packet
141 * encapsulated in an 802.11 data frame.
142 */
143NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame);
144
145/*
146 * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo.
147 */
148NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node);
149
150/*
151 * Generates an unencrypted 802.11 data frame body with the EAPoL-Logoff format for UDS
152 * communication.
153 * @returns The generated frame body.
154 */
155std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id,
156 const NodeList& nodes, u8 max_nodes, u8 total_nodes);
157
158/*
159 * Returns a EAPoLLogoffPacket representing the specified 802.11-encapsulated data frame.
160 */
161EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame);
162
77} // namespace NWM 163} // namespace NWM
78} // namespace Service 164} // namespace Service
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 5e7fc68f9..854ab9a05 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -36,6 +36,10 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService
36 std::string name, unsigned int max_sessions) { 36 std::string name, unsigned int max_sessions) {
37 37
38 CASCADE_CODE(ValidateServiceName(name)); 38 CASCADE_CODE(ValidateServiceName(name));
39
40 if (registered_services.find(name) != registered_services.end())
41 return ERR_ALREADY_REGISTERED;
42
39 Kernel::SharedPtr<Kernel::ServerPort> server_port; 43 Kernel::SharedPtr<Kernel::ServerPort> server_port;
40 Kernel::SharedPtr<Kernel::ClientPort> client_port; 44 Kernel::SharedPtr<Kernel::ClientPort> client_port;
41 std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name); 45 std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name);
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 8f0dbf2db..9f60a7965 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -32,6 +32,9 @@ constexpr ResultCode ERR_ACCESS_DENIED(6, ErrorModule::SRV, ErrorSummary::Invali
32 ErrorLevel::Permanent); // 0xD8E06406 32 ErrorLevel::Permanent); // 0xD8E06406
33constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, 33constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument,
34 ErrorLevel::Permanent); // 0xD9006407 34 ErrorLevel::Permanent); // 0xD9006407
35constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorDescription::AlreadyExists, ErrorModule::OS,
36 ErrorSummary::WrongArgument,
37 ErrorLevel::Permanent); // 0xD9001BFC
35 38
36class ServiceManager { 39class ServiceManager {
37public: 40public:
diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp
index 352941e69..5c955cf54 100644
--- a/src/core/hle/service/sm/srv.cpp
+++ b/src/core/hle/service/sm/srv.cpp
@@ -13,6 +13,7 @@
13#include "core/hle/kernel/errors.h" 13#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/hle_ipc.h" 14#include "core/hle/kernel/hle_ipc.h"
15#include "core/hle/kernel/semaphore.h" 15#include "core/hle/kernel/semaphore.h"
16#include "core/hle/kernel/server_port.h"
16#include "core/hle/kernel/server_session.h" 17#include "core/hle/kernel/server_session.h"
17#include "core/hle/service/sm/sm.h" 18#include "core/hle/service/sm/sm.h"
18#include "core/hle/service/sm/srv.h" 19#include "core/hle/service/sm/srv.h"
@@ -184,12 +185,35 @@ void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) {
184 flags); 185 flags);
185} 186}
186 187
188void SRV::RegisterService(Kernel::HLERequestContext& ctx) {
189 IPC::RequestParser rp(ctx, 0x3, 4, 0);
190
191 auto name_buf = rp.PopRaw<std::array<char, 8>>();
192 size_t name_len = rp.Pop<u32>();
193 u32 max_sessions = rp.Pop<u32>();
194
195 std::string name(name_buf.data(), std::min(name_len, name_buf.size()));
196
197 auto port = service_manager->RegisterService(name, max_sessions);
198
199 if (port.Failed()) {
200 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
201 rb.Push(port.Code());
202 LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), port.Code().raw);
203 return;
204 }
205
206 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
207 rb.Push(RESULT_SUCCESS);
208 rb.PushObjects(port.Unwrap());
209}
210
187SRV::SRV(std::shared_ptr<ServiceManager> service_manager) 211SRV::SRV(std::shared_ptr<ServiceManager> service_manager)
188 : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) { 212 : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) {
189 static const FunctionInfo functions[] = { 213 static const FunctionInfo functions[] = {
190 {0x00010002, &SRV::RegisterClient, "RegisterClient"}, 214 {0x00010002, &SRV::RegisterClient, "RegisterClient"},
191 {0x00020000, &SRV::EnableNotification, "EnableNotification"}, 215 {0x00020000, &SRV::EnableNotification, "EnableNotification"},
192 {0x00030100, nullptr, "RegisterService"}, 216 {0x00030100, &SRV::RegisterService, "RegisterService"},
193 {0x000400C0, nullptr, "UnregisterService"}, 217 {0x000400C0, nullptr, "UnregisterService"},
194 {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"}, 218 {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"},
195 {0x000600C2, nullptr, "RegisterPort"}, 219 {0x000600C2, nullptr, "RegisterPort"},
diff --git a/src/core/hle/service/sm/srv.h b/src/core/hle/service/sm/srv.h
index 75cca5184..aad839563 100644
--- a/src/core/hle/service/sm/srv.h
+++ b/src/core/hle/service/sm/srv.h
@@ -28,6 +28,7 @@ private:
28 void Subscribe(Kernel::HLERequestContext& ctx); 28 void Subscribe(Kernel::HLERequestContext& ctx);
29 void Unsubscribe(Kernel::HLERequestContext& ctx); 29 void Unsubscribe(Kernel::HLERequestContext& ctx);
30 void PublishToSubscriber(Kernel::HLERequestContext& ctx); 30 void PublishToSubscriber(Kernel::HLERequestContext& ctx);
31 void RegisterService(Kernel::HLERequestContext& ctx);
31 32
32 std::shared_ptr<ServiceManager> service_manager; 33 std::shared_ptr<ServiceManager> service_manager;
33 Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore; 34 Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore;
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index dfc36748c..6be5db13f 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -271,6 +271,24 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds)
271 // Create an event to wake the thread up after the specified nanosecond delay has passed 271 // Create an event to wake the thread up after the specified nanosecond delay has passed
272 thread->WakeAfterDelay(nano_seconds); 272 thread->WakeAfterDelay(nano_seconds);
273 273
274 thread->wakeup_callback = [](ThreadWakeupReason reason,
275 Kernel::SharedPtr<Kernel::Thread> thread,
276 Kernel::SharedPtr<Kernel::WaitObject> object) {
277
278 ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
279
280 if (reason == ThreadWakeupReason::Timeout) {
281 thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT);
282 return;
283 }
284
285 ASSERT(reason == ThreadWakeupReason::Signal);
286 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
287
288 // WaitSynchronization1 doesn't have an output index like WaitSynchronizationN, so we
289 // don't have to do anything else here.
290 };
291
274 Core::System::GetInstance().PrepareReschedule(); 292 Core::System::GetInstance().PrepareReschedule();
275 293
276 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread 294 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread
@@ -344,6 +362,23 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
344 // Create an event to wake the thread up after the specified nanosecond delay has passed 362 // Create an event to wake the thread up after the specified nanosecond delay has passed
345 thread->WakeAfterDelay(nano_seconds); 363 thread->WakeAfterDelay(nano_seconds);
346 364
365 thread->wakeup_callback = [](ThreadWakeupReason reason,
366 Kernel::SharedPtr<Kernel::Thread> thread,
367 Kernel::SharedPtr<Kernel::WaitObject> object) {
368
369 ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ALL);
370
371 if (reason == ThreadWakeupReason::Timeout) {
372 thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT);
373 return;
374 }
375
376 ASSERT(reason == ThreadWakeupReason::Signal);
377
378 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
379 // The wait_all case does not update the output index.
380 };
381
347 Core::System::GetInstance().PrepareReschedule(); 382 Core::System::GetInstance().PrepareReschedule();
348 383
349 // This value gets set to -1 by default in this case, it is not modified after this. 384 // This value gets set to -1 by default in this case, it is not modified after this.
@@ -361,7 +396,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
361 // We found a ready object, acquire it and set the result value 396 // We found a ready object, acquire it and set the result value
362 Kernel::WaitObject* object = itr->get(); 397 Kernel::WaitObject* object = itr->get();
363 object->Acquire(thread); 398 object->Acquire(thread);
364 *out = std::distance(objects.begin(), itr); 399 *out = static_cast<s32>(std::distance(objects.begin(), itr));
365 return RESULT_SUCCESS; 400 return RESULT_SUCCESS;
366 } 401 }
367 402
@@ -389,12 +424,28 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
389 // Create an event to wake the thread up after the specified nanosecond delay has passed 424 // Create an event to wake the thread up after the specified nanosecond delay has passed
390 thread->WakeAfterDelay(nano_seconds); 425 thread->WakeAfterDelay(nano_seconds);
391 426
427 thread->wakeup_callback = [](ThreadWakeupReason reason,
428 Kernel::SharedPtr<Kernel::Thread> thread,
429 Kernel::SharedPtr<Kernel::WaitObject> object) {
430
431 ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
432
433 if (reason == ThreadWakeupReason::Timeout) {
434 thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT);
435 return;
436 }
437
438 ASSERT(reason == ThreadWakeupReason::Signal);
439
440 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
441 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get()));
442 };
443
392 Core::System::GetInstance().PrepareReschedule(); 444 Core::System::GetInstance().PrepareReschedule();
393 445
394 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a 446 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
395 // signal in one of its wait objects. 447 // signal in one of its wait objects.
396 // Otherwise we retain the default value of timeout, and -1 in the out parameter 448 // Otherwise we retain the default value of timeout, and -1 in the out parameter
397 thread->wait_set_output = true;
398 *out = -1; 449 *out = -1;
399 return Kernel::RESULT_TIMEOUT; 450 return Kernel::RESULT_TIMEOUT;
400 } 451 }
@@ -469,7 +520,7 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl
469 // We found a ready object, acquire it and set the result value 520 // We found a ready object, acquire it and set the result value
470 Kernel::WaitObject* object = itr->get(); 521 Kernel::WaitObject* object = itr->get();
471 object->Acquire(thread); 522 object->Acquire(thread);
472 *index = std::distance(objects.begin(), itr); 523 *index = static_cast<s32>(std::distance(objects.begin(), itr));
473 524
474 if (object->GetHandleType() == Kernel::HandleType::ServerSession) { 525 if (object->GetHandleType() == Kernel::HandleType::ServerSession) {
475 auto server_session = static_cast<Kernel::ServerSession*>(object); 526 auto server_session = static_cast<Kernel::ServerSession*>(object);
@@ -483,8 +534,6 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl
483 534
484 // No objects were ready to be acquired, prepare to suspend the thread. 535 // No objects were ready to be acquired, prepare to suspend the thread.
485 536
486 // TODO(Subv): Perform IPC translation upon wakeup.
487
488 // Put the thread to sleep 537 // Put the thread to sleep
489 thread->status = THREADSTATUS_WAIT_SYNCH_ANY; 538 thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
490 539
@@ -496,12 +545,24 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl
496 545
497 thread->wait_objects = std::move(objects); 546 thread->wait_objects = std::move(objects);
498 547
548 thread->wakeup_callback = [](ThreadWakeupReason reason,
549 Kernel::SharedPtr<Kernel::Thread> thread,
550 Kernel::SharedPtr<Kernel::WaitObject> object) {
551
552 ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
553 ASSERT(reason == ThreadWakeupReason::Signal);
554
555 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
556 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get()));
557
558 // TODO(Subv): Perform IPC translation upon wakeup.
559 };
560
499 Core::System::GetInstance().PrepareReschedule(); 561 Core::System::GetInstance().PrepareReschedule();
500 562
501 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a 563 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
502 // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. 564 // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error.
503 // By default the index is set to -1. 565 // By default the index is set to -1.
504 thread->wait_set_output = true;
505 *index = -1; 566 *index = -1;
506 return RESULT_SUCCESS; 567 return RESULT_SUCCESS;
507} 568}
@@ -656,8 +717,9 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, u32 priority, u32 ent
656 "Newly created thread must run in the SysCore (Core1), unimplemented."); 717 "Newly created thread must run in the SysCore (Core1), unimplemented.");
657 } 718 }
658 719
659 CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(name, entry_point, priority, 720 CASCADE_RESULT(SharedPtr<Thread> thread,
660 arg, processor_id, stack_top)); 721 Kernel::Thread::Create(name, entry_point, priority, arg, processor_id, stack_top,
722 Kernel::g_current_process));
661 723
662 thread->context.fpscr = 724 thread->context.fpscr =
663 FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000 725 FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000
@@ -682,7 +744,7 @@ static void ExitThread() {
682} 744}
683 745
684/// Gets the priority for the specified thread 746/// Gets the priority for the specified thread
685static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { 747static ResultCode GetThreadPriority(u32* priority, Kernel::Handle handle) {
686 const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); 748 const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
687 if (thread == nullptr) 749 if (thread == nullptr)
688 return ERR_INVALID_HANDLE; 750 return ERR_INVALID_HANDLE;
@@ -692,7 +754,7 @@ static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) {
692} 754}
693 755
694/// Sets the priority for the specified thread 756/// Sets the priority for the specified thread
695static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { 757static ResultCode SetThreadPriority(Kernel::Handle handle, u32 priority) {
696 if (priority > THREADPRIO_LOWEST) { 758 if (priority > THREADPRIO_LOWEST) {
697 return Kernel::ERR_OUT_OF_RANGE; 759 return Kernel::ERR_OUT_OF_RANGE;
698 } 760 }
@@ -977,7 +1039,7 @@ static void SleepThread(s64 nanoseconds) {
977static s64 GetSystemTick() { 1039static s64 GetSystemTick() {
978 s64 result = CoreTiming::GetTicks(); 1040 s64 result = CoreTiming::GetTicks();
979 // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. 1041 // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end.
980 Core::CPU().AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b 1042 CoreTiming::AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b
981 return result; 1043 return result;
982} 1044}
983 1045
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 69cdc0867..918038f1e 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -91,8 +91,8 @@ static u32 TranslateAddr(u32 addr, const THREEloadinfo* loadinfo, u32* offsets)
91 return loadinfo->seg_addrs[2] + addr - offsets[1]; 91 return loadinfo->seg_addrs[2] + addr - offsets[1];
92} 92}
93 93
94using Kernel::SharedPtr;
95using Kernel::CodeSet; 94using Kernel::CodeSet;
95using Kernel::SharedPtr;
96 96
97static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, 97static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr,
98 SharedPtr<CodeSet>* out_codeset) { 98 SharedPtr<CodeSet>* out_codeset) {
@@ -255,7 +255,7 @@ FileType AppLoader_THREEDSX::IdentifyType(FileUtil::IOFile& file) {
255 return FileType::Error; 255 return FileType::Error;
256} 256}
257 257
258ResultStatus AppLoader_THREEDSX::Load() { 258ResultStatus AppLoader_THREEDSX::Load(Kernel::SharedPtr<Kernel::Process>& process) {
259 if (is_loaded) 259 if (is_loaded)
260 return ResultStatus::ErrorAlreadyLoaded; 260 return ResultStatus::ErrorAlreadyLoaded;
261 261
@@ -267,19 +267,17 @@ ResultStatus AppLoader_THREEDSX::Load() {
267 return ResultStatus::Error; 267 return ResultStatus::Error;
268 codeset->name = filename; 268 codeset->name = filename;
269 269
270 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); 270 process = Kernel::Process::Create(std::move(codeset));
271 Kernel::g_current_process->svc_access_mask.set(); 271 process->svc_access_mask.set();
272 Kernel::g_current_process->address_mappings = default_address_mappings; 272 process->address_mappings = default_address_mappings;
273 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
274 273
275 // Attach the default resource limit (APPLICATION) to the process 274 // Attach the default resource limit (APPLICATION) to the process
276 Kernel::g_current_process->resource_limit = 275 process->resource_limit =
277 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); 276 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
278 277
279 Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); 278 process->Run(48, Kernel::DEFAULT_STACK_SIZE);
280 279
281 Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_SelfNCCH>(*this), 280 Service::FS::RegisterSelfNCCH(*this);
282 Service::FS::ArchiveIdCode::SelfNCCH);
283 281
284 is_loaded = true; 282 is_loaded = true;
285 return ResultStatus::Success; 283 return ResultStatus::Success;
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h
index 3f376778a..1e59bbb9d 100644
--- a/src/core/loader/3dsx.h
+++ b/src/core/loader/3dsx.h
@@ -31,7 +31,7 @@ public:
31 return IdentifyType(file); 31 return IdentifyType(file);
32 } 32 }
33 33
34 ResultStatus Load() override; 34 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
35 35
36 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 36 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
37 37
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 2f27606a1..e36e42120 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -13,8 +13,8 @@
13#include "core/loader/elf.h" 13#include "core/loader/elf.h"
14#include "core/memory.h" 14#include "core/memory.h"
15 15
16using Kernel::SharedPtr;
17using Kernel::CodeSet; 16using Kernel::CodeSet;
17using Kernel::SharedPtr;
18 18
19//////////////////////////////////////////////////////////////////////////////////////////////////// 19////////////////////////////////////////////////////////////////////////////////////////////////////
20// ELF Header Constants 20// ELF Header Constants
@@ -375,7 +375,7 @@ FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) {
375 return FileType::Error; 375 return FileType::Error;
376} 376}
377 377
378ResultStatus AppLoader_ELF::Load() { 378ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
379 if (is_loaded) 379 if (is_loaded)
380 return ResultStatus::ErrorAlreadyLoaded; 380 return ResultStatus::ErrorAlreadyLoaded;
381 381
@@ -394,16 +394,15 @@ ResultStatus AppLoader_ELF::Load() {
394 SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); 394 SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
395 codeset->name = filename; 395 codeset->name = filename;
396 396
397 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); 397 process = Kernel::Process::Create(std::move(codeset));
398 Kernel::g_current_process->svc_access_mask.set(); 398 process->svc_access_mask.set();
399 Kernel::g_current_process->address_mappings = default_address_mappings; 399 process->address_mappings = default_address_mappings;
400 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
401 400
402 // Attach the default resource limit (APPLICATION) to the process 401 // Attach the default resource limit (APPLICATION) to the process
403 Kernel::g_current_process->resource_limit = 402 process->resource_limit =
404 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); 403 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
405 404
406 Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); 405 process->Run(48, Kernel::DEFAULT_STACK_SIZE);
407 406
408 is_loaded = true; 407 is_loaded = true;
409 return ResultStatus::Success; 408 return ResultStatus::Success;
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index 862aa90d8..113da5917 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -30,7 +30,7 @@ public:
30 return IdentifyType(file); 30 return IdentifyType(file);
31 } 31 }
32 32
33 ResultStatus Load() override; 33 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
34 34
35private: 35private:
36 std::string filename; 36 std::string filename;
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index e731888a2..82b2be6a3 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -13,10 +13,12 @@
13#include <boost/optional.hpp> 13#include <boost/optional.hpp>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/file_util.h" 15#include "common/file_util.h"
16#include "core/hle/kernel/kernel.h"
16 17
17namespace Kernel { 18namespace Kernel {
18struct AddressMapping; 19struct AddressMapping;
19} 20class Process;
21} // namespace Kernel
20 22
21//////////////////////////////////////////////////////////////////////////////////////////////////// 23////////////////////////////////////////////////////////////////////////////////////////////////////
22// Loader namespace 24// Loader namespace
@@ -92,10 +94,11 @@ public:
92 virtual FileType GetFileType() = 0; 94 virtual FileType GetFileType() = 0;
93 95
94 /** 96 /**
95 * Load the application 97 * Load the application and return the created Process instance
96 * @return ResultStatus result of function 98 * @param process The newly created process.
99 * @return The status result of the operation.
97 */ 100 */
98 virtual ResultStatus Load() = 0; 101 virtual ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) = 0;
99 102
100 /** 103 /**
101 * Loads the system mode that this application needs. 104 * Loads the system mode that this application needs.
@@ -167,6 +170,19 @@ public:
167 } 170 }
168 171
169 /** 172 /**
173 * Get the update RomFS of the application
174 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
175 * @param romfs_file The file containing the RomFS
176 * @param offset The offset the romfs begins on
177 * @param size The size of the romfs
178 * @return ResultStatus result of function
179 */
180 virtual ResultStatus ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
181 u64& size) {
182 return ResultStatus::ErrorNotImplemented;
183 }
184
185 /**
170 * Get the title of the application 186 * Get the title of the application
171 * @param title Reference to store the application title into 187 * @param title Reference to store the application title into
172 * @return ResultStatus result of function 188 * @return ResultStatus result of function
@@ -193,4 +209,4 @@ extern const std::initializer_list<Kernel::AddressMapping> default_address_mappi
193 */ 209 */
194std::unique_ptr<AppLoader> GetLoader(const std::string& filename); 210std::unique_ptr<AppLoader> GetLoader(const std::string& filename);
195 211
196} // namespace 212} // namespace Loader
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 79ea50147..66bc5823d 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -13,6 +13,7 @@
13#include "common/swap.h" 13#include "common/swap.h"
14#include "core/core.h" 14#include "core/core.h"
15#include "core/file_sys/archive_selfncch.h" 15#include "core/file_sys/archive_selfncch.h"
16#include "core/file_sys/ncch_container.h"
16#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/resource_limit.h" 18#include "core/hle/kernel/resource_limit.h"
18#include "core/hle/service/cfg/cfg.h" 19#include "core/hle/service/cfg/cfg.h"
@@ -27,87 +28,7 @@
27 28
28namespace Loader { 29namespace Loader {
29 30
30static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs 31static const u64 UPDATE_MASK = 0x0000000e00000000;
31static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
32
33/**
34 * Get the decompressed size of an LZSS compressed ExeFS file
35 * @param buffer Buffer of compressed file
36 * @param size Size of compressed buffer
37 * @return Size of decompressed buffer
38 */
39static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) {
40 u32 offset_size = *(u32*)(buffer + size - 4);
41 return offset_size + size;
42}
43
44/**
45 * Decompress ExeFS file (compressed with LZSS)
46 * @param compressed Compressed buffer
47 * @param compressed_size Size of compressed buffer
48 * @param decompressed Decompressed buffer
49 * @param decompressed_size Size of decompressed buffer
50 * @return True on success, otherwise false
51 */
52static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed,
53 u32 decompressed_size) {
54 const u8* footer = compressed + compressed_size - 8;
55 u32 buffer_top_and_bottom = *reinterpret_cast<const u32*>(footer);
56 u32 out = decompressed_size;
57 u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF);
58 u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF);
59
60 memset(decompressed, 0, decompressed_size);
61 memcpy(decompressed, compressed, compressed_size);
62
63 while (index > stop_index) {
64 u8 control = compressed[--index];
65
66 for (unsigned i = 0; i < 8; i++) {
67 if (index <= stop_index)
68 break;
69 if (index <= 0)
70 break;
71 if (out <= 0)
72 break;
73
74 if (control & 0x80) {
75 // Check if compression is out of bounds
76 if (index < 2)
77 return false;
78 index -= 2;
79
80 u32 segment_offset = compressed[index] | (compressed[index + 1] << 8);
81 u32 segment_size = ((segment_offset >> 12) & 15) + 3;
82 segment_offset &= 0x0FFF;
83 segment_offset += 2;
84
85 // Check if compression is out of bounds
86 if (out < segment_size)
87 return false;
88
89 for (unsigned j = 0; j < segment_size; j++) {
90 // Check if compression is out of bounds
91 if (out + segment_offset >= decompressed_size)
92 return false;
93
94 u8 data = decompressed[out + segment_offset];
95 decompressed[--out] = data;
96 }
97 } else {
98 // Check if compression is out of bounds
99 if (out < 1)
100 return false;
101 decompressed[--out] = compressed[--index];
102 }
103 control <<= 1;
104 }
105 }
106 return true;
107}
108
109////////////////////////////////////////////////////////////////////////////////////////////////////
110// AppLoader_NCCH class
111 32
112FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { 33FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
113 u32 magic; 34 u32 magic;
@@ -124,203 +45,94 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
124 return FileType::Error; 45 return FileType::Error;
125} 46}
126 47
48static std::string GetUpdateNCCHPath(u64_le program_id) {
49 u32 high = static_cast<u32>((program_id | UPDATE_MASK) >> 32);
50 u32 low = static_cast<u32>((program_id | UPDATE_MASK) & 0xFFFFFFFF);
51
52 return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/%08x/%08x/content/00000000.app",
53 FileUtil::GetUserPath(D_SDMC_IDX).c_str(), SYSTEM_ID, SDCARD_ID,
54 high, low);
55}
56
127std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() { 57std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() {
128 if (!is_loaded) { 58 if (!is_loaded) {
129 ResultStatus res = LoadExeFS(); 59 ResultStatus res = base_ncch.Load();
130 if (res != ResultStatus::Success) { 60 if (res != ResultStatus::Success) {
131 return std::make_pair(boost::none, res); 61 return std::make_pair(boost::none, res);
132 } 62 }
133 } 63 }
64
134 // Set the system mode as the one from the exheader. 65 // Set the system mode as the one from the exheader.
135 return std::make_pair(exheader_header.arm11_system_local_caps.system_mode.Value(), 66 return std::make_pair(overlay_ncch->exheader_header.arm11_system_local_caps.system_mode.Value(),
136 ResultStatus::Success); 67 ResultStatus::Success);
137} 68}
138 69
139ResultStatus AppLoader_NCCH::LoadExec() { 70ResultStatus AppLoader_NCCH::LoadExec(Kernel::SharedPtr<Kernel::Process>& process) {
140 using Kernel::SharedPtr;
141 using Kernel::CodeSet; 71 using Kernel::CodeSet;
72 using Kernel::SharedPtr;
142 73
143 if (!is_loaded) 74 if (!is_loaded)
144 return ResultStatus::ErrorNotLoaded; 75 return ResultStatus::ErrorNotLoaded;
145 76
146 std::vector<u8> code; 77 std::vector<u8> code;
147 if (ResultStatus::Success == ReadCode(code)) { 78 u64_le program_id;
79 if (ResultStatus::Success == ReadCode(code) &&
80 ResultStatus::Success == ReadProgramId(program_id)) {
148 std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( 81 std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
149 (const char*)exheader_header.codeset_info.name, 8); 82 (const char*)overlay_ncch->exheader_header.codeset_info.name, 8);
150 83
151 SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, ncch_header.program_id); 84 SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, program_id);
152 85
153 codeset->code.offset = 0; 86 codeset->code.offset = 0;
154 codeset->code.addr = exheader_header.codeset_info.text.address; 87 codeset->code.addr = overlay_ncch->exheader_header.codeset_info.text.address;
155 codeset->code.size = exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE; 88 codeset->code.size =
89 overlay_ncch->exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE;
156 90
157 codeset->rodata.offset = codeset->code.offset + codeset->code.size; 91 codeset->rodata.offset = codeset->code.offset + codeset->code.size;
158 codeset->rodata.addr = exheader_header.codeset_info.ro.address; 92 codeset->rodata.addr = overlay_ncch->exheader_header.codeset_info.ro.address;
159 codeset->rodata.size = exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE; 93 codeset->rodata.size =
94 overlay_ncch->exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE;
160 95
161 // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just 96 // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just
162 // to the regular size. Playing it safe for now. 97 // to the regular size. Playing it safe for now.
163 u32 bss_page_size = (exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF; 98 u32 bss_page_size = (overlay_ncch->exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF;
164 code.resize(code.size() + bss_page_size, 0); 99 code.resize(code.size() + bss_page_size, 0);
165 100
166 codeset->data.offset = codeset->rodata.offset + codeset->rodata.size; 101 codeset->data.offset = codeset->rodata.offset + codeset->rodata.size;
167 codeset->data.addr = exheader_header.codeset_info.data.address; 102 codeset->data.addr = overlay_ncch->exheader_header.codeset_info.data.address;
168 codeset->data.size = 103 codeset->data.size =
169 exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE + bss_page_size; 104 overlay_ncch->exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE +
105 bss_page_size;
170 106
171 codeset->entrypoint = codeset->code.addr; 107 codeset->entrypoint = codeset->code.addr;
172 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); 108 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
173 109
174 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); 110 process = Kernel::Process::Create(std::move(codeset));
175 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
176 111
177 // Attach a resource limit to the process based on the resource limit category 112 // Attach a resource limit to the process based on the resource limit category
178 Kernel::g_current_process->resource_limit = 113 process->resource_limit =
179 Kernel::ResourceLimit::GetForCategory(static_cast<Kernel::ResourceLimitCategory>( 114 Kernel::ResourceLimit::GetForCategory(static_cast<Kernel::ResourceLimitCategory>(
180 exheader_header.arm11_system_local_caps.resource_limit_category)); 115 overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category));
181 116
182 // Set the default CPU core for this process 117 // Set the default CPU core for this process
183 Kernel::g_current_process->ideal_processor = 118 process->ideal_processor =
184 exheader_header.arm11_system_local_caps.ideal_processor; 119 overlay_ncch->exheader_header.arm11_system_local_caps.ideal_processor;
185 120
186 // Copy data while converting endianness 121 // Copy data while converting endianness
187 std::array<u32, ARRAY_SIZE(exheader_header.arm11_kernel_caps.descriptors)> kernel_caps; 122 std::array<u32, ARRAY_SIZE(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors)>
188 std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), 123 kernel_caps;
124 std::copy_n(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(),
189 begin(kernel_caps)); 125 begin(kernel_caps));
190 Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); 126 process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size());
191 127
192 s32 priority = exheader_header.arm11_system_local_caps.priority; 128 s32 priority = overlay_ncch->exheader_header.arm11_system_local_caps.priority;
193 u32 stack_size = exheader_header.codeset_info.stack_size; 129 u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size;
194 Kernel::g_current_process->Run(priority, stack_size); 130 process->Run(priority, stack_size);
195 return ResultStatus::Success; 131 return ResultStatus::Success;
196 } 132 }
197 return ResultStatus::Error; 133 return ResultStatus::Error;
198} 134}
199 135
200ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) {
201 if (!file.IsOpen())
202 return ResultStatus::Error;
203
204 ResultStatus result = LoadExeFS();
205 if (result != ResultStatus::Success)
206 return result;
207
208 LOG_DEBUG(Loader, "%d sections:", kMaxSections);
209 // Iterate through the ExeFs archive until we find a section with the specified name...
210 for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
211 const auto& section = exefs_header.section[section_number];
212
213 // Load the specified section...
214 if (strcmp(section.name, name) == 0) {
215 LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number,
216 section.offset, section.size, section.name);
217
218 s64 section_offset =
219 (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
220 file.Seek(section_offset, SEEK_SET);
221
222 if (strcmp(section.name, ".code") == 0 && is_compressed) {
223 // Section is compressed, read compressed .code section...
224 std::unique_ptr<u8[]> temp_buffer;
225 try {
226 temp_buffer.reset(new u8[section.size]);
227 } catch (std::bad_alloc&) {
228 return ResultStatus::ErrorMemoryAllocationFailed;
229 }
230
231 if (file.ReadBytes(&temp_buffer[0], section.size) != section.size)
232 return ResultStatus::Error;
233
234 // Decompress .code section...
235 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size);
236 buffer.resize(decompressed_size);
237 if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size))
238 return ResultStatus::ErrorInvalidFormat;
239 } else {
240 // Section is uncompressed...
241 buffer.resize(section.size);
242 if (file.ReadBytes(&buffer[0], section.size) != section.size)
243 return ResultStatus::Error;
244 }
245 return ResultStatus::Success;
246 }
247 }
248 return ResultStatus::ErrorNotUsed;
249}
250
251ResultStatus AppLoader_NCCH::LoadExeFS() {
252 if (is_exefs_loaded)
253 return ResultStatus::Success;
254
255 if (!file.IsOpen())
256 return ResultStatus::Error;
257
258 // Reset read pointer in case this file has been read before.
259 file.Seek(0, SEEK_SET);
260
261 if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header))
262 return ResultStatus::Error;
263
264 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
265 if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
266 LOG_DEBUG(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
267 ncch_offset = 0x4000;
268 file.Seek(ncch_offset, SEEK_SET);
269 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
270 }
271
272 // Verify we are loading the correct file type...
273 if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
274 return ResultStatus::ErrorInvalidFormat;
275
276 // Read ExHeader...
277
278 if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header))
279 return ResultStatus::Error;
280
281 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
282 entry_point = exheader_header.codeset_info.text.address;
283 code_size = exheader_header.codeset_info.text.code_size;
284 stack_size = exheader_header.codeset_info.stack_size;
285 bss_size = exheader_header.codeset_info.bss_size;
286 core_version = exheader_header.arm11_system_local_caps.core_version;
287 priority = exheader_header.arm11_system_local_caps.priority;
288 resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category;
289
290 LOG_DEBUG(Loader, "Name: %s", exheader_header.codeset_info.name);
291 LOG_DEBUG(Loader, "Program ID: %016" PRIX64, ncch_header.program_id);
292 LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
293 LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
294 LOG_DEBUG(Loader, "Code size: 0x%08X", code_size);
295 LOG_DEBUG(Loader, "Stack size: 0x%08X", stack_size);
296 LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size);
297 LOG_DEBUG(Loader, "Core version: %d", core_version);
298 LOG_DEBUG(Loader, "Thread priority: 0x%X", priority);
299 LOG_DEBUG(Loader, "Resource limit category: %d", resource_limit_category);
300 LOG_DEBUG(Loader, "System Mode: %d",
301 static_cast<int>(exheader_header.arm11_system_local_caps.system_mode));
302
303 if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) {
304 LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted.");
305 return ResultStatus::ErrorEncrypted;
306 }
307
308 // Read ExeFS...
309
310 exefs_offset = ncch_header.exefs_offset * kBlockSize;
311 u32 exefs_size = ncch_header.exefs_size * kBlockSize;
312
313 LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
314 LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
315
316 file.Seek(exefs_offset + ncch_offset, SEEK_SET);
317 if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
318 return ResultStatus::Error;
319
320 is_exefs_loaded = true;
321 return ResultStatus::Success;
322}
323
324void AppLoader_NCCH::ParseRegionLockoutInfo() { 136void AppLoader_NCCH::ParseRegionLockoutInfo() {
325 std::vector<u8> smdh_buffer; 137 std::vector<u8> smdh_buffer;
326 if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) { 138 if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) {
@@ -338,35 +150,43 @@ void AppLoader_NCCH::ParseRegionLockoutInfo() {
338 } 150 }
339} 151}
340 152
341ResultStatus AppLoader_NCCH::Load() { 153ResultStatus AppLoader_NCCH::Load(Kernel::SharedPtr<Kernel::Process>& process) {
154 u64_le ncch_program_id;
155
342 if (is_loaded) 156 if (is_loaded)
343 return ResultStatus::ErrorAlreadyLoaded; 157 return ResultStatus::ErrorAlreadyLoaded;
344 158
345 ResultStatus result = LoadExeFS(); 159 ResultStatus result = base_ncch.Load();
346 if (result != ResultStatus::Success) 160 if (result != ResultStatus::Success)
347 return result; 161 return result;
348 162
349 std::string program_id{Common::StringFromFormat("%016" PRIX64, ncch_header.program_id)}; 163 ReadProgramId(ncch_program_id);
164 std::string program_id{Common::StringFromFormat("%016" PRIX64, ncch_program_id)};
350 165
351 LOG_INFO(Loader, "Program ID: %s", program_id.c_str()); 166 LOG_INFO(Loader, "Program ID: %s", program_id.c_str());
352 167
168 update_ncch.OpenFile(GetUpdateNCCHPath(ncch_program_id));
169 result = update_ncch.Load();
170 if (result == ResultStatus::Success) {
171 overlay_ncch = &update_ncch;
172 }
173
353 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id); 174 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
354 175
355 if (auto room_member = Network::GetRoomMember().lock()) { 176 if (auto room_member = Network::GetRoomMember().lock()) {
356 Network::GameInfo game_info; 177 Network::GameInfo game_info;
357 ReadTitle(game_info.name); 178 ReadTitle(game_info.name);
358 game_info.id = ncch_header.program_id; 179 game_info.id = ncch_program_id;
359 room_member->SendGameInfo(game_info); 180 room_member->SendGameInfo(game_info);
360 } 181 }
361 182
362 is_loaded = true; // Set state to loaded 183 is_loaded = true; // Set state to loaded
363 184
364 result = LoadExec(); // Load the executable into memory for booting 185 result = LoadExec(process); // Load the executable into memory for booting
365 if (ResultStatus::Success != result) 186 if (ResultStatus::Success != result)
366 return result; 187 return result;
367 188
368 Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_SelfNCCH>(*this), 189 Service::FS::RegisterSelfNCCH(*this);
369 Service::FS::ArchiveIdCode::SelfNCCH);
370 190
371 ParseRegionLockoutInfo(); 191 ParseRegionLockoutInfo();
372 192
@@ -374,61 +194,40 @@ ResultStatus AppLoader_NCCH::Load() {
374} 194}
375 195
376ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { 196ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) {
377 return LoadSectionExeFS(".code", buffer); 197 return overlay_ncch->LoadSectionExeFS(".code", buffer);
378} 198}
379 199
380ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) { 200ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) {
381 return LoadSectionExeFS("icon", buffer); 201 return overlay_ncch->LoadSectionExeFS("icon", buffer);
382} 202}
383 203
384ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) { 204ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) {
385 return LoadSectionExeFS("banner", buffer); 205 return overlay_ncch->LoadSectionExeFS("banner", buffer);
386} 206}
387 207
388ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) { 208ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) {
389 return LoadSectionExeFS("logo", buffer); 209 return overlay_ncch->LoadSectionExeFS("logo", buffer);
390} 210}
391 211
392ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) { 212ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) {
393 if (!file.IsOpen()) 213 ResultStatus result = base_ncch.ReadProgramId(out_program_id);
394 return ResultStatus::Error;
395
396 ResultStatus result = LoadExeFS();
397 if (result != ResultStatus::Success) 214 if (result != ResultStatus::Success)
398 return result; 215 return result;
399 216
400 out_program_id = ncch_header.program_id;
401 return ResultStatus::Success; 217 return ResultStatus::Success;
402} 218}
403 219
404ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, 220ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
405 u64& size) { 221 u64& size) {
406 if (!file.IsOpen()) 222 return base_ncch.ReadRomFS(romfs_file, offset, size);
407 return ResultStatus::Error; 223}
408
409 // Check if the NCCH has a RomFS...
410 if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) {
411 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
412 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
413
414 LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
415 LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
416
417 if (file.GetSize() < romfs_offset + romfs_size)
418 return ResultStatus::Error;
419
420 // We reopen the file, to allow its position to be independent from file's
421 romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb");
422 if (!romfs_file->IsOpen())
423 return ResultStatus::Error;
424 224
425 offset = romfs_offset; 225ResultStatus AppLoader_NCCH::ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file,
426 size = romfs_size; 226 u64& offset, u64& size) {
227 ResultStatus result = update_ncch.ReadRomFS(romfs_file, offset, size);
427 228
428 return ResultStatus::Success; 229 if (result != ResultStatus::Success)
429 } 230 return base_ncch.ReadRomFS(romfs_file, offset, size);
430 LOG_DEBUG(Loader, "NCCH has no RomFS");
431 return ResultStatus::ErrorNotUsed;
432} 231}
433 232
434ResultStatus AppLoader_NCCH::ReadTitle(std::string& title) { 233ResultStatus AppLoader_NCCH::ReadTitle(std::string& title) {
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index e40cef764..09230ae33 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -5,155 +5,12 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include "common/bit_field.h"
9#include "common/common_types.h" 8#include "common/common_types.h"
10#include "common/swap.h" 9#include "common/swap.h"
10#include "core/file_sys/ncch_container.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
13//////////////////////////////////////////////////////////////////////////////////////////////////// 13////////////////////////////////////////////////////////////////////////////////////////////////////
14/// NCCH header (Note: "NCCH" appears to be a publicly unknown acronym)
15
16struct NCCH_Header {
17 u8 signature[0x100];
18 u32_le magic;
19 u32_le content_size;
20 u8 partition_id[8];
21 u16_le maker_code;
22 u16_le version;
23 u8 reserved_0[4];
24 u64_le program_id;
25 u8 reserved_1[0x10];
26 u8 logo_region_hash[0x20];
27 u8 product_code[0x10];
28 u8 extended_header_hash[0x20];
29 u32_le extended_header_size;
30 u8 reserved_2[4];
31 u8 flags[8];
32 u32_le plain_region_offset;
33 u32_le plain_region_size;
34 u32_le logo_region_offset;
35 u32_le logo_region_size;
36 u32_le exefs_offset;
37 u32_le exefs_size;
38 u32_le exefs_hash_region_size;
39 u8 reserved_3[4];
40 u32_le romfs_offset;
41 u32_le romfs_size;
42 u32_le romfs_hash_region_size;
43 u8 reserved_4[4];
44 u8 exefs_super_block_hash[0x20];
45 u8 romfs_super_block_hash[0x20];
46};
47
48static_assert(sizeof(NCCH_Header) == 0x200, "NCCH header structure size is wrong");
49
50////////////////////////////////////////////////////////////////////////////////////////////////////
51// ExeFS (executable file system) headers
52
53struct ExeFs_SectionHeader {
54 char name[8];
55 u32 offset;
56 u32 size;
57};
58
59struct ExeFs_Header {
60 ExeFs_SectionHeader section[8];
61 u8 reserved[0x80];
62 u8 hashes[8][0x20];
63};
64
65////////////////////////////////////////////////////////////////////////////////////////////////////
66// ExHeader (executable file system header) headers
67
68struct ExHeader_SystemInfoFlags {
69 u8 reserved[5];
70 u8 flag;
71 u8 remaster_version[2];
72};
73
74struct ExHeader_CodeSegmentInfo {
75 u32 address;
76 u32 num_max_pages;
77 u32 code_size;
78};
79
80struct ExHeader_CodeSetInfo {
81 u8 name[8];
82 ExHeader_SystemInfoFlags flags;
83 ExHeader_CodeSegmentInfo text;
84 u32 stack_size;
85 ExHeader_CodeSegmentInfo ro;
86 u8 reserved[4];
87 ExHeader_CodeSegmentInfo data;
88 u32 bss_size;
89};
90
91struct ExHeader_DependencyList {
92 u8 program_id[0x30][8];
93};
94
95struct ExHeader_SystemInfo {
96 u64 save_data_size;
97 u8 jump_id[8];
98 u8 reserved_2[0x30];
99};
100
101struct ExHeader_StorageInfo {
102 u8 ext_save_data_id[8];
103 u8 system_save_data_id[8];
104 u8 reserved[8];
105 u8 access_info[7];
106 u8 other_attributes;
107};
108
109struct ExHeader_ARM11_SystemLocalCaps {
110 u64_le program_id;
111 u32_le core_version;
112 u8 reserved_flags[2];
113 union {
114 u8 flags0;
115 BitField<0, 2, u8> ideal_processor;
116 BitField<2, 2, u8> affinity_mask;
117 BitField<4, 4, u8> system_mode;
118 };
119 u8 priority;
120 u8 resource_limit_descriptor[0x10][2];
121 ExHeader_StorageInfo storage_info;
122 u8 service_access_control[0x20][8];
123 u8 ex_service_access_control[0x2][8];
124 u8 reserved[0xf];
125 u8 resource_limit_category;
126};
127
128struct ExHeader_ARM11_KernelCaps {
129 u32_le descriptors[28];
130 u8 reserved[0x10];
131};
132
133struct ExHeader_ARM9_AccessControl {
134 u8 descriptors[15];
135 u8 descversion;
136};
137
138struct ExHeader_Header {
139 ExHeader_CodeSetInfo codeset_info;
140 ExHeader_DependencyList dependency_list;
141 ExHeader_SystemInfo system_info;
142 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
143 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
144 ExHeader_ARM9_AccessControl arm9_access_control;
145 struct {
146 u8 signature[0x100];
147 u8 ncch_public_key_modulus[0x100];
148 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
149 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
150 ExHeader_ARM9_AccessControl arm9_access_control;
151 } access_desc;
152};
153
154static_assert(sizeof(ExHeader_Header) == 0x800, "ExHeader structure size is wrong");
155
156////////////////////////////////////////////////////////////////////////////////////////////////////
157// Loader namespace 14// Loader namespace
158 15
159namespace Loader { 16namespace Loader {
@@ -162,7 +19,8 @@ namespace Loader {
162class AppLoader_NCCH final : public AppLoader { 19class AppLoader_NCCH final : public AppLoader {
163public: 20public:
164 AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath) 21 AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath)
165 : AppLoader(std::move(file)), filepath(filepath) {} 22 : AppLoader(std::move(file)), filepath(filepath), base_ncch(filepath),
23 overlay_ncch(&base_ncch) {}
166 24
167 /** 25 /**
168 * Returns the type of the file 26 * Returns the type of the file
@@ -175,7 +33,7 @@ public:
175 return IdentifyType(file); 33 return IdentifyType(file);
176 } 34 }
177 35
178 ResultStatus Load() override; 36 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
179 37
180 /** 38 /**
181 * Loads the Exheader and returns the system mode for this application. 39 * Loads the Exheader and returns the system mode for this application.
@@ -196,48 +54,25 @@ public:
196 ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, 54 ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
197 u64& size) override; 55 u64& size) override;
198 56
57 ResultStatus ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
58 u64& size) override;
59
199 ResultStatus ReadTitle(std::string& title) override; 60 ResultStatus ReadTitle(std::string& title) override;
200 61
201private: 62private:
202 /** 63 /**
203 * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
204 * @param name Name of section to read out of NCCH file
205 * @param buffer Vector to read data into
206 * @return ResultStatus result of function
207 */
208 ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer);
209
210 /**
211 * Loads .code section into memory for booting 64 * Loads .code section into memory for booting
65 * @param process The newly created process
212 * @return ResultStatus result of function 66 * @return ResultStatus result of function
213 */ 67 */
214 ResultStatus LoadExec(); 68 ResultStatus LoadExec(Kernel::SharedPtr<Kernel::Process>& process);
215
216 /**
217 * Ensure ExeFS is loaded and ready for reading sections
218 * @return ResultStatus result of function
219 */
220 ResultStatus LoadExeFS();
221 69
222 /// Reads the region lockout info in the SMDH and send it to CFG service 70 /// Reads the region lockout info in the SMDH and send it to CFG service
223 void ParseRegionLockoutInfo(); 71 void ParseRegionLockoutInfo();
224 72
225 bool is_exefs_loaded = false; 73 FileSys::NCCHContainer base_ncch;
226 bool is_compressed = false; 74 FileSys::NCCHContainer update_ncch;
227 75 FileSys::NCCHContainer* overlay_ncch;
228 u32 entry_point = 0;
229 u32 code_size = 0;
230 u32 stack_size = 0;
231 u32 bss_size = 0;
232 u32 core_version = 0;
233 u8 priority = 0;
234 u8 resource_limit_category = 0;
235 u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header
236 u32 exefs_offset = 0;
237
238 NCCH_Header ncch_header;
239 ExeFs_Header exefs_header;
240 ExHeader_Header exheader_header;
241 76
242 std::string filepath; 77 std::string filepath;
243}; 78};
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 68a6b1ac2..7f58be6de 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -9,6 +9,8 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/arm/arm_interface.h"
13#include "core/core.h"
12#include "core/hle/kernel/memory.h" 14#include "core/hle/kernel/memory.h"
13#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
14#include "core/hle/lock.h" 16#include "core/hle/lock.h"
@@ -22,10 +24,17 @@ namespace Memory {
22static std::array<u8, Memory::VRAM_SIZE> vram; 24static std::array<u8, Memory::VRAM_SIZE> vram;
23static std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram; 25static std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
24 26
25PageTable* current_page_table = nullptr; 27static PageTable* current_page_table = nullptr;
26 28
27std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { 29void SetCurrentPageTable(PageTable* page_table) {
28 return &current_page_table->pointers; 30 current_page_table = page_table;
31 if (Core::System::GetInstance().IsPoweredOn()) {
32 Core::CPU().PageTableChanged();
33 }
34}
35
36PageTable* GetCurrentPageTable() {
37 return current_page_table;
29} 38}
30 39
31static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) { 40static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) {
@@ -73,10 +82,10 @@ void UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
73 * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) 82 * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
74 * using a VMA from the current process 83 * using a VMA from the current process
75 */ 84 */
76static u8* GetPointerFromVMA(VAddr vaddr) { 85static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
77 u8* direct_pointer = nullptr; 86 u8* direct_pointer = nullptr;
78 87
79 auto& vm_manager = Kernel::g_current_process->vm_manager; 88 auto& vm_manager = process.vm_manager;
80 89
81 auto it = vm_manager.FindVMA(vaddr); 90 auto it = vm_manager.FindVMA(vaddr);
82 ASSERT(it != vm_manager.vma_map.end()); 91 ASSERT(it != vm_manager.vma_map.end());
@@ -99,10 +108,18 @@ static u8* GetPointerFromVMA(VAddr vaddr) {
99} 108}
100 109
101/** 110/**
111 * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
112 * using a VMA from the current process.
113 */
114static u8* GetPointerFromVMA(VAddr vaddr) {
115 return GetPointerFromVMA(*Kernel::g_current_process, vaddr);
116}
117
118/**
102 * This function should only be called for virtual addreses with attribute `PageType::Special`. 119 * This function should only be called for virtual addreses with attribute `PageType::Special`.
103 */ 120 */
104static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { 121static MMIORegionPointer GetMMIOHandler(const PageTable& page_table, VAddr vaddr) {
105 for (const auto& region : current_page_table->special_regions) { 122 for (const auto& region : page_table.special_regions) {
106 if (vaddr >= region.base && vaddr < (region.base + region.size)) { 123 if (vaddr >= region.base && vaddr < (region.base + region.size)) {
107 return region.handler; 124 return region.handler;
108 } 125 }
@@ -111,6 +128,11 @@ static MMIORegionPointer GetMMIOHandler(VAddr vaddr) {
111 return nullptr; // Should never happen 128 return nullptr; // Should never happen
112} 129}
113 130
131static MMIORegionPointer GetMMIOHandler(VAddr vaddr) {
132 const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
133 return GetMMIOHandler(page_table, vaddr);
134}
135
114template <typename T> 136template <typename T>
115T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr); 137T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr);
116 138
@@ -195,18 +217,20 @@ void Write(const VAddr vaddr, const T data) {
195 } 217 }
196} 218}
197 219
198bool IsValidVirtualAddress(const VAddr vaddr) { 220bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
199 const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; 221 auto& page_table = process.vm_manager.page_table;
222
223 const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
200 if (page_pointer) 224 if (page_pointer)
201 return true; 225 return true;
202 226
203 if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) 227 if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory)
204 return true; 228 return true;
205 229
206 if (current_page_table->attributes[vaddr >> PAGE_BITS] != PageType::Special) 230 if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special)
207 return false; 231 return false;
208 232
209 MMIORegionPointer mmio_region = GetMMIOHandler(vaddr); 233 MMIORegionPointer mmio_region = GetMMIOHandler(page_table, vaddr);
210 if (mmio_region) { 234 if (mmio_region) {
211 return mmio_region->IsValidAddress(vaddr); 235 return mmio_region->IsValidAddress(vaddr);
212 } 236 }
@@ -214,6 +238,10 @@ bool IsValidVirtualAddress(const VAddr vaddr) {
214 return false; 238 return false;
215} 239}
216 240
241bool IsValidVirtualAddress(const VAddr vaddr) {
242 return IsValidVirtualAddress(*Kernel::g_current_process, vaddr);
243}
244
217bool IsValidPhysicalAddress(const PAddr paddr) { 245bool IsValidPhysicalAddress(const PAddr paddr) {
218 return GetPhysicalPointer(paddr) != nullptr; 246 return GetPhysicalPointer(paddr) != nullptr;
219} 247}
@@ -316,8 +344,15 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
316 344
317 for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) { 345 for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) {
318 boost::optional<VAddr> maybe_vaddr = PhysicalToVirtualAddress(paddr); 346 boost::optional<VAddr> maybe_vaddr = PhysicalToVirtualAddress(paddr);
319 if (!maybe_vaddr) 347 // While the physical <-> virtual mapping is 1:1 for the regions supported by the cache,
348 // some games (like Pokemon Super Mystery Dungeon) will try to use textures that go beyond
349 // the end address of VRAM, causing the Virtual->Physical translation to fail when flushing
350 // parts of the texture.
351 if (!maybe_vaddr) {
352 LOG_ERROR(HW_Memory,
353 "Trying to flush a cached region to an invalid physical address %08X", paddr);
320 continue; 354 continue;
355 }
321 VAddr vaddr = *maybe_vaddr; 356 VAddr vaddr = *maybe_vaddr;
322 357
323 u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS]; 358 u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS];
@@ -329,6 +364,10 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
329 if (res_count == 0) { 364 if (res_count == 0) {
330 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; 365 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
331 switch (page_type) { 366 switch (page_type) {
367 case PageType::Unmapped:
368 // It is not necessary for a process to have this region mapped into its address
369 // space, for example, a system module need not have a VRAM mapping.
370 break;
332 case PageType::Memory: 371 case PageType::Memory:
333 page_type = PageType::RasterizerCachedMemory; 372 page_type = PageType::RasterizerCachedMemory;
334 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; 373 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
@@ -347,6 +386,10 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
347 if (res_count == 0) { 386 if (res_count == 0) {
348 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; 387 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
349 switch (page_type) { 388 switch (page_type) {
389 case PageType::Unmapped:
390 // It is not necessary for a process to have this region mapped into its address
391 // space, for example, a system module need not have a VRAM mapping.
392 break;
350 case PageType::RasterizerCachedMemory: { 393 case PageType::RasterizerCachedMemory: {
351 u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); 394 u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
352 if (pointer == nullptr) { 395 if (pointer == nullptr) {
@@ -435,16 +478,19 @@ u64 Read64(const VAddr addr) {
435 return Read<u64_le>(addr); 478 return Read<u64_le>(addr);
436} 479}
437 480
438void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { 481void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
482 const size_t size) {
483 auto& page_table = process.vm_manager.page_table;
484
439 size_t remaining_size = size; 485 size_t remaining_size = size;
440 size_t page_index = src_addr >> PAGE_BITS; 486 size_t page_index = src_addr >> PAGE_BITS;
441 size_t page_offset = src_addr & PAGE_MASK; 487 size_t page_offset = src_addr & PAGE_MASK;
442 488
443 while (remaining_size > 0) { 489 while (remaining_size > 0) {
444 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); 490 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
445 const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; 491 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
446 492
447 switch (current_page_table->attributes[page_index]) { 493 switch (page_table.attributes[page_index]) {
448 case PageType::Unmapped: { 494 case PageType::Unmapped: {
449 LOG_ERROR(HW_Memory, "unmapped ReadBlock @ 0x%08X (start address = 0x%08X, size = %zu)", 495 LOG_ERROR(HW_Memory, "unmapped ReadBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
450 current_vaddr, src_addr, size); 496 current_vaddr, src_addr, size);
@@ -452,27 +498,30 @@ void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) {
452 break; 498 break;
453 } 499 }
454 case PageType::Memory: { 500 case PageType::Memory: {
455 DEBUG_ASSERT(current_page_table->pointers[page_index]); 501 DEBUG_ASSERT(page_table.pointers[page_index]);
456 502
457 const u8* src_ptr = current_page_table->pointers[page_index] + page_offset; 503 const u8* src_ptr = page_table.pointers[page_index] + page_offset;
458 std::memcpy(dest_buffer, src_ptr, copy_amount); 504 std::memcpy(dest_buffer, src_ptr, copy_amount);
459 break; 505 break;
460 } 506 }
461 case PageType::Special: { 507 case PageType::Special: {
462 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 508 MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
463 509 DEBUG_ASSERT(handler);
464 GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount); 510 handler->ReadBlock(current_vaddr, dest_buffer, copy_amount);
465 break; 511 break;
466 } 512 }
467 case PageType::RasterizerCachedMemory: { 513 case PageType::RasterizerCachedMemory: {
468 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); 514 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
469 std::memcpy(dest_buffer, GetPointerFromVMA(current_vaddr), copy_amount); 515 FlushMode::Flush);
516 std::memcpy(dest_buffer, GetPointerFromVMA(process, current_vaddr), copy_amount);
470 break; 517 break;
471 } 518 }
472 case PageType::RasterizerCachedSpecial: { 519 case PageType::RasterizerCachedSpecial: {
473 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 520 MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
474 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); 521 DEBUG_ASSERT(handler);
475 GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount); 522 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
523 FlushMode::Flush);
524 handler->ReadBlock(current_vaddr, dest_buffer, copy_amount);
476 break; 525 break;
477 } 526 }
478 default: 527 default:
@@ -486,6 +535,10 @@ void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) {
486 } 535 }
487} 536}
488 537
538void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) {
539 ReadBlock(*Kernel::g_current_process, src_addr, dest_buffer, size);
540}
541
489void Write8(const VAddr addr, const u8 data) { 542void Write8(const VAddr addr, const u8 data) {
490 Write<u8>(addr, data); 543 Write<u8>(addr, data);
491} 544}
@@ -502,16 +555,18 @@ void Write64(const VAddr addr, const u64 data) {
502 Write<u64_le>(addr, data); 555 Write<u64_le>(addr, data);
503} 556}
504 557
505void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) { 558void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
559 const size_t size) {
560 auto& page_table = process.vm_manager.page_table;
506 size_t remaining_size = size; 561 size_t remaining_size = size;
507 size_t page_index = dest_addr >> PAGE_BITS; 562 size_t page_index = dest_addr >> PAGE_BITS;
508 size_t page_offset = dest_addr & PAGE_MASK; 563 size_t page_offset = dest_addr & PAGE_MASK;
509 564
510 while (remaining_size > 0) { 565 while (remaining_size > 0) {
511 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); 566 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
512 const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; 567 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
513 568
514 switch (current_page_table->attributes[page_index]) { 569 switch (page_table.attributes[page_index]) {
515 case PageType::Unmapped: { 570 case PageType::Unmapped: {
516 LOG_ERROR(HW_Memory, 571 LOG_ERROR(HW_Memory,
517 "unmapped WriteBlock @ 0x%08X (start address = 0x%08X, size = %zu)", 572 "unmapped WriteBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
@@ -519,27 +574,30 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size
519 break; 574 break;
520 } 575 }
521 case PageType::Memory: { 576 case PageType::Memory: {
522 DEBUG_ASSERT(current_page_table->pointers[page_index]); 577 DEBUG_ASSERT(page_table.pointers[page_index]);
523 578
524 u8* dest_ptr = current_page_table->pointers[page_index] + page_offset; 579 u8* dest_ptr = page_table.pointers[page_index] + page_offset;
525 std::memcpy(dest_ptr, src_buffer, copy_amount); 580 std::memcpy(dest_ptr, src_buffer, copy_amount);
526 break; 581 break;
527 } 582 }
528 case PageType::Special: { 583 case PageType::Special: {
529 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 584 MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
530 585 DEBUG_ASSERT(handler);
531 GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount); 586 handler->WriteBlock(current_vaddr, src_buffer, copy_amount);
532 break; 587 break;
533 } 588 }
534 case PageType::RasterizerCachedMemory: { 589 case PageType::RasterizerCachedMemory: {
535 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); 590 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
536 std::memcpy(GetPointerFromVMA(current_vaddr), src_buffer, copy_amount); 591 FlushMode::FlushAndInvalidate);
592 std::memcpy(GetPointerFromVMA(process, current_vaddr), src_buffer, copy_amount);
537 break; 593 break;
538 } 594 }
539 case PageType::RasterizerCachedSpecial: { 595 case PageType::RasterizerCachedSpecial: {
540 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 596 MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
541 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); 597 DEBUG_ASSERT(handler);
542 GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount); 598 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
599 FlushMode::FlushAndInvalidate);
600 handler->WriteBlock(current_vaddr, src_buffer, copy_amount);
543 break; 601 break;
544 } 602 }
545 default: 603 default:
@@ -553,6 +611,10 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size
553 } 611 }
554} 612}
555 613
614void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) {
615 WriteBlock(*Kernel::g_current_process, dest_addr, src_buffer, size);
616}
617
556void ZeroBlock(const VAddr dest_addr, const size_t size) { 618void ZeroBlock(const VAddr dest_addr, const size_t size) {
557 size_t remaining_size = size; 619 size_t remaining_size = size;
558 size_t page_index = dest_addr >> PAGE_BITS; 620 size_t page_index = dest_addr >> PAGE_BITS;
@@ -562,7 +624,7 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
562 624
563 while (remaining_size > 0) { 625 while (remaining_size > 0) {
564 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); 626 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
565 const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; 627 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
566 628
567 switch (current_page_table->attributes[page_index]) { 629 switch (current_page_table->attributes[page_index]) {
568 case PageType::Unmapped: { 630 case PageType::Unmapped: {
@@ -584,13 +646,15 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
584 break; 646 break;
585 } 647 }
586 case PageType::RasterizerCachedMemory: { 648 case PageType::RasterizerCachedMemory: {
587 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); 649 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
650 FlushMode::FlushAndInvalidate);
588 std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount); 651 std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount);
589 break; 652 break;
590 } 653 }
591 case PageType::RasterizerCachedSpecial: { 654 case PageType::RasterizerCachedSpecial: {
592 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 655 DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
593 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); 656 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
657 FlushMode::FlushAndInvalidate);
594 GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount); 658 GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
595 break; 659 break;
596 } 660 }
@@ -611,7 +675,7 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
611 675
612 while (remaining_size > 0) { 676 while (remaining_size > 0) {
613 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); 677 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
614 const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; 678 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
615 679
616 switch (current_page_table->attributes[page_index]) { 680 switch (current_page_table->attributes[page_index]) {
617 case PageType::Unmapped: { 681 case PageType::Unmapped: {
@@ -635,13 +699,15 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
635 break; 699 break;
636 } 700 }
637 case PageType::RasterizerCachedMemory: { 701 case PageType::RasterizerCachedMemory: {
638 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); 702 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
703 FlushMode::Flush);
639 WriteBlock(dest_addr, GetPointerFromVMA(current_vaddr), copy_amount); 704 WriteBlock(dest_addr, GetPointerFromVMA(current_vaddr), copy_amount);
640 break; 705 break;
641 } 706 }
642 case PageType::RasterizerCachedSpecial: { 707 case PageType::RasterizerCachedSpecial: {
643 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 708 DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
644 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); 709 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
710 FlushMode::Flush);
645 711
646 std::vector<u8> buffer(copy_amount); 712 std::vector<u8> buffer(copy_amount);
647 GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size()); 713 GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size());
@@ -654,8 +720,8 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
654 720
655 page_index++; 721 page_index++;
656 page_offset = 0; 722 page_offset = 0;
657 dest_addr += copy_amount; 723 dest_addr += static_cast<VAddr>(copy_amount);
658 src_addr += copy_amount; 724 src_addr += static_cast<VAddr>(copy_amount);
659 remaining_size -= copy_amount; 725 remaining_size -= copy_amount;
660 } 726 }
661} 727}
diff --git a/src/core/memory.h b/src/core/memory.h
index b228a48c2..dd599f73e 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -12,6 +12,10 @@
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/mmio.h" 13#include "core/mmio.h"
14 14
15namespace Kernel {
16class Process;
17}
18
15namespace Memory { 19namespace Memory {
16 20
17/** 21/**
@@ -182,9 +186,13 @@ enum : VAddr {
182}; 186};
183 187
184/// Currently active page table 188/// Currently active page table
185extern PageTable* current_page_table; 189void SetCurrentPageTable(PageTable* page_table);
190PageTable* GetCurrentPageTable();
186 191
192/// Determines if the given VAddr is valid for the specified process.
193bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr);
187bool IsValidVirtualAddress(const VAddr addr); 194bool IsValidVirtualAddress(const VAddr addr);
195
188bool IsValidPhysicalAddress(const PAddr addr); 196bool IsValidPhysicalAddress(const PAddr addr);
189 197
190u8 Read8(VAddr addr); 198u8 Read8(VAddr addr);
@@ -197,7 +205,11 @@ void Write16(VAddr addr, u16 data);
197void Write32(VAddr addr, u32 data); 205void Write32(VAddr addr, u32 data);
198void Write64(VAddr addr, u64 data); 206void Write64(VAddr addr, u64 data);
199 207
208void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
209 size_t size);
200void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); 210void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size);
211void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
212 size_t size);
201void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); 213void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size);
202void ZeroBlock(const VAddr dest_addr, const size_t size); 214void ZeroBlock(const VAddr dest_addr, const size_t size);
203void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); 215void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
@@ -259,10 +271,4 @@ enum class FlushMode {
259 */ 271 */
260void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode); 272void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode);
261 273
262/**
263 * Dynarmic has an optimization to memory accesses when the pointer to the page exists that
264 * can be used by setting up the current page table as a callback. This function is used to
265 * retrieve the current page table for that purpose.
266 */
267std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers();
268} // namespace Memory 274} // namespace Memory
diff --git a/src/network/packet.cpp b/src/network/packet.cpp
index cc60f2fbc..7e1a812f3 100644
--- a/src/network/packet.cpp
+++ b/src/network/packet.cpp
@@ -233,7 +233,7 @@ Packet& Packet::operator<<(double in_data) {
233 233
234Packet& Packet::operator<<(const char* in_data) { 234Packet& Packet::operator<<(const char* in_data) {
235 // First insert string length 235 // First insert string length
236 u32 length = std::strlen(in_data); 236 u32 length = static_cast<u32>(std::strlen(in_data));
237 *this << length; 237 *this << length;
238 238
239 // Then insert characters 239 // Then insert characters
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 5e9c4c2bf..1aac0daa2 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -4,6 +4,7 @@ set(SRCS
4 core/arm/dyncom/arm_dyncom_vfp_tests.cpp 4 core/arm/dyncom/arm_dyncom_vfp_tests.cpp
5 core/file_sys/path_parser.cpp 5 core/file_sys/path_parser.cpp
6 core/hle/kernel/hle_ipc.cpp 6 core/hle/kernel/hle_ipc.cpp
7 core/memory/memory.cpp
7 glad.cpp 8 glad.cpp
8 tests.cpp 9 tests.cpp
9 ) 10 )
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 8384ce744..484713a92 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -3,30 +3,34 @@
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/hle/kernel/process.h"
6#include "core/memory.h" 7#include "core/memory.h"
7#include "core/memory_setup.h" 8#include "core/memory_setup.h"
8#include "tests/core/arm/arm_test_common.h" 9#include "tests/core/arm/arm_test_common.h"
9 10
10namespace ArmTests { 11namespace ArmTests {
11 12
12static Memory::PageTable page_table; 13static Memory::PageTable* page_table = nullptr;
13 14
14TestEnvironment::TestEnvironment(bool mutable_memory_) 15TestEnvironment::TestEnvironment(bool mutable_memory_)
15 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { 16 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
16 17
17 page_table.pointers.fill(nullptr); 18 Kernel::g_current_process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0));
18 page_table.attributes.fill(Memory::PageType::Unmapped); 19 page_table = &Kernel::g_current_process->vm_manager.page_table;
19 page_table.cached_res_count.fill(0);
20 20
21 Memory::MapIoRegion(page_table, 0x00000000, 0x80000000, test_memory); 21 page_table->pointers.fill(nullptr);
22 Memory::MapIoRegion(page_table, 0x80000000, 0x80000000, test_memory); 22 page_table->attributes.fill(Memory::PageType::Unmapped);
23 page_table->cached_res_count.fill(0);
23 24
24 Memory::current_page_table = &page_table; 25 Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
26 Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
27
28 Memory::SetCurrentPageTable(page_table);
25} 29}
26 30
27TestEnvironment::~TestEnvironment() { 31TestEnvironment::~TestEnvironment() {
28 Memory::UnmapRegion(page_table, 0x80000000, 0x80000000); 32 Memory::UnmapRegion(*page_table, 0x80000000, 0x80000000);
29 Memory::UnmapRegion(page_table, 0x00000000, 0x80000000); 33 Memory::UnmapRegion(*page_table, 0x00000000, 0x80000000);
30} 34}
31 35
32void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) { 36void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) {
diff --git a/src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp b/src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp
index 86de41773..83719a58e 100644
--- a/src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp
+++ b/src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp
@@ -5,6 +5,7 @@
5#include <catch.hpp> 5#include <catch.hpp>
6 6
7#include "core/arm/dyncom/arm_dyncom.h" 7#include "core/arm/dyncom/arm_dyncom.h"
8#include "core/core_timing.h"
8#include "tests/core/arm/arm_test_common.h" 9#include "tests/core/arm/arm_test_common.h"
9 10
10namespace ArmTests { 11namespace ArmTests {
@@ -29,7 +30,6 @@ TEST_CASE("ARM_DynCom (vfp): vadd", "[arm_dyncom]") {
29 }}; 30 }};
30 31
31 for (const auto& test_case : test_cases) { 32 for (const auto& test_case : test_cases) {
32 dyncom.down_count = 1000; // Ensure that CoreTimeing will not be called.
33 dyncom.SetPC(0); 33 dyncom.SetPC(0);
34 dyncom.SetVFPSystemReg(VFP_FPSCR, test_case.initial_fpscr); 34 dyncom.SetVFPSystemReg(VFP_FPSCR, test_case.initial_fpscr);
35 dyncom.SetVFPReg(4, test_case.a); 35 dyncom.SetVFPReg(4, test_case.a);
diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp
new file mode 100644
index 000000000..a01b896f7
--- /dev/null
+++ b/src/tests/core/memory/memory.cpp
@@ -0,0 +1,56 @@
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 <catch.hpp>
6#include "core/hle/kernel/memory.h"
7#include "core/hle/kernel/process.h"
8#include "core/memory.h"
9
10TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") {
11 SECTION("these regions should not be mapped on an empty process") {
12 auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0));
13 CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false);
14 CHECK(Memory::IsValidVirtualAddress(*process, Memory::HEAP_VADDR) == false);
15 CHECK(Memory::IsValidVirtualAddress(*process, Memory::LINEAR_HEAP_VADDR) == false);
16 CHECK(Memory::IsValidVirtualAddress(*process, Memory::VRAM_VADDR) == false);
17 CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false);
18 CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == false);
19 CHECK(Memory::IsValidVirtualAddress(*process, Memory::TLS_AREA_VADDR) == false);
20 }
21
22 SECTION("CONFIG_MEMORY_VADDR and SHARED_PAGE_VADDR should be valid after mapping them") {
23 auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0));
24 Kernel::MapSharedPages(process->vm_manager);
25 CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == true);
26 CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == true);
27 }
28
29 SECTION("special regions should be valid after mapping them") {
30 auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0));
31 SECTION("VRAM") {
32 Kernel::HandleSpecialMapping(process->vm_manager,
33 {Memory::VRAM_VADDR, Memory::VRAM_SIZE, false, false});
34 CHECK(Memory::IsValidVirtualAddress(*process, Memory::VRAM_VADDR) == true);
35 }
36
37 SECTION("IO (Not yet implemented)") {
38 Kernel::HandleSpecialMapping(
39 process->vm_manager, {Memory::IO_AREA_VADDR, Memory::IO_AREA_SIZE, false, false});
40 CHECK_FALSE(Memory::IsValidVirtualAddress(*process, Memory::IO_AREA_VADDR) == true);
41 }
42
43 SECTION("DSP") {
44 Kernel::HandleSpecialMapping(
45 process->vm_manager, {Memory::DSP_RAM_VADDR, Memory::DSP_RAM_SIZE, false, false});
46 CHECK(Memory::IsValidVirtualAddress(*process, Memory::DSP_RAM_VADDR) == true);
47 }
48 }
49
50 SECTION("Unmapping a VAddr should make it invalid") {
51 auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0));
52 Kernel::MapSharedPages(process->vm_manager);
53 process->vm_manager.UnmapRange(Memory::CONFIG_MEMORY_VADDR, Memory::CONFIG_MEMORY_SIZE);
54 CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false);
55 }
56}
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index fb65a3a0a..caf9f7a06 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -119,6 +119,224 @@ static void WriteUniformFloatReg(ShaderRegs& config, Shader::ShaderSetup& setup,
119 } 119 }
120} 120}
121 121
122static void LoadDefaultVertexAttributes(u32 register_value) {
123 auto& regs = g_state.regs;
124
125 // TODO: Does actual hardware indeed keep an intermediate buffer or does
126 // it directly write the values?
127 default_attr_write_buffer[default_attr_counter++] = register_value;
128
129 // Default attributes are written in a packed format such that four float24 values are encoded
130 // in three 32-bit numbers.
131 // We write to internal memory once a full such vector is written.
132 if (default_attr_counter >= 3) {
133 default_attr_counter = 0;
134
135 auto& setup = regs.pipeline.vs_default_attributes_setup;
136
137 if (setup.index >= 16) {
138 LOG_ERROR(HW_GPU, "Invalid VS default attribute index %d", (int)setup.index);
139 return;
140 }
141
142 Math::Vec4<float24> attribute;
143
144 // NOTE: The destination component order indeed is "backwards"
145 attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8);
146 attribute.z = float24::FromRaw(((default_attr_write_buffer[0] & 0xFF) << 16) |
147 ((default_attr_write_buffer[1] >> 16) & 0xFFFF));
148 attribute.y = float24::FromRaw(((default_attr_write_buffer[1] & 0xFFFF) << 8) |
149 ((default_attr_write_buffer[2] >> 24) & 0xFF));
150 attribute.x = float24::FromRaw(default_attr_write_buffer[2] & 0xFFFFFF);
151
152 LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index,
153 attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(),
154 attribute.w.ToFloat32());
155
156 // TODO: Verify that this actually modifies the register!
157 if (setup.index < 15) {
158 g_state.input_default_attributes.attr[setup.index] = attribute;
159 setup.index++;
160 } else {
161 // Put each attribute into an immediate input buffer. When all specified immediate
162 // attributes are present, the Vertex Shader is invoked and everything is sent to
163 // the primitive assembler.
164
165 auto& immediate_input = g_state.immediate.input_vertex;
166 auto& immediate_attribute_id = g_state.immediate.current_attribute;
167
168 immediate_input.attr[immediate_attribute_id] = attribute;
169
170 if (immediate_attribute_id < regs.pipeline.max_input_attrib_index) {
171 immediate_attribute_id += 1;
172 } else {
173 MICROPROFILE_SCOPE(GPU_Drawing);
174 immediate_attribute_id = 0;
175
176 auto* shader_engine = Shader::GetEngine();
177 shader_engine->SetupBatch(g_state.vs, regs.vs.main_offset);
178
179 // Send to vertex shader
180 if (g_debug_context)
181 g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation,
182 static_cast<void*>(&immediate_input));
183 Shader::UnitState shader_unit;
184 Shader::AttributeBuffer output{};
185
186 shader_unit.LoadInput(regs.vs, immediate_input);
187 shader_engine->Run(g_state.vs, shader_unit);
188 shader_unit.WriteOutput(regs.vs, output);
189
190 // Send to geometry pipeline
191 if (g_state.immediate.reset_geometry_pipeline) {
192 g_state.geometry_pipeline.Reconfigure();
193 g_state.immediate.reset_geometry_pipeline = false;
194 }
195 ASSERT(!g_state.geometry_pipeline.NeedIndexInput());
196 g_state.geometry_pipeline.Setup(shader_engine);
197 g_state.geometry_pipeline.SubmitVertex(output);
198
199 // TODO: If drawing after every immediate mode triangle kills performance,
200 // change it to flush triangles whenever a drawing config register changes
201 // See: https://github.com/citra-emu/citra/pull/2866#issuecomment-327011550
202 VideoCore::g_renderer->Rasterizer()->DrawTriangles();
203 if (g_debug_context) {
204 g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
205 }
206 }
207 }
208 }
209}
210
211static void Draw(u32 command_id) {
212 MICROPROFILE_SCOPE(GPU_Drawing);
213 auto& regs = g_state.regs;
214
215#if PICA_LOG_TEV
216 DebugUtils::DumpTevStageConfig(regs.GetTevStages());
217#endif
218 if (g_debug_context)
219 g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr);
220
221 // Processes information about internal vertex attributes to figure out how a vertex is
222 // loaded.
223 // Later, these can be compiled and cached.
224 const u32 base_address = regs.pipeline.vertex_attributes.GetPhysicalBaseAddress();
225 VertexLoader loader(regs.pipeline);
226
227 // Load vertices
228 bool is_indexed = (command_id == PICA_REG_INDEX(pipeline.trigger_draw_indexed));
229
230 const auto& index_info = regs.pipeline.index_array;
231 const u8* index_address_8 = Memory::GetPhysicalPointer(base_address + index_info.offset);
232 const u16* index_address_16 = reinterpret_cast<const u16*>(index_address_8);
233 bool index_u16 = index_info.format != 0;
234
235 PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler;
236
237 if (g_debug_context && g_debug_context->recorder) {
238 for (int i = 0; i < 3; ++i) {
239 const auto texture = regs.texturing.GetTextures()[i];
240 if (!texture.enabled)
241 continue;
242
243 u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress());
244 g_debug_context->recorder->MemoryAccessed(
245 texture_data, Pica::TexturingRegs::NibblesPerPixel(texture.format) *
246 texture.config.width / 2 * texture.config.height,
247 texture.config.GetPhysicalAddress());
248 }
249 }
250
251 DebugUtils::MemoryAccessTracker memory_accesses;
252
253 // Simple circular-replacement vertex cache
254 // The size has been tuned for optimal balance between hit-rate and the cost of lookup
255 const size_t VERTEX_CACHE_SIZE = 32;
256 std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids;
257 std::array<Shader::AttributeBuffer, VERTEX_CACHE_SIZE> vertex_cache;
258 Shader::AttributeBuffer vs_output;
259
260 unsigned int vertex_cache_pos = 0;
261 vertex_cache_ids.fill(-1);
262
263 auto* shader_engine = Shader::GetEngine();
264 Shader::UnitState shader_unit;
265
266 shader_engine->SetupBatch(g_state.vs, regs.vs.main_offset);
267
268 g_state.geometry_pipeline.Reconfigure();
269 g_state.geometry_pipeline.Setup(shader_engine);
270 if (g_state.geometry_pipeline.NeedIndexInput())
271 ASSERT(is_indexed);
272
273 for (unsigned int index = 0; index < regs.pipeline.num_vertices; ++index) {
274 // Indexed rendering doesn't use the start offset
275 unsigned int vertex = is_indexed
276 ? (index_u16 ? index_address_16[index] : index_address_8[index])
277 : (index + regs.pipeline.vertex_offset);
278
279 // -1 is a common special value used for primitive restart. Since it's unknown if
280 // the PICA supports it, and it would mess up the caching, guard against it here.
281 ASSERT(vertex != -1);
282
283 bool vertex_cache_hit = false;
284
285 if (is_indexed) {
286 if (g_state.geometry_pipeline.NeedIndexInput()) {
287 g_state.geometry_pipeline.SubmitIndex(vertex);
288 continue;
289 }
290
291 if (g_debug_context && Pica::g_debug_context->recorder) {
292 int size = index_u16 ? 2 : 1;
293 memory_accesses.AddAccess(base_address + index_info.offset + size * index, size);
294 }
295
296 for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) {
297 if (vertex == vertex_cache_ids[i]) {
298 vs_output = vertex_cache[i];
299 vertex_cache_hit = true;
300 break;
301 }
302 }
303 }
304
305 if (!vertex_cache_hit) {
306 // Initialize data for the current vertex
307 Shader::AttributeBuffer input;
308 loader.LoadVertex(base_address, index, vertex, input, memory_accesses);
309
310 // Send to vertex shader
311 if (g_debug_context)
312 g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation,
313 (void*)&input);
314 shader_unit.LoadInput(regs.vs, input);
315 shader_engine->Run(g_state.vs, shader_unit);
316 shader_unit.WriteOutput(regs.vs, vs_output);
317
318 if (is_indexed) {
319 vertex_cache[vertex_cache_pos] = vs_output;
320 vertex_cache_ids[vertex_cache_pos] = vertex;
321 vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE;
322 }
323 }
324
325 // Send to geometry pipeline
326 g_state.geometry_pipeline.SubmitVertex(vs_output);
327 }
328
329 for (auto& range : memory_accesses.ranges) {
330 g_debug_context->recorder->MemoryAccessed(Memory::GetPhysicalPointer(range.first),
331 range.second, range.first);
332 }
333
334 VideoCore::g_renderer->Rasterizer()->DrawTriangles();
335 if (g_debug_context) {
336 g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
337 }
338}
339
122static void WritePicaReg(u32 id, u32 value, u32 mask) { 340static void WritePicaReg(u32 id, u32 value, u32 mask) {
123 auto& regs = g_state.regs; 341 auto& regs = g_state.regs;
124 342
@@ -168,98 +386,12 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
168 // Load default vertex input attributes 386 // Load default vertex input attributes
169 case PICA_REG_INDEX_WORKAROUND(pipeline.vs_default_attributes_setup.set_value[0], 0x233): 387 case PICA_REG_INDEX_WORKAROUND(pipeline.vs_default_attributes_setup.set_value[0], 0x233):
170 case PICA_REG_INDEX_WORKAROUND(pipeline.vs_default_attributes_setup.set_value[1], 0x234): 388 case PICA_REG_INDEX_WORKAROUND(pipeline.vs_default_attributes_setup.set_value[1], 0x234):
171 case PICA_REG_INDEX_WORKAROUND(pipeline.vs_default_attributes_setup.set_value[2], 0x235): { 389 case PICA_REG_INDEX_WORKAROUND(pipeline.vs_default_attributes_setup.set_value[2], 0x235):
172 // TODO: Does actual hardware indeed keep an intermediate buffer or does 390 LoadDefaultVertexAttributes(value);
173 // it directly write the values?
174 default_attr_write_buffer[default_attr_counter++] = value;
175
176 // Default attributes are written in a packed format such that four float24 values are
177 // encoded in
178 // three 32-bit numbers. We write to internal memory once a full such vector is
179 // written.
180 if (default_attr_counter >= 3) {
181 default_attr_counter = 0;
182
183 auto& setup = regs.pipeline.vs_default_attributes_setup;
184
185 if (setup.index >= 16) {
186 LOG_ERROR(HW_GPU, "Invalid VS default attribute index %d", (int)setup.index);
187 break;
188 }
189
190 Math::Vec4<float24> attribute;
191
192 // NOTE: The destination component order indeed is "backwards"
193 attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8);
194 attribute.z = float24::FromRaw(((default_attr_write_buffer[0] & 0xFF) << 16) |
195 ((default_attr_write_buffer[1] >> 16) & 0xFFFF));
196 attribute.y = float24::FromRaw(((default_attr_write_buffer[1] & 0xFFFF) << 8) |
197 ((default_attr_write_buffer[2] >> 24) & 0xFF));
198 attribute.x = float24::FromRaw(default_attr_write_buffer[2] & 0xFFFFFF);
199
200 LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index,
201 attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(),
202 attribute.w.ToFloat32());
203
204 // TODO: Verify that this actually modifies the register!
205 if (setup.index < 15) {
206 g_state.input_default_attributes.attr[setup.index] = attribute;
207 setup.index++;
208 } else {
209 // Put each attribute into an immediate input buffer. When all specified immediate
210 // attributes are present, the Vertex Shader is invoked and everything is sent to
211 // the primitive assembler.
212
213 auto& immediate_input = g_state.immediate.input_vertex;
214 auto& immediate_attribute_id = g_state.immediate.current_attribute;
215
216 immediate_input.attr[immediate_attribute_id] = attribute;
217
218 if (immediate_attribute_id < regs.pipeline.max_input_attrib_index) {
219 immediate_attribute_id += 1;
220 } else {
221 MICROPROFILE_SCOPE(GPU_Drawing);
222 immediate_attribute_id = 0;
223
224 auto* shader_engine = Shader::GetEngine();
225 shader_engine->SetupBatch(g_state.vs, regs.vs.main_offset);
226
227 // Send to vertex shader
228 if (g_debug_context)
229 g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation,
230 static_cast<void*>(&immediate_input));
231 Shader::UnitState shader_unit;
232 Shader::AttributeBuffer output{};
233
234 shader_unit.LoadInput(regs.vs, immediate_input);
235 shader_engine->Run(g_state.vs, shader_unit);
236 shader_unit.WriteOutput(regs.vs, output);
237
238 // Send to geometry pipeline
239 if (g_state.immediate.reset_geometry_pipeline) {
240 g_state.geometry_pipeline.Reconfigure();
241 g_state.immediate.reset_geometry_pipeline = false;
242 }
243 ASSERT(!g_state.geometry_pipeline.NeedIndexInput());
244 g_state.geometry_pipeline.Setup(shader_engine);
245 g_state.geometry_pipeline.SubmitVertex(output);
246 }
247 }
248 }
249 break; 391 break;
250 }
251 392
252 case PICA_REG_INDEX(pipeline.gpu_mode): 393 case PICA_REG_INDEX(pipeline.gpu_mode):
253 if (regs.pipeline.gpu_mode == PipelineRegs::GPUMode::Configuring) { 394 // This register likely just enables vertex processing and doesn't need any special handling
254 MICROPROFILE_SCOPE(GPU_Drawing);
255
256 // Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring
257 VideoCore::g_renderer->Rasterizer()->DrawTriangles();
258
259 if (g_debug_context) {
260 g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
261 }
262 }
263 break; 395 break;
264 396
265 case PICA_REG_INDEX_WORKAROUND(pipeline.command_buffer.trigger[0], 0x23c): 397 case PICA_REG_INDEX_WORKAROUND(pipeline.command_buffer.trigger[0], 0x23c):
@@ -275,131 +407,9 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
275 407
276 // It seems like these trigger vertex rendering 408 // It seems like these trigger vertex rendering
277 case PICA_REG_INDEX(pipeline.trigger_draw): 409 case PICA_REG_INDEX(pipeline.trigger_draw):
278 case PICA_REG_INDEX(pipeline.trigger_draw_indexed): { 410 case PICA_REG_INDEX(pipeline.trigger_draw_indexed):
279 MICROPROFILE_SCOPE(GPU_Drawing); 411 Draw(id);
280
281#if PICA_LOG_TEV
282 DebugUtils::DumpTevStageConfig(regs.GetTevStages());
283#endif
284 if (g_debug_context)
285 g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr);
286
287 // Processes information about internal vertex attributes to figure out how a vertex is
288 // loaded.
289 // Later, these can be compiled and cached.
290 const u32 base_address = regs.pipeline.vertex_attributes.GetPhysicalBaseAddress();
291 VertexLoader loader(regs.pipeline);
292
293 // Load vertices
294 bool is_indexed = (id == PICA_REG_INDEX(pipeline.trigger_draw_indexed));
295
296 const auto& index_info = regs.pipeline.index_array;
297 const u8* index_address_8 = Memory::GetPhysicalPointer(base_address + index_info.offset);
298 const u16* index_address_16 = reinterpret_cast<const u16*>(index_address_8);
299 bool index_u16 = index_info.format != 0;
300
301 PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler;
302
303 if (g_debug_context && g_debug_context->recorder) {
304 for (int i = 0; i < 3; ++i) {
305 const auto texture = regs.texturing.GetTextures()[i];
306 if (!texture.enabled)
307 continue;
308
309 u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress());
310 g_debug_context->recorder->MemoryAccessed(
311 texture_data, Pica::TexturingRegs::NibblesPerPixel(texture.format) *
312 texture.config.width / 2 * texture.config.height,
313 texture.config.GetPhysicalAddress());
314 }
315 }
316
317 DebugUtils::MemoryAccessTracker memory_accesses;
318
319 // Simple circular-replacement vertex cache
320 // The size has been tuned for optimal balance between hit-rate and the cost of lookup
321 const size_t VERTEX_CACHE_SIZE = 32;
322 std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids;
323 std::array<Shader::AttributeBuffer, VERTEX_CACHE_SIZE> vertex_cache;
324 Shader::AttributeBuffer vs_output;
325
326 unsigned int vertex_cache_pos = 0;
327 vertex_cache_ids.fill(-1);
328
329 auto* shader_engine = Shader::GetEngine();
330 Shader::UnitState shader_unit;
331
332 shader_engine->SetupBatch(g_state.vs, regs.vs.main_offset);
333
334 g_state.geometry_pipeline.Reconfigure();
335 g_state.geometry_pipeline.Setup(shader_engine);
336 if (g_state.geometry_pipeline.NeedIndexInput())
337 ASSERT(is_indexed);
338
339 for (unsigned int index = 0; index < regs.pipeline.num_vertices; ++index) {
340 // Indexed rendering doesn't use the start offset
341 unsigned int vertex =
342 is_indexed ? (index_u16 ? index_address_16[index] : index_address_8[index])
343 : (index + regs.pipeline.vertex_offset);
344
345 // -1 is a common special value used for primitive restart. Since it's unknown if
346 // the PICA supports it, and it would mess up the caching, guard against it here.
347 ASSERT(vertex != -1);
348
349 bool vertex_cache_hit = false;
350
351 if (is_indexed) {
352 if (g_state.geometry_pipeline.NeedIndexInput()) {
353 g_state.geometry_pipeline.SubmitIndex(vertex);
354 continue;
355 }
356
357 if (g_debug_context && Pica::g_debug_context->recorder) {
358 int size = index_u16 ? 2 : 1;
359 memory_accesses.AddAccess(base_address + index_info.offset + size * index,
360 size);
361 }
362
363 for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) {
364 if (vertex == vertex_cache_ids[i]) {
365 vs_output = vertex_cache[i];
366 vertex_cache_hit = true;
367 break;
368 }
369 }
370 }
371
372 if (!vertex_cache_hit) {
373 // Initialize data for the current vertex
374 Shader::AttributeBuffer input;
375 loader.LoadVertex(base_address, index, vertex, input, memory_accesses);
376
377 // Send to vertex shader
378 if (g_debug_context)
379 g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation,
380 (void*)&input);
381 shader_unit.LoadInput(regs.vs, input);
382 shader_engine->Run(g_state.vs, shader_unit);
383 shader_unit.WriteOutput(regs.vs, vs_output);
384
385 if (is_indexed) {
386 vertex_cache[vertex_cache_pos] = vs_output;
387 vertex_cache_ids[vertex_cache_pos] = vertex;
388 vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE;
389 }
390 }
391
392 // Send to geometry pipeline
393 g_state.geometry_pipeline.SubmitVertex(vs_output);
394 }
395
396 for (auto& range : memory_accesses.ranges) {
397 g_debug_context->recorder->MemoryAccessed(Memory::GetPhysicalPointer(range.first),
398 range.second, range.first);
399 }
400
401 break; 412 break;
402 }
403 413
404 case PICA_REG_INDEX(gs.bool_uniforms): 414 case PICA_REG_INDEX(gs.bool_uniforms):
405 WriteUniformBoolReg(g_state.gs, g_state.regs.gs.bool_uniforms.Value()); 415 WriteUniformBoolReg(g_state.gs, g_state.regs.gs.bool_uniforms.Value());
@@ -632,6 +642,6 @@ void ProcessCommandList(const u32* list, u32 size) {
632 } 642 }
633} 643}
634 644
635} // namespace 645} // namespace CommandProcessor
636 646
637} // namespace 647} // namespace Pica
diff --git a/src/video_core/geometry_pipeline.cpp b/src/video_core/geometry_pipeline.cpp
index b146e2ecb..98ff2ccd3 100644
--- a/src/video_core/geometry_pipeline.cpp
+++ b/src/video_core/geometry_pipeline.cpp
@@ -105,7 +105,7 @@ public:
105 DEBUG_ASSERT(need_index); 105 DEBUG_ASSERT(need_index);
106 106
107 // The number of vertex input is put to the uniform register 107 // The number of vertex input is put to the uniform register
108 float24 vertex_num = float24::FromFloat32(val); 108 float24 vertex_num = float24::FromFloat32(static_cast<float>(val));
109 setup.uniforms.f[0] = Math::MakeVec(vertex_num, vertex_num, vertex_num, vertex_num); 109 setup.uniforms.f[0] = Math::MakeVec(vertex_num, vertex_num, vertex_num, vertex_num);
110 110
111 // The second uniform register and so on are used for receiving input vertices 111 // The second uniform register and so on are used for receiving input vertices
diff --git a/src/video_core/pica_types.h b/src/video_core/pica_types.h
index 5d7e10066..2eafa7e9e 100644
--- a/src/video_core/pica_types.h
+++ b/src/video_core/pica_types.h
@@ -58,11 +58,12 @@ public:
58 } 58 }
59 59
60 Float<M, E> operator*(const Float<M, E>& flt) const { 60 Float<M, E> operator*(const Float<M, E>& flt) const {
61 if ((this->value == 0.f && !std::isnan(flt.value)) || 61 float result = value * flt.ToFloat32();
62 (flt.value == 0.f && !std::isnan(this->value))) 62 // PICA gives 0 instead of NaN when multiplying by inf
63 // PICA gives 0 instead of NaN when multiplying by inf 63 if (!std::isnan(value) && !std::isnan(flt.ToFloat32()))
64 return Zero(); 64 if (std::isnan(result))
65 return Float<M, E>::FromFloat32(ToFloat32() * flt.ToFloat32()); 65 result = 0.f;
66 return Float<M, E>::FromFloat32(result);
66 } 67 }
67 68
68 Float<M, E> operator/(const Float<M, E>& flt) const { 69 Float<M, E> operator/(const Float<M, E>& flt) const {
@@ -78,12 +79,7 @@ public:
78 } 79 }
79 80
80 Float<M, E>& operator*=(const Float<M, E>& flt) { 81 Float<M, E>& operator*=(const Float<M, E>& flt) {
81 if ((this->value == 0.f && !std::isnan(flt.value)) || 82 value = operator*(flt).value;
82 (flt.value == 0.f && !std::isnan(this->value)))
83 // PICA gives 0 instead of NaN when multiplying by inf
84 *this = Zero();
85 else
86 value *= flt.ToFloat32();
87 return *this; 83 return *this;
88 } 84 }
89 85
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 06a905766..5770ae08f 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -267,9 +267,9 @@ void OpenGLState::Apply() const {
267 for (size_t i = 0; i < clip_distance.size(); ++i) { 267 for (size_t i = 0; i < clip_distance.size(); ++i) {
268 if (clip_distance[i] != cur_state.clip_distance[i]) { 268 if (clip_distance[i] != cur_state.clip_distance[i]) {
269 if (clip_distance[i]) { 269 if (clip_distance[i]) {
270 glEnable(GL_CLIP_DISTANCE0 + i); 270 glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i));
271 } else { 271 } else {
272 glDisable(GL_CLIP_DISTANCE0 + i); 272 glDisable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i));
273 } 273 }
274 } 274 }
275 } 275 }
diff --git a/src/video_core/swrasterizer/clipper.cpp b/src/video_core/swrasterizer/clipper.cpp
index a52129eb7..c1ed48398 100644
--- a/src/video_core/swrasterizer/clipper.cpp
+++ b/src/video_core/swrasterizer/clipper.cpp
@@ -98,7 +98,7 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu
98 98
99 auto FlipQuaternionIfOpposite = [](auto& a, const auto& b) { 99 auto FlipQuaternionIfOpposite = [](auto& a, const auto& b) {
100 if (Math::Dot(a, b) < float24::Zero()) 100 if (Math::Dot(a, b) < float24::Zero())
101 a = -a; 101 a = a * float24::FromFloat32(-1.0f);
102 }; 102 };
103 103
104 // Flip the quaternions if they are opposite to prevent interpolating them over the wrong 104 // Flip the quaternions if they are opposite to prevent interpolating them over the wrong
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index 7ce83a055..d8567f314 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -8,17 +8,11 @@
8 8
9namespace VideoCore { 9namespace VideoCore {
10 10
11/** 11// 8x8 Z-Order coordinate from 2D coordinates
12 * Interleave the lower 3 bits of each coordinate to get the intra-block offsets, which are
13 * arranged in a Z-order curve. More details on the bit manipulation at:
14 * https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
15 */
16static inline u32 MortonInterleave(u32 x, u32 y) { 12static inline u32 MortonInterleave(u32 x, u32 y) {
17 u32 i = (x & 7) | ((y & 7) << 8); // ---- -210 13 static const u32 xlut[] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15};
18 i = (i ^ (i << 2)) & 0x1313; // ---2 --10 14 static const u32 ylut[] = {0x00, 0x02, 0x08, 0x0a, 0x20, 0x22, 0x28, 0x2a};
19 i = (i ^ (i << 1)) & 0x1515; // ---2 -1-0 15 return xlut[x % 8] + ylut[y % 8];
20 i = (i | (i >> 7)) & 0x3F;
21 return i;
22} 16}
23 17
24/** 18/**