summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt10
-rw-r--r--src/citra/CMakeLists.txt10
-rw-r--r--src/citra/citra.cpp3
-rw-r--r--src/citra/config.cpp4
-rw-r--r--src/citra/config.h4
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp14
-rw-r--r--src/citra_qt/CMakeLists.txt6
-rw-r--r--src/citra_qt/bootmanager.cpp11
-rw-r--r--src/citra_qt/bootmanager.h3
-rw-r--r--src/citra_qt/debugger/graphics/graphics_cmdlists.cpp1
-rw-r--r--src/citra_qt/debugger/wait_tree.cpp1
-rw-r--r--src/citra_qt/debugger/wait_tree.h5
-rw-r--r--src/citra_qt/game_list_p.h2
-rw-r--r--src/citra_qt/main.cpp124
-rw-r--r--src/citra_qt/main.h3
-rw-r--r--src/common/CMakeLists.txt5
-rw-r--r--src/common/bit_field.h65
-rw-r--r--src/common/break_points.cpp1
-rw-r--r--src/common/common_funcs.h2
-rw-r--r--src/common/hash.cpp6
-rw-r--r--src/common/x64/cpu_detect.cpp2
-rw-r--r--src/core/3ds.h21
-rw-r--r--src/core/CMakeLists.txt24
-rw-r--r--src/core/core.cpp29
-rw-r--r--src/core/core.h17
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp12
-rw-r--r--src/core/file_sys/archive_ncch.cpp39
-rw-r--r--src/core/file_sys/archive_source_sd_savedata.cpp7
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp4
-rw-r--r--src/core/file_sys/disk_archive.cpp7
-rw-r--r--src/core/file_sys/errors.h127
-rw-r--r--src/core/frontend/emu_window.cpp8
-rw-r--r--src/core/frontend/emu_window.h2
-rw-r--r--src/core/frontend/framebuffer_layout.cpp (renamed from src/common/framebuffer_layout.cpp)20
-rw-r--r--src/core/frontend/framebuffer_layout.h (renamed from src/common/framebuffer_layout.h)11
-rw-r--r--src/core/hle/applets/mii_selector.cpp1
-rw-r--r--src/core/hle/applets/swkbd.cpp1
-rw-r--r--src/core/hle/function_wrappers.h2
-rw-r--r--src/core/hle/ipc.h7
-rw-r--r--src/core/hle/ipc_helpers.h2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp7
-rw-r--r--src/core/hle/kernel/address_arbiter.h1
-rw-r--r--src/core/hle/kernel/client_port.cpp16
-rw-r--r--src/core/hle/kernel/client_port.h1
-rw-r--r--src/core/hle/kernel/client_session.cpp24
-rw-r--r--src/core/hle/kernel/client_session.h3
-rw-r--r--src/core/hle/kernel/errors.h98
-rw-r--r--src/core/hle/kernel/event.h1
-rw-r--r--src/core/hle/kernel/handle_table.cpp97
-rw-r--r--src/core/hle/kernel/handle_table.h126
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp26
-rw-r--r--src/core/hle/kernel/hle_ipc.h87
-rw-r--r--src/core/hle/kernel/kernel.cpp163
-rw-r--r--src/core/hle/kernel/kernel.h188
-rw-r--r--src/core/hle/kernel/memory.cpp1
-rw-r--r--src/core/hle/kernel/mutex.h1
-rw-r--r--src/core/hle/kernel/process.cpp1
-rw-r--r--src/core/hle/kernel/resource_limit.cpp1
-rw-r--r--src/core/hle/kernel/semaphore.cpp7
-rw-r--r--src/core/hle/kernel/semaphore.h2
-rw-r--r--src/core/hle/kernel/server_port.cpp4
-rw-r--r--src/core/hle/kernel/server_port.h22
-rw-r--r--src/core/hle/kernel/server_session.cpp19
-rw-r--r--src/core/hle/kernel/server_session.h30
-rw-r--r--src/core/hle/kernel/shared_memory.cpp16
-rw-r--r--src/core/hle/kernel/thread.cpp25
-rw-r--r--src/core/hle/kernel/thread.h3
-rw-r--r--src/core/hle/kernel/timer.cpp1
-rw-r--r--src/core/hle/kernel/timer.h1
-rw-r--r--src/core/hle/kernel/vm_manager.cpp1
-rw-r--r--src/core/hle/kernel/vm_manager.h8
-rw-r--r--src/core/hle/kernel/wait_object.cpp99
-rw-r--r--src/core/hle/kernel/wait_object.h67
-rw-r--r--src/core/hle/result.h95
-rw-r--r--src/core/hle/service/ac/ac.cpp5
-rw-r--r--src/core/hle/service/am/am.cpp6
-rw-r--r--src/core/hle/service/apt/apt.cpp2
-rw-r--r--src/core/hle/service/apt/apt.h2
-rw-r--r--src/core/hle/service/boss/boss.cpp7
-rw-r--r--src/core/hle/service/cam/cam.cpp549
-rw-r--r--src/core/hle/service/cam/cam.h13
-rw-r--r--src/core/hle/service/cecd/cecd.cpp3
-rw-r--r--src/core/hle/service/cfg/cfg.cpp8
-rw-r--r--src/core/hle/service/csnd_snd.cpp3
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.cpp1
-rw-r--r--src/core/hle/service/dsp_dsp.cpp10
-rw-r--r--src/core/hle/service/err_f.cpp4
-rw-r--r--src/core/hle/service/frd/frd.cpp5
-rw-r--r--src/core/hle/service/fs/archive.cpp52
-rw-r--r--src/core/hle/service/fs/archive.h6
-rw-r--r--src/core/hle/service/fs/fs_user.cpp49
-rw-r--r--src/core/hle/service/gsp_gpu.cpp41
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/ir/ir_rst.cpp1
-rw-r--r--src/core/hle/service/ir/ir_user.cpp4
-rw-r--r--src/core/hle/service/ldr_ro/ldr_ro.cpp1
-rw-r--r--src/core/hle/service/mic_u.cpp2
-rw-r--r--src/core/hle/service/ndm/ndm.cpp2
-rw-r--r--src/core/hle/service/nfc/nfc.cpp2
-rw-r--r--src/core/hle/service/nim/nim.cpp1
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp99
-rw-r--r--src/core/hle/service/nwm/uds_beacon.cpp7
-rw-r--r--src/core/hle/service/ptm/ptm.cpp3
-rw-r--r--src/core/hle/service/service.cpp131
-rw-r--r--src/core/hle/service/service.h208
-rw-r--r--src/core/hle/service/sm/sm.cpp69
-rw-r--r--src/core/hle/service/sm/sm.h55
-rw-r--r--src/core/hle/service/sm/srv.cpp204
-rw-r--r--src/core/hle/service/sm/srv.h37
-rw-r--r--src/core/hle/service/soc_u.cpp1
-rw-r--r--src/core/hle/service/srv.cpp190
-rw-r--r--src/core/hle/service/srv.h24
-rw-r--r--src/core/hle/service/ssl_c.cpp2
-rw-r--r--src/core/hle/service/y2r_u.cpp2
-rw-r--r--src/core/hle/svc.cpp103
-rw-r--r--src/core/loader/loader.h7
-rw-r--r--src/core/loader/ncch.cpp12
-rw-r--r--src/core/loader/ncch.h4
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/tracer/recorder.cpp2
-rw-r--r--src/core/tracer/recorder.h2
-rw-r--r--src/input_common/CMakeLists.txt7
-rw-r--r--src/tests/CMakeLists.txt9
-rw-r--r--src/video_core/CMakeLists.txt11
-rw-r--r--src/video_core/regs_texturing.h27
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp20
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp87
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp10
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h3
-rw-r--r--src/video_core/swrasterizer/rasterizer.cpp53
-rw-r--r--src/video_core/video_core.h15
131 files changed, 2538 insertions, 1514 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index a72a907ef..0ad86bb7a 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -27,20 +27,18 @@ set(HEADERS
27 time_stretch.h 27 time_stretch.h
28 ) 28 )
29 29
30include_directories(../../externals/soundtouch/include)
31
32if(SDL2_FOUND) 30if(SDL2_FOUND)
33 set(SRCS ${SRCS} sdl2_sink.cpp) 31 set(SRCS ${SRCS} sdl2_sink.cpp)
34 set(HEADERS ${HEADERS} sdl2_sink.h) 32 set(HEADERS ${HEADERS} sdl2_sink.h)
35 include_directories(${SDL2_INCLUDE_DIR})
36endif() 33endif()
37 34
38create_directory_groups(${SRCS} ${HEADERS}) 35create_directory_groups(${SRCS} ${HEADERS})
39 36
40add_library(audio_core STATIC ${SRCS} ${HEADERS}) 37add_library(audio_core STATIC ${SRCS} ${HEADERS})
41target_link_libraries(audio_core SoundTouch) 38target_link_libraries(audio_core PUBLIC common core)
39target_link_libraries(audio_core PRIVATE SoundTouch)
42 40
43if(SDL2_FOUND) 41if(SDL2_FOUND)
44 target_link_libraries(audio_core ${SDL2_LIBRARY}) 42 target_link_libraries(audio_core PRIVATE SDL2)
45 set_property(TARGET audio_core APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2) 43 target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
46endif() 44endif()
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt
index 47231ba71..d72d2b5f4 100644
--- a/src/citra/CMakeLists.txt
+++ b/src/citra/CMakeLists.txt
@@ -15,15 +15,13 @@ set(HEADERS
15 15
16create_directory_groups(${SRCS} ${HEADERS}) 16create_directory_groups(${SRCS} ${HEADERS})
17 17
18include_directories(${SDL2_INCLUDE_DIR})
19
20add_executable(citra ${SRCS} ${HEADERS}) 18add_executable(citra ${SRCS} ${HEADERS})
21target_link_libraries(citra core video_core audio_core common input_common) 19target_link_libraries(citra PRIVATE common core input_common)
22target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad) 20target_link_libraries(citra PRIVATE inih glad)
23if (MSVC) 21if (MSVC)
24 target_link_libraries(citra getopt) 22 target_link_libraries(citra PRIVATE getopt)
25endif() 23endif()
26target_link_libraries(citra ${PLATFORM_LIBRARIES} Threads::Threads) 24target_link_libraries(citra PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads)
27 25
28if(UNIX AND NOT APPLE) 26if(UNIX AND NOT APPLE)
29 install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 27 install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index 76f5caeb1..dd357ff72 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -33,7 +33,6 @@
33#include "core/gdbstub/gdbstub.h" 33#include "core/gdbstub/gdbstub.h"
34#include "core/loader/loader.h" 34#include "core/loader/loader.h"
35#include "core/settings.h" 35#include "core/settings.h"
36#include "video_core/video_core.h"
37 36
38static void PrintHelp(const char* argv0) { 37static void PrintHelp(const char* argv0) {
39 std::cout << "Usage: " << argv0 38 std::cout << "Usage: " << argv0
@@ -145,7 +144,7 @@ int main(int argc, char** argv) {
145 LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before " 144 LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before "
146 "being used with Citra. \n\n For more information on dumping and " 145 "being used with Citra. \n\n For more information on dumping and "
147 "decrypting games, please refer to: " 146 "decrypting games, please refer to: "
148 "https://citra-emu.org/wiki/Dumping-Game-Cartridges"); 147 "https://citra-emu.org/wiki/dumping-game-cartridges/");
149 return -1; 148 return -1;
150 case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: 149 case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
151 LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported."); 150 LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index a4162e9ad..f08b4069c 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -5,11 +5,11 @@
5#include <memory> 5#include <memory>
6#include <SDL.h> 6#include <SDL.h>
7#include <inih/cpp/INIReader.h> 7#include <inih/cpp/INIReader.h>
8#include "citra/config.h"
8#include "citra/default_ini.h" 9#include "citra/default_ini.h"
9#include "common/file_util.h" 10#include "common/file_util.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "common/param_package.h" 12#include "common/param_package.h"
12#include "config.h"
13#include "core/settings.h" 13#include "core/settings.h"
14#include "input_common/main.h" 14#include "input_common/main.h"
15 15
@@ -21,6 +21,8 @@ Config::Config() {
21 Reload(); 21 Reload();
22} 22}
23 23
24Config::~Config() = default;
25
24bool Config::LoadINI(const std::string& default_contents, bool retry) { 26bool Config::LoadINI(const std::string& default_contents, bool retry) {
25 const char* location = this->sdl2_config_loc.c_str(); 27 const char* location = this->sdl2_config_loc.c_str();
26 if (sdl2_config->ParseError() < 0) { 28 if (sdl2_config->ParseError() < 0) {
diff --git a/src/citra/config.h b/src/citra/config.h
index b1c31f59c..abc90f642 100644
--- a/src/citra/config.h
+++ b/src/citra/config.h
@@ -6,7 +6,8 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <inih/cpp/INIReader.h> 9
10class INIReader;
10 11
11class Config { 12class Config {
12 std::unique_ptr<INIReader> sdl2_config; 13 std::unique_ptr<INIReader> sdl2_config;
@@ -17,6 +18,7 @@ class Config {
17 18
18public: 19public:
19 Config(); 20 Config();
21 ~Config();
20 22
21 void Reload(); 23 void Reload();
22}; 24};
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
index 6bc0b0d00..47aadd60c 100644
--- a/src/citra/emu_window/emu_window_sdl2.cpp
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -12,10 +12,10 @@
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/scm_rev.h" 13#include "common/scm_rev.h"
14#include "common/string_util.h" 14#include "common/string_util.h"
15#include "core/3ds.h"
15#include "core/settings.h" 16#include "core/settings.h"
16#include "input_common/keyboard.h" 17#include "input_common/keyboard.h"
17#include "input_common/main.h" 18#include "input_common/main.h"
18#include "video_core/video_core.h"
19 19
20void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 20void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
21 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); 21 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
@@ -80,12 +80,12 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
80 80
81 std::string window_title = Common::StringFromFormat("Citra %s| %s-%s ", Common::g_build_name, 81 std::string window_title = Common::StringFromFormat("Citra %s| %s-%s ", Common::g_build_name,
82 Common::g_scm_branch, Common::g_scm_desc); 82 Common::g_scm_branch, Common::g_scm_desc);
83 render_window = SDL_CreateWindow( 83 render_window =
84 window_title.c_str(), 84 SDL_CreateWindow(window_title.c_str(),
85 SDL_WINDOWPOS_UNDEFINED, // x position 85 SDL_WINDOWPOS_UNDEFINED, // x position
86 SDL_WINDOWPOS_UNDEFINED, // y position 86 SDL_WINDOWPOS_UNDEFINED, // y position
87 VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight, 87 Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight,
88 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); 88 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
89 89
90 if (render_window == nullptr) { 90 if (render_window == nullptr) {
91 LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting..."); 91 LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting...");
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 4e837668e..4841cbf05 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -91,9 +91,9 @@ if (APPLE)
91else() 91else()
92 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) 92 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
93endif() 93endif()
94target_link_libraries(citra-qt core video_core audio_core common input_common) 94target_link_libraries(citra-qt PRIVATE audio_core common core input_common video_core)
95target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) 95target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets)
96target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads) 96target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
97 97
98if(UNIX AND NOT APPLE) 98if(UNIX AND NOT APPLE)
99 install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 99 install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index bae576d6a..a8a4aed8b 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -12,12 +12,11 @@
12#include "common/microprofile.h" 12#include "common/microprofile.h"
13#include "common/scm_rev.h" 13#include "common/scm_rev.h"
14#include "common/string_util.h" 14#include "common/string_util.h"
15#include "core/3ds.h"
15#include "core/core.h" 16#include "core/core.h"
16#include "core/settings.h" 17#include "core/settings.h"
17#include "input_common/keyboard.h" 18#include "input_common/keyboard.h"
18#include "input_common/main.h" 19#include "input_common/main.h"
19#include "video_core/debug_utils/debug_utils.h"
20#include "video_core/video_core.h"
21 20
22EmuThread::EmuThread(GRenderWindow* render_window) 21EmuThread::EmuThread(GRenderWindow* render_window)
23 : exec_step(false), running(false), stop_run(false), render_window(render_window) {} 22 : exec_step(false), running(false), stop_run(false), render_window(render_window) {}
@@ -38,7 +37,10 @@ void EmuThread::run() {
38 if (!was_active) 37 if (!was_active)
39 emit DebugModeLeft(); 38 emit DebugModeLeft();
40 39
41 Core::System::GetInstance().RunLoop(); 40 Core::System::ResultStatus result = Core::System::GetInstance().RunLoop();
41 if (result != Core::System::ResultStatus::Success) {
42 emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails());
43 }
42 44
43 was_active = running || exec_step; 45 was_active = running || exec_step;
44 if (!was_active && !stop_run) 46 if (!was_active && !stop_run)
@@ -266,8 +268,7 @@ void GRenderWindow::InitRenderTarget() {
266 child = new GGLWidgetInternal(fmt, this); 268 child = new GGLWidgetInternal(fmt, this);
267 QBoxLayout* layout = new QHBoxLayout(this); 269 QBoxLayout* layout = new QHBoxLayout(this);
268 270
269 resize(VideoCore::kScreenTopWidth, 271 resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight);
270 VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
271 layout->addWidget(child); 272 layout->addWidget(child);
272 layout->setMargin(0); 273 layout->setMargin(0);
273 setLayout(layout); 274 setLayout(layout);
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index 9d39f1af8..4b3a3b3cc 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -10,6 +10,7 @@
10#include <QGLWidget> 10#include <QGLWidget>
11#include <QThread> 11#include <QThread>
12#include "common/thread.h" 12#include "common/thread.h"
13#include "core/core.h"
13#include "core/frontend/emu_window.h" 14#include "core/frontend/emu_window.h"
14#include "core/frontend/motion_emu.h" 15#include "core/frontend/motion_emu.h"
15 16
@@ -97,6 +98,8 @@ signals:
97 * Qt::BlockingQueuedConnection (additionally block source thread until slot returns) 98 * Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
98 */ 99 */
99 void DebugModeLeft(); 100 void DebugModeLeft();
101
102 void ErrorThrown(Core::System::ResultStatus, std::string);
100}; 103};
101 104
102class GRenderWindow : public QWidget, public EmuWindow { 105class GRenderWindow : public QWidget, public EmuWindow {
diff --git a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
index c68fe753b..7d06ec28a 100644
--- a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
@@ -17,6 +17,7 @@
17#include "citra_qt/util/spinbox.h" 17#include "citra_qt/util/spinbox.h"
18#include "citra_qt/util/util.h" 18#include "citra_qt/util/util.h"
19#include "common/vector_math.h" 19#include "common/vector_math.h"
20#include "core/memory.h"
20#include "video_core/debug_utils/debug_utils.h" 21#include "video_core/debug_utils/debug_utils.h"
21#include "video_core/pica_state.h" 22#include "video_core/pica_state.h"
22#include "video_core/regs.h" 23#include "video_core/regs.h"
diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp
index b6ecf3819..8c244b6b2 100644
--- a/src/citra_qt/debugger/wait_tree.cpp
+++ b/src/citra_qt/debugger/wait_tree.cpp
@@ -10,6 +10,7 @@
10#include "core/hle/kernel/semaphore.h" 10#include "core/hle/kernel/semaphore.h"
11#include "core/hle/kernel/thread.h" 11#include "core/hle/kernel/thread.h"
12#include "core/hle/kernel/timer.h" 12#include "core/hle/kernel/timer.h"
13#include "core/hle/kernel/wait_object.h"
13 14
14WaitTreeItem::~WaitTreeItem() {} 15WaitTreeItem::~WaitTreeItem() {}
15 16
diff --git a/src/citra_qt/debugger/wait_tree.h b/src/citra_qt/debugger/wait_tree.h
index ee9708fc1..2b38712b9 100644
--- a/src/citra_qt/debugger/wait_tree.h
+++ b/src/citra_qt/debugger/wait_tree.h
@@ -4,12 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <boost/container/flat_set.hpp>
8
9#include <QAbstractItemModel> 7#include <QAbstractItemModel>
10#include <QDockWidget> 8#include <QDockWidget>
11#include <QTreeView> 9#include <QTreeView>
12 10#include <boost/container/flat_set.hpp>
13#include "core/core.h" 11#include "core/core.h"
14#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
15 13
@@ -20,7 +18,6 @@ class WaitObject;
20class Event; 18class Event;
21class Mutex; 19class Mutex;
22class Semaphore; 20class Semaphore;
23class Session;
24class Thread; 21class Thread;
25class Timer; 22class Timer;
26} 23}
diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h
index d1118ff7f..12212a3a4 100644
--- a/src/citra_qt/game_list_p.h
+++ b/src/citra_qt/game_list_p.h
@@ -10,10 +10,8 @@
10#include <QStandardItem> 10#include <QStandardItem>
11#include <QString> 11#include <QString>
12#include "citra_qt/util/util.h" 12#include "citra_qt/util/util.h"
13#include "common/color.h"
14#include "common/string_util.h" 13#include "common/string_util.h"
15#include "core/loader/smdh.h" 14#include "core/loader/smdh.h"
16#include "video_core/utils.h"
17 15
18/** 16/**
19 * Gets the game icon from SMDH data. 17 * Gets the game icon from SMDH data.
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index d7fad555f..4f5b2ddab 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -43,7 +43,6 @@
43#include "core/gdbstub/gdbstub.h" 43#include "core/gdbstub/gdbstub.h"
44#include "core/loader/loader.h" 44#include "core/loader/loader.h"
45#include "core/settings.h" 45#include "core/settings.h"
46#include "video_core/video_core.h"
47 46
48#ifdef QT_STATICPLUGIN 47#ifdef QT_STATICPLUGIN
49Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); 48Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
@@ -94,6 +93,14 @@ void GMainWindow::InitializeWidgets() {
94 ui.horizontalLayout->addWidget(game_list); 93 ui.horizontalLayout->addWidget(game_list);
95 94
96 // Create status bar 95 // Create status bar
96 message_label = new QLabel();
97 // Configured separately for left alignment
98 message_label->setVisible(false);
99 message_label->setFrameStyle(QFrame::NoFrame);
100 message_label->setContentsMargins(4, 0, 4, 0);
101 message_label->setAlignment(Qt::AlignLeft);
102 statusBar()->addPermanentWidget(message_label, 1);
103
97 emu_speed_label = new QLabel(); 104 emu_speed_label = new QLabel();
98 emu_speed_label->setToolTip(tr("Current emulation speed. Values higher or lower than 100% " 105 emu_speed_label->setToolTip(tr("Current emulation speed. Values higher or lower than 100% "
99 "indicate emulation is running faster or slower than a 3DS.")); 106 "indicate emulation is running faster or slower than a 3DS."));
@@ -109,7 +116,7 @@ void GMainWindow::InitializeWidgets() {
109 label->setVisible(false); 116 label->setVisible(false);
110 label->setFrameStyle(QFrame::NoFrame); 117 label->setFrameStyle(QFrame::NoFrame);
111 label->setContentsMargins(4, 0, 4, 0); 118 label->setContentsMargins(4, 0, 4, 0);
112 statusBar()->addPermanentWidget(label); 119 statusBar()->addPermanentWidget(label, 0);
113 } 120 }
114 statusBar()->setVisible(true); 121 statusBar()->setVisible(true);
115 setStyleSheet("QStatusBar::item{border: none;}"); 122 setStyleSheet("QStatusBar::item{border: none;}");
@@ -301,9 +308,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
301 render_window->MakeCurrent(); 308 render_window->MakeCurrent();
302 309
303 if (!gladLoadGL()) { 310 if (!gladLoadGL()) {
304 QMessageBox::critical(this, tr("Error while starting Citra!"), 311 QMessageBox::critical(this, tr("Error while initializing OpenGL 3.3 Core!"),
305 tr("Failed to initialize the video core!\n\n" 312 tr("Your GPU may not support OpenGL 3.3, or you do not"
306 "Please ensure that your GPU supports OpenGL 3.3 and that you "
307 "have the latest graphics driver.")); 313 "have the latest graphics driver."));
308 return false; 314 return false;
309 } 315 }
@@ -328,18 +334,17 @@ bool GMainWindow::LoadROM(const QString& filename) {
328 break; 334 break;
329 335
330 case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: { 336 case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: {
331 // Build the MessageBox ourselves to have clickable link 337 QMessageBox::critical(
332 QMessageBox popup_error; 338 this, tr("Error while loading ROM!"),
333 popup_error.setTextFormat(Qt::RichText);
334 popup_error.setWindowTitle(tr("Error while loading ROM!"));
335 popup_error.setText(
336 tr("The game that you are trying to load must be decrypted before being used with " 339 tr("The game that you are trying to load must be decrypted before being used with "
337 "Citra.<br/><br/>" 340 "Citra. A real 3DS is required.<br/><br/>"
338 "For more information on dumping and decrypting games, please see: <a " 341 "For more information on dumping and decrypting games, please see the following "
339 "href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://" 342 "wiki pages: <ul>"
340 "citra-emu.org/wiki/Dumping-Game-Cartridges</a>")); 343 "<li><a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Dumping Game "
341 popup_error.setIcon(QMessageBox::Critical); 344 "Cartridges</a></li>"
342 popup_error.exec(); 345 "<li><a href='https://citra-emu.org/wiki/dumping-installed-titles/'>Dumping "
346 "Installed Titles</a></li>"
347 "</ul>"));
343 break; 348 break;
344 } 349 }
345 case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: 350 case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
@@ -347,8 +352,23 @@ bool GMainWindow::LoadROM(const QString& filename) {
347 tr("The ROM format is not supported.")); 352 tr("The ROM format is not supported."));
348 break; 353 break;
349 354
355 case Core::System::ResultStatus::ErrorVideoCore:
356 QMessageBox::critical(
357 this, tr("An error occured in the video core."),
358 tr("Citra has encountered an error while running the video core, please see the "
359 "log for more details."
360 "For more information on accessing the log, please see the following page: "
361 "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
362 "to "
363 "Upload the Log File</a>."
364 "Ensure that you have the latest graphics drivers for your GPU."));
365
366 break;
367
350 default: 368 default:
351 QMessageBox::critical(this, tr("Error while loading ROM!"), tr("Unknown error!")); 369 QMessageBox::critical(
370 this, tr("Error while loading ROM!"),
371 tr("An unknown error occured. Please see the log for more details."));
352 break; 372 break;
353 } 373 }
354 return false; 374 return false;
@@ -425,6 +445,7 @@ void GMainWindow::ShutdownGame() {
425 445
426 // Disable status bar updates 446 // Disable status bar updates
427 status_bar_update_timer.stop(); 447 status_bar_update_timer.stop();
448 message_label->setVisible(false);
428 emu_speed_label->setVisible(false); 449 emu_speed_label->setVisible(false);
429 game_fps_label->setVisible(false); 450 game_fps_label->setVisible(false);
430 emu_frametime_label->setVisible(false); 451 emu_frametime_label->setVisible(false);
@@ -531,6 +552,10 @@ void GMainWindow::OnMenuRecentFile() {
531 552
532void GMainWindow::OnStartGame() { 553void GMainWindow::OnStartGame() {
533 emu_thread->SetRunning(true); 554 emu_thread->SetRunning(true);
555 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
556 qRegisterMetaType<std::string>("std::string");
557 connect(emu_thread.get(), SIGNAL(ErrorThrown(Core::System::ResultStatus, std::string)), this,
558 SLOT(OnCoreError(Core::System::ResultStatus, std::string)));
534 559
535 ui.action_Start->setEnabled(false); 560 ui.action_Start->setEnabled(false);
536 ui.action_Start->setText(tr("Continue")); 561 ui.action_Start->setText(tr("Continue"));
@@ -623,11 +648,74 @@ void GMainWindow::UpdateStatusBar() {
623 emu_frametime_label->setVisible(true); 648 emu_frametime_label->setVisible(true);
624} 649}
625 650
651void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
652 QMessageBox::StandardButton answer;
653 QString status_message;
654 const QString common_message =
655 tr("The game you are trying to load requires additional files from your 3DS to be dumped "
656 "before playing.<br/><br/>For more information on dumping these files, please see the "
657 "following wiki page: <a "
658 "href='https://citra-emu.org/wiki/"
659 "dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>Dumping System "
660 "Archives and the Shared Fonts from a 3DS Console</a>.<br/><br/>Would you like to quit "
661 "back to the game list? Continuing emulation may result in crashes, corrupted save "
662 "data, or other bugs.");
663 switch (result) {
664 case Core::System::ResultStatus::ErrorSystemFiles: {
665 QString message = "Citra was unable to locate a 3DS system archive";
666 if (!details.empty()) {
667 message.append(tr(": %1. ").arg(details.c_str()));
668 } else {
669 message.append(". ");
670 }
671 message.append(common_message);
672
673 answer = QMessageBox::question(this, tr("System Archive Not Found"), message,
674 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
675 status_message = "System Archive Missing";
676 break;
677 }
678
679 case Core::System::ResultStatus::ErrorSharedFont: {
680 QString message = tr("Citra was unable to locate the 3DS shared fonts. ");
681 message.append(common_message);
682 answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message,
683 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
684 status_message = "Shared Font Missing";
685 break;
686 }
687
688 default:
689 answer = QMessageBox::question(
690 this, tr("Fatal Error"),
691 tr("Citra has encountered a fatal error, please see the log for more details. "
692 "For more information on accessing the log, please see the following page: "
693 "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to "
694 "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? "
695 "Continuing emulation may result in crashes, corrupted save data, or other bugs."),
696 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
697 status_message = "Fatal Error encountered";
698 break;
699 }
700
701 if (answer == QMessageBox::Yes) {
702 if (emu_thread) {
703 ShutdownGame();
704 }
705 } else {
706 // Only show the message if the game is still running.
707 if (emu_thread) {
708 message_label->setText(status_message);
709 message_label->setVisible(true);
710 }
711 }
712}
713
626bool GMainWindow::ConfirmClose() { 714bool GMainWindow::ConfirmClose() {
627 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) 715 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
628 return true; 716 return true;
629 717
630 auto answer = 718 QMessageBox::StandardButton answer =
631 QMessageBox::question(this, tr("Citra"), tr("Are you sure you want to close Citra?"), 719 QMessageBox::question(this, tr("Citra"), tr("Are you sure you want to close Citra?"),
632 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 720 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
633 return answer != QMessageBox::No; 721 return answer != QMessageBox::No;
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index cb2e87cbd..952a50974 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -8,6 +8,7 @@
8#include <memory> 8#include <memory>
9#include <QMainWindow> 9#include <QMainWindow>
10#include <QTimer> 10#include <QTimer>
11#include "core/core.h"
11#include "ui_main.h" 12#include "ui_main.h"
12 13
13class Config; 14class Config;
@@ -125,6 +126,7 @@ private slots:
125 void OnDisplayTitleBars(bool); 126 void OnDisplayTitleBars(bool);
126 void ToggleWindowMode(); 127 void ToggleWindowMode();
127 void OnCreateGraphicsSurfaceViewer(); 128 void OnCreateGraphicsSurfaceViewer();
129 void OnCoreError(Core::System::ResultStatus, std::string);
128 130
129private: 131private:
130 void UpdateStatusBar(); 132 void UpdateStatusBar();
@@ -135,6 +137,7 @@ private:
135 GameList* game_list; 137 GameList* game_list;
136 138
137 // Status bar elements 139 // Status bar elements
140 QLabel* message_label = nullptr;
138 QLabel* emu_speed_label = nullptr; 141 QLabel* emu_speed_label = nullptr;
139 QLabel* game_fps_label = nullptr; 142 QLabel* game_fps_label = nullptr;
140 QLabel* emu_frametime_label = nullptr; 143 QLabel* emu_frametime_label = nullptr;
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 6905d2d50..7e83e64b0 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -27,7 +27,6 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU
27set(SRCS 27set(SRCS
28 break_points.cpp 28 break_points.cpp
29 file_util.cpp 29 file_util.cpp
30 framebuffer_layout.cpp
31 hash.cpp 30 hash.cpp
32 logging/filter.cpp 31 logging/filter.cpp
33 logging/text_formatter.cpp 32 logging/text_formatter.cpp
@@ -56,7 +55,6 @@ set(HEADERS
56 common_paths.h 55 common_paths.h
57 common_types.h 56 common_types.h
58 file_util.h 57 file_util.h
59 framebuffer_layout.h
60 hash.h 58 hash.h
61 linear_disk_cache.h 59 linear_disk_cache.h
62 logging/text_formatter.h 60 logging/text_formatter.h
@@ -97,6 +95,7 @@ endif()
97create_directory_groups(${SRCS} ${HEADERS}) 95create_directory_groups(${SRCS} ${HEADERS})
98 96
99add_library(common STATIC ${SRCS} ${HEADERS}) 97add_library(common STATIC ${SRCS} ${HEADERS})
98target_link_libraries(common PUBLIC Boost::boost microprofile)
100if (ARCHITECTURE_x86_64) 99if (ARCHITECTURE_x86_64)
101 target_link_libraries(common xbyak) 100 target_link_libraries(common PRIVATE xbyak)
102endif() 101endif()
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 030f7caeb..0cc0a1be0 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -108,7 +108,7 @@
108 * symptoms. 108 * symptoms.
109 */ 109 */
110#pragma pack(1) 110#pragma pack(1)
111template <std::size_t position, std::size_t bits, typename T> 111template <std::size_t Position, std::size_t Bits, typename T>
112struct BitField { 112struct BitField {
113private: 113private:
114 // We hide the copy assigment operator here, because the default copy 114 // We hide the copy assigment operator here, because the default copy
@@ -117,7 +117,45 @@ private:
117 // We don't delete it because we want BitField to be trivially copyable. 117 // We don't delete it because we want BitField to be trivially copyable.
118 BitField& operator=(const BitField&) = default; 118 BitField& operator=(const BitField&) = default;
119 119
120 // StorageType is T for non-enum types and the underlying type of T if
121 // T is an enumeration. Note that T is wrapped within an enable_if in the
122 // former case to workaround compile errors which arise when using
123 // std::underlying_type<T>::type directly.
124 using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>,
125 std::enable_if<true, T>>::type;
126
127 // Unsigned version of StorageType
128 using StorageTypeU = std::make_unsigned_t<StorageType>;
129
120public: 130public:
131 /// Constants to allow limited introspection of fields if needed
132 static constexpr size_t position = Position;
133 static constexpr size_t bits = Bits;
134 static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
135
136 /**
137 * Formats a value by masking and shifting it according to the field parameters. A value
138 * containing several bitfields can be assembled by formatting each of their values and ORing
139 * the results together.
140 */
141 static constexpr FORCE_INLINE StorageType FormatValue(const T& value) {
142 return ((StorageType)value << position) & mask;
143 }
144
145 /**
146 * Extracts a value from the passed storage. In most situations prefer use the member functions
147 * (such as Value() or operator T), but this can be used to extract a value from a bitfield
148 * union in a constexpr context.
149 */
150 static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
151 if (std::numeric_limits<T>::is_signed) {
152 std::size_t shift = 8 * sizeof(T) - bits;
153 return (T)((storage << (shift - position)) >> shift);
154 } else {
155 return (T)((storage & mask) >> position);
156 }
157 }
158
121 // This constructor and assignment operator might be considered ambiguous: 159 // This constructor and assignment operator might be considered ambiguous:
122 // Would they initialize the storage or just the bitfield? 160 // Would they initialize the storage or just the bitfield?
123 // Hence, delete them. Use the Assign method to set bitfield values! 161 // Hence, delete them. Use the Assign method to set bitfield values!
@@ -126,23 +164,18 @@ public:
126 164
127 // Force default constructor to be created 165 // Force default constructor to be created
128 // so that we can use this within unions 166 // so that we can use this within unions
129 BitField() = default; 167 constexpr BitField() = default;
130 168
131 FORCE_INLINE operator T() const { 169 FORCE_INLINE operator T() const {
132 return Value(); 170 return Value();
133 } 171 }
134 172
135 FORCE_INLINE void Assign(const T& value) { 173 FORCE_INLINE void Assign(const T& value) {
136 storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask()); 174 storage = (storage & ~mask) | FormatValue(value);
137 } 175 }
138 176
139 FORCE_INLINE T Value() const { 177 FORCE_INLINE T Value() const {
140 if (std::numeric_limits<T>::is_signed) { 178 return ExtractValue(storage);
141 std::size_t shift = 8 * sizeof(T) - bits;
142 return (T)((storage << (shift - position)) >> shift);
143 } else {
144 return (T)((storage & GetMask()) >> position);
145 }
146 } 179 }
147 180
148 // TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015 181 // TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
@@ -151,20 +184,6 @@ public:
151 } 184 }
152 185
153private: 186private:
154 // StorageType is T for non-enum types and the underlying type of T if
155 // T is an enumeration. Note that T is wrapped within an enable_if in the
156 // former case to workaround compile errors which arise when using
157 // std::underlying_type<T>::type directly.
158 typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
159 std::enable_if<true, T>>::type::type StorageType;
160
161 // Unsigned version of StorageType
162 typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
163
164 FORCE_INLINE StorageType GetMask() const {
165 return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
166 }
167
168 StorageType storage; 187 StorageType storage;
169 188
170 static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); 189 static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
diff --git a/src/common/break_points.cpp b/src/common/break_points.cpp
index 03a19acba..fa367a4ca 100644
--- a/src/common/break_points.cpp
+++ b/src/common/break_points.cpp
@@ -5,7 +5,6 @@
5#include <algorithm> 5#include <algorithm>
6#include <sstream> 6#include <sstream>
7#include "common/break_points.h" 7#include "common/break_points.h"
8#include "common/logging/log.h"
9 8
10bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const { 9bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const {
11 auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; }; 10 auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; };
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index b141e79ed..2e7877500 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -7,7 +7,7 @@
7#if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM) 7#if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM)
8#include <cstdlib> // for exit 8#include <cstdlib> // for exit
9#endif 9#endif
10#include "common_types.h" 10#include "common/common_types.h"
11 11
12#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) 12#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
13 13
diff --git a/src/common/hash.cpp b/src/common/hash.cpp
index f3d390dc5..a02e9e5b9 100644
--- a/src/common/hash.cpp
+++ b/src/common/hash.cpp
@@ -5,9 +5,9 @@
5#if defined(_MSC_VER) 5#if defined(_MSC_VER)
6#include <stdlib.h> 6#include <stdlib.h>
7#endif 7#endif
8#include "common_funcs.h" 8#include "common/common_funcs.h"
9#include "common_types.h" 9#include "common/common_types.h"
10#include "hash.h" 10#include "common/hash.h"
11 11
12namespace Common { 12namespace Common {
13 13
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index 2cb3ab9cc..62f17fbb5 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -6,7 +6,7 @@
6#include <string> 6#include <string>
7#include <thread> 7#include <thread>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "cpu_detect.h" 9#include "common/x64/cpu_detect.h"
10 10
11#ifdef _MSC_VER 11#ifdef _MSC_VER
12#include <intrin.h> 12#include <intrin.h>
diff --git a/src/core/3ds.h b/src/core/3ds.h
new file mode 100644
index 000000000..8715e27db
--- /dev/null
+++ b/src/core/3ds.h
@@ -0,0 +1,21 @@
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
7namespace Core {
8
9// 3DS Video Constants
10// -------------------
11
12// NOTE: The LCDs actually rotate the image 90 degrees when displaying. Because of that the
13// framebuffers in video memory are stored in column-major order and rendered sideways, causing
14// the widths and heights of the framebuffers read by the LCD to be switched compared to the
15// heights and widths of the screens listed here.
16constexpr int kScreenTopWidth = 400; ///< 3DS top screen width
17constexpr int kScreenTopHeight = 240; ///< 3DS top screen height
18constexpr int kScreenBottomWidth = 320; ///< 3DS bottom screen width
19constexpr int kScreenBottomHeight = 240; ///< 3DS bottom screen height
20
21} // namespace Core
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 57504529f..b16a89990 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -32,6 +32,7 @@ set(SRCS
32 frontend/camera/factory.cpp 32 frontend/camera/factory.cpp
33 frontend/camera/interface.cpp 33 frontend/camera/interface.cpp
34 frontend/emu_window.cpp 34 frontend/emu_window.cpp
35 frontend/framebuffer_layout.cpp
35 frontend/motion_emu.cpp 36 frontend/motion_emu.cpp
36 gdbstub/gdbstub.cpp 37 gdbstub/gdbstub.cpp
37 hle/config_mem.cpp 38 hle/config_mem.cpp
@@ -44,6 +45,8 @@ set(SRCS
44 hle/kernel/client_port.cpp 45 hle/kernel/client_port.cpp
45 hle/kernel/client_session.cpp 46 hle/kernel/client_session.cpp
46 hle/kernel/event.cpp 47 hle/kernel/event.cpp
48 hle/kernel/handle_table.cpp
49 hle/kernel/hle_ipc.cpp
47 hle/kernel/kernel.cpp 50 hle/kernel/kernel.cpp
48 hle/kernel/memory.cpp 51 hle/kernel/memory.cpp
49 hle/kernel/mutex.cpp 52 hle/kernel/mutex.cpp
@@ -56,6 +59,7 @@ set(SRCS
56 hle/kernel/thread.cpp 59 hle/kernel/thread.cpp
57 hle/kernel/timer.cpp 60 hle/kernel/timer.cpp
58 hle/kernel/vm_manager.cpp 61 hle/kernel/vm_manager.cpp
62 hle/kernel/wait_object.cpp
59 hle/service/ac/ac.cpp 63 hle/service/ac/ac.cpp
60 hle/service/ac/ac_i.cpp 64 hle/service/ac/ac_i.cpp
61 hle/service/ac/ac_u.cpp 65 hle/service/ac/ac_u.cpp
@@ -152,8 +156,9 @@ set(SRCS
152 hle/service/qtm/qtm_sp.cpp 156 hle/service/qtm/qtm_sp.cpp
153 hle/service/qtm/qtm_u.cpp 157 hle/service/qtm/qtm_u.cpp
154 hle/service/service.cpp 158 hle/service/service.cpp
159 hle/service/sm/sm.cpp
160 hle/service/sm/srv.cpp
155 hle/service/soc_u.cpp 161 hle/service/soc_u.cpp
156 hle/service/srv.cpp
157 hle/service/ssl_c.cpp 162 hle/service/ssl_c.cpp
158 hle/service/y2r_u.cpp 163 hle/service/y2r_u.cpp
159 hle/shared_page.cpp 164 hle/shared_page.cpp
@@ -178,6 +183,7 @@ set(SRCS
178 ) 183 )
179 184
180set(HEADERS 185set(HEADERS
186 3ds.h
181 arm/arm_interface.h 187 arm/arm_interface.h
182 arm/dynarmic/arm_dynarmic.h 188 arm/dynarmic/arm_dynarmic.h
183 arm/dynarmic/arm_dynarmic_cp15.h 189 arm/dynarmic/arm_dynarmic_cp15.h
@@ -216,6 +222,7 @@ set(HEADERS
216 frontend/camera/factory.h 222 frontend/camera/factory.h
217 frontend/camera/interface.h 223 frontend/camera/interface.h
218 frontend/emu_window.h 224 frontend/emu_window.h
225 frontend/framebuffer_layout.h
219 frontend/input.h 226 frontend/input.h
220 frontend/motion_emu.h 227 frontend/motion_emu.h
221 gdbstub/gdbstub.h 228 gdbstub/gdbstub.h
@@ -231,7 +238,10 @@ set(HEADERS
231 hle/kernel/address_arbiter.h 238 hle/kernel/address_arbiter.h
232 hle/kernel/client_port.h 239 hle/kernel/client_port.h
233 hle/kernel/client_session.h 240 hle/kernel/client_session.h
241 hle/kernel/errors.h
234 hle/kernel/event.h 242 hle/kernel/event.h
243 hle/kernel/handle_table.h
244 hle/kernel/hle_ipc.h
235 hle/kernel/kernel.h 245 hle/kernel/kernel.h
236 hle/kernel/memory.h 246 hle/kernel/memory.h
237 hle/kernel/mutex.h 247 hle/kernel/mutex.h
@@ -245,6 +255,7 @@ set(HEADERS
245 hle/kernel/thread.h 255 hle/kernel/thread.h
246 hle/kernel/timer.h 256 hle/kernel/timer.h
247 hle/kernel/vm_manager.h 257 hle/kernel/vm_manager.h
258 hle/kernel/wait_object.h
248 hle/result.h 259 hle/result.h
249 hle/service/ac/ac.h 260 hle/service/ac/ac.h
250 hle/service/ac/ac_i.h 261 hle/service/ac/ac_i.h
@@ -342,8 +353,9 @@ set(HEADERS
342 hle/service/qtm/qtm_sp.h 353 hle/service/qtm/qtm_sp.h
343 hle/service/qtm/qtm_u.h 354 hle/service/qtm/qtm_u.h
344 hle/service/service.h 355 hle/service/service.h
356 hle/service/sm/sm.h
357 hle/service/sm/srv.h
345 hle/service/soc_u.h 358 hle/service/soc_u.h
346 hle/service/srv.h
347 hle/service/ssl_c.h 359 hle/service/ssl_c.h
348 hle/service/y2r_u.h 360 hle/service/y2r_u.h
349 hle/shared_page.h 361 hle/shared_page.h
@@ -370,11 +382,7 @@ set(HEADERS
370 telemetry_session.h 382 telemetry_session.h
371 ) 383 )
372 384
373include_directories(../../externals/dynarmic/include)
374include_directories(../../externals/cryptopp)
375
376create_directory_groups(${SRCS} ${HEADERS}) 385create_directory_groups(${SRCS} ${HEADERS})
377
378add_library(core STATIC ${SRCS} ${HEADERS}) 386add_library(core STATIC ${SRCS} ${HEADERS})
379 387target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
380target_link_libraries(core dynarmic cryptopp) 388target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt)
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 450e7566d..5429bcb26 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -3,7 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6 6#include <utility>
7#include "audio_core/audio_core.h" 7#include "audio_core/audio_core.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/arm/arm_interface.h" 9#include "core/arm/arm_interface.h"
@@ -26,6 +26,7 @@ namespace Core {
26/*static*/ System System::s_instance; 26/*static*/ System System::s_instance;
27 27
28System::ResultStatus System::RunLoop(int tight_loop) { 28System::ResultStatus System::RunLoop(int tight_loop) {
29 status = ResultStatus::Success;
29 if (!cpu_core) { 30 if (!cpu_core) {
30 return ResultStatus::ErrorNotInitialized; 31 return ResultStatus::ErrorNotInitialized;
31 } 32 }
@@ -59,7 +60,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
59 HW::Update(); 60 HW::Update();
60 Reschedule(); 61 Reschedule();
61 62
62 return ResultStatus::Success; 63 return status;
63} 64}
64 65
65System::ResultStatus System::SingleStep() { 66System::ResultStatus System::SingleStep() {
@@ -73,14 +74,25 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
73 LOG_CRITICAL(Core, "Failed to obtain loader for %s!", filepath.c_str()); 74 LOG_CRITICAL(Core, "Failed to obtain loader for %s!", filepath.c_str());
74 return ResultStatus::ErrorGetLoader; 75 return ResultStatus::ErrorGetLoader;
75 } 76 }
77 std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
78 app_loader->LoadKernelSystemMode();
79
80 if (system_mode.second != Loader::ResultStatus::Success) {
81 LOG_CRITICAL(Core, "Failed to determine system mode (Error %i)!",
82 static_cast<int>(system_mode.second));
83 System::Shutdown();
76 84
77 boost::optional<u32> system_mode{app_loader->LoadKernelSystemMode()}; 85 switch (system_mode.second) {
78 if (!system_mode) { 86 case Loader::ResultStatus::ErrorEncrypted:
79 LOG_CRITICAL(Core, "Failed to determine system mode!"); 87 return ResultStatus::ErrorLoader_ErrorEncrypted;
80 return ResultStatus::ErrorSystemMode; 88 case Loader::ResultStatus::ErrorInvalidFormat:
89 return ResultStatus::ErrorLoader_ErrorInvalidFormat;
90 default:
91 return ResultStatus::ErrorSystemMode;
92 }
81 } 93 }
82 94
83 ResultStatus init_result{Init(emu_window, system_mode.get())}; 95 ResultStatus init_result{Init(emu_window, system_mode.first.get())};
84 if (init_result != ResultStatus::Success) { 96 if (init_result != ResultStatus::Success) {
85 LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result); 97 LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result);
86 System::Shutdown(); 98 System::Shutdown();
@@ -101,7 +113,8 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
101 return ResultStatus::ErrorLoader; 113 return ResultStatus::ErrorLoader;
102 } 114 }
103 } 115 }
104 return ResultStatus::Success; 116 status = ResultStatus::Success;
117 return status;
105} 118}
106 119
107void System::PrepareReschedule() { 120void System::PrepareReschedule() {
diff --git a/src/core/core.h b/src/core/core.h
index 6af772831..4e3b6b409 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -40,7 +40,10 @@ public:
40 ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption 40 ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption
41 ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an 41 ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
42 /// invalid format 42 /// invalid format
43 ErrorSystemFiles, ///< Error in finding system files
44 ErrorSharedFont, ///< Error in finding shared font
43 ErrorVideoCore, ///< Error in the video core 45 ErrorVideoCore, ///< Error in the video core
46 ErrorUnknown ///< Any other error
44 }; 47 };
45 48
46 /** 49 /**
@@ -105,6 +108,17 @@ public:
105 PerfStats perf_stats; 108 PerfStats perf_stats;
106 FrameLimiter frame_limiter; 109 FrameLimiter frame_limiter;
107 110
111 void SetStatus(ResultStatus new_status, const char* details = nullptr) {
112 status = new_status;
113 if (details) {
114 status_details = details;
115 }
116 }
117
118 const std::string& GetStatusDetails() const {
119 return status_details;
120 }
121
108private: 122private:
109 /** 123 /**
110 * Initialize the emulated system. 124 * Initialize the emulated system.
@@ -130,6 +144,9 @@ private:
130 std::unique_ptr<Core::TelemetrySession> telemetry_session; 144 std::unique_ptr<Core::TelemetrySession> telemetry_session;
131 145
132 static System s_instance; 146 static System s_instance;
147
148 ResultStatus status = ResultStatus::Success;
149 std::string status_details = "";
133}; 150};
134 151
135inline ARM_Interface& CPU() { 152inline ARM_Interface& CPU() {
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp
index f454e7840..4867c9d17 100644
--- a/src/core/file_sys/archive_extsavedata.cpp
+++ b/src/core/file_sys/archive_extsavedata.cpp
@@ -38,8 +38,7 @@ public:
38 ResultVal<size_t> Write(u64 offset, size_t length, bool flush, 38 ResultVal<size_t> Write(u64 offset, size_t length, bool flush,
39 const u8* buffer) const override { 39 const u8* buffer) const override {
40 if (offset > size) { 40 if (offset > size) {
41 return ResultCode(ErrorDescription::FS_WriteBeyondEnd, ErrorModule::FS, 41 return ERR_WRITE_BEYOND_END;
42 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
43 } else if (offset == size) { 42 } else if (offset == size) {
44 return MakeResult<size_t>(0); 43 return MakeResult<size_t>(0);
45 } 44 }
@@ -191,11 +190,9 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons
191 // TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData. 190 // TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData.
192 // ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist. 191 // ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist.
193 if (!shared) { 192 if (!shared) {
194 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, 193 return ERR_NOT_FOUND_INVALID_STATE;
195 ErrorSummary::InvalidState, ErrorLevel::Status);
196 } else { 194 } else {
197 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, 195 return ERR_NOT_FORMATTED;
198 ErrorSummary::InvalidState, ErrorLevel::Status);
199 } 196 }
200 } 197 }
201 auto archive = std::make_unique<ExtSaveDataArchive>(fullpath); 198 auto archive = std::make_unique<ExtSaveDataArchive>(fullpath);
@@ -230,8 +227,7 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Pat
230 if (!file.IsOpen()) { 227 if (!file.IsOpen()) {
231 LOG_ERROR(Service_FS, "Could not open metadata information for archive"); 228 LOG_ERROR(Service_FS, "Could not open metadata information for archive");
232 // TODO(Subv): Verify error code 229 // TODO(Subv): Verify error code
233 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, 230 return ERR_NOT_FORMATTED;
234 ErrorSummary::InvalidState, ErrorLevel::Status);
235 } 231 }
236 232
237 ArchiveFormatInfo info = {}; 233 ArchiveFormatInfo info = {};
diff --git a/src/core/file_sys/archive_ncch.cpp b/src/core/file_sys/archive_ncch.cpp
index 89455e39c..6d9007731 100644
--- a/src/core/file_sys/archive_ncch.cpp
+++ b/src/core/file_sys/archive_ncch.cpp
@@ -9,7 +9,9 @@
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "core/core.h"
12#include "core/file_sys/archive_ncch.h" 13#include "core/file_sys/archive_ncch.h"
14#include "core/file_sys/errors.h"
13#include "core/file_sys/ivfc_archive.h" 15#include "core/file_sys/ivfc_archive.h"
14#include "core/hle/service/fs/archive.h" 16#include "core/hle/service/fs/archive.h"
15 17
@@ -33,11 +35,44 @@ ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory)
33ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path) { 35ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path) {
34 auto vec = path.AsBinary(); 36 auto vec = path.AsBinary();
35 const u32* data = reinterpret_cast<u32*>(vec.data()); 37 const u32* data = reinterpret_cast<u32*>(vec.data());
36 std::string file_path = GetNCCHPath(mount_point, data[1], data[0]); 38 u32 high = data[1];
39 u32 low = data[0];
40 std::string file_path = GetNCCHPath(mount_point, high, low);
37 auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb"); 41 auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb");
38 42
39 if (!file->IsOpen()) { 43 if (!file->IsOpen()) {
40 return ResultCode(-1); // TODO(Subv): Find the right error code 44 // High Title ID of the archive: The category (https://3dbrew.org/wiki/Title_list).
45 constexpr u32 shared_data_archive = 0x0004009B;
46 constexpr u32 system_data_archive = 0x000400DB;
47
48 // Low Title IDs.
49 constexpr u32 mii_data = 0x00010202;
50 constexpr u32 region_manifest = 0x00010402;
51 constexpr u32 ng_word_list = 0x00010302;
52
53 LOG_DEBUG(Service_FS, "Full Path: %s. Category: 0x%X. Path: 0x%X.", path.DebugStr().c_str(),
54 high, low);
55
56 if (high == shared_data_archive) {
57 if (low == mii_data) {
58 LOG_ERROR(Service_FS, "Failed to get a handle for shared data archive: Mii data. ");
59 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles,
60 "Mii data");
61 } else if (low == region_manifest) {
62 LOG_ERROR(Service_FS,
63 "Failed to get a handle for shared data archive: region manifest.");
64 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles,
65 "Region manifest");
66 }
67 } else if (high == system_data_archive) {
68 if (low == ng_word_list) {
69 LOG_ERROR(Service_FS,
70 "Failed to get a handle for system data archive: NG bad word list.");
71 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles,
72 "NG bad word list");
73 }
74 }
75 return ERROR_NOT_FOUND;
41 } 76 }
42 auto size = file->GetSize(); 77 auto size = file->GetSize();
43 78
diff --git a/src/core/file_sys/archive_source_sd_savedata.cpp b/src/core/file_sys/archive_source_sd_savedata.cpp
index f31a68038..a7e331724 100644
--- a/src/core/file_sys/archive_source_sd_savedata.cpp
+++ b/src/core/file_sys/archive_source_sd_savedata.cpp
@@ -6,6 +6,7 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/string_util.h" 7#include "common/string_util.h"
8#include "core/file_sys/archive_source_sd_savedata.h" 8#include "core/file_sys/archive_source_sd_savedata.h"
9#include "core/file_sys/errors.h"
9#include "core/file_sys/savedata_archive.h" 10#include "core/file_sys/savedata_archive.h"
10#include "core/hle/service/fs/archive.h" 11#include "core/hle/service/fs/archive.h"
11 12
@@ -49,8 +50,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 pr
49 // save file/directory structure expected by the game has not yet been initialized. 50 // save file/directory structure expected by the game has not yet been initialized.
50 // Returning the NotFormatted error code will signal the game to provision the SaveData 51 // Returning the NotFormatted error code will signal the game to provision the SaveData
51 // archive with the files and folders that it expects. 52 // archive with the files and folders that it expects.
52 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, 53 return ERR_NOT_FORMATTED;
53 ErrorSummary::InvalidState, ErrorLevel::Status);
54 } 54 }
55 55
56 auto archive = std::make_unique<SaveDataArchive>(std::move(concrete_mount_point)); 56 auto archive = std::make_unique<SaveDataArchive>(std::move(concrete_mount_point));
@@ -81,8 +81,7 @@ ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(u64 program
81 if (!file.IsOpen()) { 81 if (!file.IsOpen()) {
82 LOG_ERROR(Service_FS, "Could not open metadata information for archive"); 82 LOG_ERROR(Service_FS, "Could not open metadata information for archive");
83 // TODO(Subv): Verify error code 83 // TODO(Subv): Verify error code
84 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, 84 return ERR_NOT_FORMATTED;
85 ErrorSummary::InvalidState, ErrorLevel::Status);
86 } 85 }
87 86
88 ArchiveFormatInfo info = {}; 87 ArchiveFormatInfo info = {};
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
index 8986b5c0e..81423bffd 100644
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ b/src/core/file_sys/archive_systemsavedata.cpp
@@ -9,6 +9,7 @@
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "common/string_util.h" 10#include "common/string_util.h"
11#include "core/file_sys/archive_systemsavedata.h" 11#include "core/file_sys/archive_systemsavedata.h"
12#include "core/file_sys/errors.h"
12#include "core/file_sys/savedata_archive.h" 13#include "core/file_sys/savedata_archive.h"
13#include "core/hle/service/fs/archive.h" 14#include "core/hle/service/fs/archive.h"
14 15
@@ -53,8 +54,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c
53 std::string fullpath = GetSystemSaveDataPath(base_path, path); 54 std::string fullpath = GetSystemSaveDataPath(base_path, path);
54 if (!FileUtil::Exists(fullpath)) { 55 if (!FileUtil::Exists(fullpath)) {
55 // TODO(Subv): Check error code, this one is probably wrong 56 // TODO(Subv): Check error code, this one is probably wrong
56 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, 57 return ERR_NOT_FORMATTED;
57 ErrorSummary::InvalidState, ErrorLevel::Status);
58 } 58 }
59 auto archive = std::make_unique<SaveDataArchive>(fullpath); 59 auto archive = std::make_unique<SaveDataArchive>(fullpath);
60 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 60 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
index a243d9a13..98d80aabc 100644
--- a/src/core/file_sys/disk_archive.cpp
+++ b/src/core/file_sys/disk_archive.cpp
@@ -9,6 +9,7 @@
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/file_sys/disk_archive.h" 11#include "core/file_sys/disk_archive.h"
12#include "core/file_sys/errors.h"
12 13
13//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace 15// FileSys namespace
@@ -17,8 +18,7 @@ namespace FileSys {
17 18
18ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { 19ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
19 if (!mode.read_flag) 20 if (!mode.read_flag)
20 return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, 21 return ERROR_INVALID_OPEN_FLAGS;
21 ErrorSummary::Canceled, ErrorLevel::Status);
22 22
23 file->Seek(offset, SEEK_SET); 23 file->Seek(offset, SEEK_SET);
24 return MakeResult<size_t>(file->ReadBytes(buffer, length)); 24 return MakeResult<size_t>(file->ReadBytes(buffer, length));
@@ -27,8 +27,7 @@ ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buff
27ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush, 27ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush,
28 const u8* buffer) const { 28 const u8* buffer) const {
29 if (!mode.write_flag) 29 if (!mode.write_flag)
30 return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, 30 return ERROR_INVALID_OPEN_FLAGS;
31 ErrorSummary::Canceled, ErrorLevel::Status);
32 31
33 file->Seek(offset, SEEK_SET); 32 file->Seek(offset, SEEK_SET);
34 size_t written = file->WriteBytes(buffer, length); 33 size_t written = file->WriteBytes(buffer, length);
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index 9fc8d753b..a974bc775 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -2,52 +2,93 @@
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#pragma once
6
5#include "core/hle/result.h" 7#include "core/hle/result.h"
6 8
7namespace FileSys { 9namespace FileSys {
8 10
9const ResultCode ERROR_INVALID_PATH(ErrorDescription::FS_InvalidPath, ErrorModule::FS, 11namespace ErrCodes {
10 ErrorSummary::InvalidArgument, ErrorLevel::Usage); 12enum {
11const ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ErrorDescription::FS_UnsupportedOpenFlags, 13 RomFSNotFound = 100,
12 ErrorModule::FS, ErrorSummary::NotSupported, 14 ArchiveNotMounted = 101,
13 ErrorLevel::Usage); 15 FileNotFound = 112,
14const ResultCode ERROR_INVALID_OPEN_FLAGS(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, 16 PathNotFound = 113,
15 ErrorSummary::Canceled, ErrorLevel::Status); 17 GameCardNotInserted = 141,
16const ResultCode ERROR_INVALID_READ_FLAG(ErrorDescription::FS_InvalidReadFlag, ErrorModule::FS, 18 NotFound = 120,
17 ErrorSummary::InvalidArgument, ErrorLevel::Usage); 19 FileAlreadyExists = 180,
18const ResultCode ERROR_FILE_NOT_FOUND(ErrorDescription::FS_FileNotFound, ErrorModule::FS, 20 DirectoryAlreadyExists = 185,
19 ErrorSummary::NotFound, ErrorLevel::Status); 21 AlreadyExists = 190,
20const ResultCode ERROR_PATH_NOT_FOUND(ErrorDescription::FS_PathNotFound, ErrorModule::FS, 22 InvalidOpenFlags = 230,
21 ErrorSummary::NotFound, ErrorLevel::Status); 23 DirectoryNotEmpty = 240,
22const ResultCode ERROR_NOT_FOUND(ErrorDescription::FS_NotFound, ErrorModule::FS, 24 NotAFile = 250,
23 ErrorSummary::NotFound, ErrorLevel::Status); 25 NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
24const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ErrorDescription::FS_UnexpectedFileOrDirectory, 26 ExeFSSectionNotFound = 567,
25 ErrorModule::FS, ErrorSummary::NotSupported, 27 CommandNotAllowed = 630,
26 ErrorLevel::Usage); 28 InvalidReadFlag = 700,
27const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC(ErrorDescription::FS_NotAFile, 29 InvalidPath = 702,
28 ErrorModule::FS, ErrorSummary::Canceled, 30 WriteBeyondEnd = 705,
29 ErrorLevel::Status); 31 UnsupportedOpenFlags = 760,
30const ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ErrorDescription::FS_DirectoryAlreadyExists, 32 IncorrectExeFSReadSize = 761,
31 ErrorModule::FS, ErrorSummary::NothingHappened, 33 UnexpectedFileOrDirectory = 770,
32 ErrorLevel::Status); 34};
33const ResultCode ERROR_FILE_ALREADY_EXISTS(ErrorDescription::FS_FileAlreadyExists, ErrorModule::FS, 35}
34 ErrorSummary::NothingHappened, ErrorLevel::Status); 36
35const ResultCode ERROR_ALREADY_EXISTS(ErrorDescription::FS_AlreadyExists, ErrorModule::FS, 37constexpr ResultCode ERROR_INVALID_PATH(ErrCodes::InvalidPath, ErrorModule::FS,
36 ErrorSummary::NothingHappened, ErrorLevel::Status); 38 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
37const ResultCode ERROR_DIRECTORY_NOT_EMPTY(ErrorDescription::FS_DirectoryNotEmpty, ErrorModule::FS, 39constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ErrCodes::UnsupportedOpenFlags, ErrorModule::FS,
38 ErrorSummary::Canceled, ErrorLevel::Status); 40 ErrorSummary::NotSupported, ErrorLevel::Usage);
39const ResultCode ERROR_GAMECARD_NOT_INSERTED(ErrorDescription::FS_GameCardNotInserted, 41constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ErrCodes::InvalidOpenFlags, ErrorModule::FS,
40 ErrorModule::FS, ErrorSummary::NotFound, 42 ErrorSummary::Canceled, ErrorLevel::Status);
41 ErrorLevel::Status); 43constexpr ResultCode ERROR_INVALID_READ_FLAG(ErrCodes::InvalidReadFlag, ErrorModule::FS,
42const ResultCode ERROR_INCORRECT_EXEFS_READ_SIZE(ErrorDescription::FS_IncorrectExeFSReadSize, 44 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
43 ErrorModule::FS, ErrorSummary::NotSupported, 45constexpr ResultCode ERROR_FILE_NOT_FOUND(ErrCodes::FileNotFound, ErrorModule::FS,
44 ErrorLevel::Usage); 46 ErrorSummary::NotFound, ErrorLevel::Status);
45const ResultCode ERROR_ROMFS_NOT_FOUND(ErrorDescription::FS_RomFSNotFound, ErrorModule::FS, 47constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrCodes::PathNotFound, ErrorModule::FS,
46 ErrorSummary::NotFound, ErrorLevel::Status); 48 ErrorSummary::NotFound, ErrorLevel::Status);
47const ResultCode ERROR_COMMAND_NOT_ALLOWED(ErrorDescription::FS_CommandNotAllowed, ErrorModule::FS, 49constexpr ResultCode ERROR_NOT_FOUND(ErrCodes::NotFound, ErrorModule::FS, ErrorSummary::NotFound,
48 ErrorSummary::WrongArgument, ErrorLevel::Permanent); 50 ErrorLevel::Status);
49const ResultCode ERROR_EXEFS_SECTION_NOT_FOUND(ErrorDescription::FS_ExeFSSectionNotFound, 51constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ErrCodes::UnexpectedFileOrDirectory,
50 ErrorModule::FS, ErrorSummary::NotFound, 52 ErrorModule::FS, ErrorSummary::NotSupported,
51 ErrorLevel::Status); 53 ErrorLevel::Usage);
54constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC(ErrCodes::NotAFile, ErrorModule::FS,
55 ErrorSummary::Canceled,
56 ErrorLevel::Status);
57constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ErrCodes::DirectoryAlreadyExists,
58 ErrorModule::FS, ErrorSummary::NothingHappened,
59 ErrorLevel::Status);
60constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ErrCodes::FileAlreadyExists, ErrorModule::FS,
61 ErrorSummary::NothingHappened, ErrorLevel::Status);
62constexpr ResultCode ERROR_ALREADY_EXISTS(ErrCodes::AlreadyExists, ErrorModule::FS,
63 ErrorSummary::NothingHappened, ErrorLevel::Status);
64constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(ErrCodes::DirectoryNotEmpty, ErrorModule::FS,
65 ErrorSummary::Canceled, ErrorLevel::Status);
66constexpr ResultCode ERROR_GAMECARD_NOT_INSERTED(ErrCodes::GameCardNotInserted, ErrorModule::FS,
67 ErrorSummary::NotFound, ErrorLevel::Status);
68constexpr ResultCode ERROR_INCORRECT_EXEFS_READ_SIZE(ErrCodes::IncorrectExeFSReadSize,
69 ErrorModule::FS, ErrorSummary::NotSupported,
70 ErrorLevel::Usage);
71constexpr ResultCode ERROR_ROMFS_NOT_FOUND(ErrCodes::RomFSNotFound, ErrorModule::FS,
72 ErrorSummary::NotFound, ErrorLevel::Status);
73constexpr ResultCode ERROR_COMMAND_NOT_ALLOWED(ErrCodes::CommandNotAllowed, ErrorModule::FS,
74 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
75constexpr ResultCode ERROR_EXEFS_SECTION_NOT_FOUND(ErrCodes::ExeFSSectionNotFound, ErrorModule::FS,
76 ErrorSummary::NotFound, ErrorLevel::Status);
77
78/// Returned when a function is passed an invalid archive handle.
79constexpr ResultCode ERR_INVALID_ARCHIVE_HANDLE(ErrCodes::ArchiveNotMounted, ErrorModule::FS,
80 ErrorSummary::NotFound,
81 ErrorLevel::Status); // 0xC8804465
82constexpr ResultCode ERR_WRITE_BEYOND_END(ErrCodes::WriteBeyondEnd, ErrorModule::FS,
83 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
84
85/**
86 * Variant of ERROR_NOT_FOUND returned in some places in the code. Unknown if these usages are
87 * correct or a bug.
88 */
89constexpr ResultCode ERR_NOT_FOUND_INVALID_STATE(ErrCodes::NotFound, ErrorModule::FS,
90 ErrorSummary::InvalidState, ErrorLevel::Status);
91constexpr ResultCode ERR_NOT_FORMATTED(ErrCodes::NotFormatted, ErrorModule::FS,
92 ErrorSummary::InvalidState, ErrorLevel::Status);
52 93
53} // namespace FileSys 94} // namespace FileSys
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 5fdb3a7e8..4f7d54a33 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -5,10 +5,10 @@
5#include <algorithm> 5#include <algorithm>
6#include <cmath> 6#include <cmath>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "core/3ds.h"
8#include "core/core.h" 9#include "core/core.h"
9#include "core/frontend/emu_window.h" 10#include "core/frontend/emu_window.h"
10#include "core/settings.h" 11#include "core/settings.h"
11#include "video_core/video_core.h"
12 12
13/** 13/**
14 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout 14 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
@@ -38,11 +38,9 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
38 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) 38 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
39 return; 39 return;
40 40
41 touch_x = VideoCore::kScreenBottomWidth * 41 touch_x = Core::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) /
42 (framebuffer_x - framebuffer_layout.bottom_screen.left) /
43 (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); 42 (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
44 touch_y = VideoCore::kScreenBottomHeight * 43 touch_y = Core::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) /
45 (framebuffer_y - framebuffer_layout.bottom_screen.top) /
46 (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); 44 (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
47 45
48 touch_pressed = true; 46 touch_pressed = true;
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 36f2667fa..9414123a4 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -8,8 +8,8 @@
8#include <tuple> 8#include <tuple>
9#include <utility> 9#include <utility>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/framebuffer_layout.h"
12#include "common/math_util.h" 11#include "common/math_util.h"
12#include "core/frontend/framebuffer_layout.h"
13 13
14/** 14/**
15 * Abstraction class used to provide an interface between emulation code and the frontend 15 * Abstraction class used to provide an interface between emulation code and the frontend
diff --git a/src/common/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index a2a0e7dad..d2d02f9ff 100644
--- a/src/common/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -5,16 +5,20 @@
5#include <cmath> 5#include <cmath>
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/framebuffer_layout.h" 8#include "core/3ds.h"
9#include "core/frontend/framebuffer_layout.h"
9#include "core/settings.h" 10#include "core/settings.h"
10#include "video_core/video_core.h"
11 11
12namespace Layout { 12namespace Layout {
13 13
14static const float TOP_SCREEN_ASPECT_RATIO = 14static const float TOP_SCREEN_ASPECT_RATIO =
15 static_cast<float>(VideoCore::kScreenTopHeight) / VideoCore::kScreenTopWidth; 15 static_cast<float>(Core::kScreenTopHeight) / Core::kScreenTopWidth;
16static const float BOT_SCREEN_ASPECT_RATIO = 16static const float BOT_SCREEN_ASPECT_RATIO =
17 static_cast<float>(VideoCore::kScreenBottomHeight) / VideoCore::kScreenBottomWidth; 17 static_cast<float>(Core::kScreenBottomHeight) / Core::kScreenBottomWidth;
18
19float FramebufferLayout::GetScalingRatio() const {
20 return static_cast<float>(top_screen.GetWidth()) / Core::kScreenTopWidth;
21}
18 22
19// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio 23// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio
20template <class T> 24template <class T>
@@ -106,10 +110,10 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped
106 float window_aspect_ratio = static_cast<float>(height) / width; 110 float window_aspect_ratio = static_cast<float>(height) / width;
107 float emulation_aspect_ratio = 111 float emulation_aspect_ratio =
108 swapped 112 swapped
109 ? VideoCore::kScreenBottomHeight * 4 / 113 ? Core::kScreenBottomHeight * 4 /
110 (VideoCore::kScreenBottomWidth * 4.0f + VideoCore::kScreenTopWidth) 114 (Core::kScreenBottomWidth * 4.0f + Core::kScreenTopWidth)
111 : VideoCore::kScreenTopHeight * 4 / 115 : Core::kScreenTopHeight * 4 /
112 (VideoCore::kScreenTopWidth * 4.0f + VideoCore::kScreenBottomWidth); 116 (Core::kScreenTopWidth * 4.0f + Core::kScreenBottomWidth);
113 float large_screen_aspect_ratio = swapped ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO; 117 float large_screen_aspect_ratio = swapped ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
114 float small_screen_aspect_ratio = swapped ? TOP_SCREEN_ASPECT_RATIO : BOT_SCREEN_ASPECT_RATIO; 118 float small_screen_aspect_ratio = swapped ? TOP_SCREEN_ASPECT_RATIO : BOT_SCREEN_ASPECT_RATIO;
115 119
diff --git a/src/common/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index f1df5c55a..9a7738969 100644
--- a/src/common/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -5,7 +5,9 @@
5#pragma once 5#pragma once
6 6
7#include "common/math_util.h" 7#include "common/math_util.h"
8
8namespace Layout { 9namespace Layout {
10
9/// Describes the layout of the window framebuffer (size and top/bottom screen positions) 11/// Describes the layout of the window framebuffer (size and top/bottom screen positions)
10struct FramebufferLayout { 12struct FramebufferLayout {
11 unsigned width; 13 unsigned width;
@@ -14,6 +16,12 @@ struct FramebufferLayout {
14 bool bottom_screen_enabled; 16 bool bottom_screen_enabled;
15 MathUtil::Rectangle<unsigned> top_screen; 17 MathUtil::Rectangle<unsigned> top_screen;
16 MathUtil::Rectangle<unsigned> bottom_screen; 18 MathUtil::Rectangle<unsigned> bottom_screen;
19
20 /**
21 * Returns the ration of pixel size of the top screen, compared to the native size of the 3DS
22 * screen.
23 */
24 float GetScalingRatio() const;
17}; 25};
18 26
19/** 27/**
@@ -52,4 +60,5 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swap
52 * @return Newly created FramebufferLayout object with default screen regions initialized 60 * @return Newly created FramebufferLayout object with default screen regions initialized
53 */ 61 */
54FramebufferLayout CustomFrameLayout(unsigned width, unsigned height); 62FramebufferLayout CustomFrameLayout(unsigned width, unsigned height);
55} 63
64} // namespace Layout
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 07c7f5b99..89f08daa2 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -11,7 +11,6 @@
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/shared_memory.h" 12#include "core/hle/kernel/shared_memory.h"
13#include "core/hle/result.h" 13#include "core/hle/result.h"
14#include "video_core/video_core.h"
15 14
16//////////////////////////////////////////////////////////////////////////////////////////////////// 15////////////////////////////////////////////////////////////////////////////////////////////////////
17 16
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index 059297fbc..fdf8807b0 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -14,7 +14,6 @@
14#include "core/hle/service/gsp_gpu.h" 14#include "core/hle/service/gsp_gpu.h"
15#include "core/hle/service/hid/hid.h" 15#include "core/hle/service/hid/hid.h"
16#include "core/memory.h" 16#include "core/memory.h"
17#include "video_core/video_core.h"
18 17
19//////////////////////////////////////////////////////////////////////////////////////////////////// 18////////////////////////////////////////////////////////////////////////////////////////////////////
20 19
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index f6eb900f0..18b6e7017 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -53,7 +53,7 @@ void Wrap() {
53 FuncReturn(retval); 53 FuncReturn(retval);
54} 54}
55 55
56template <ResultCode func(u32*, s32, u32, u32, u32, s32)> 56template <ResultCode func(u32*, u32, u32, u32, u32, s32)>
57void Wrap() { 57void Wrap() {
58 u32 param_1 = 0; 58 u32 param_1 = 0;
59 u32 retval = func(&param_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw; 59 u32 retval = func(&param_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw;
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 3a5d481a5..303ca090d 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/errors.h"
8#include "core/hle/kernel/thread.h" 9#include "core/hle/kernel/thread.h"
9#include "core/memory.h" 10#include "core/memory.h"
10 11
@@ -43,6 +44,12 @@ inline u32* GetStaticBuffers(const int offset = 0) {
43 44
44namespace IPC { 45namespace IPC {
45 46
47// These errors are commonly returned by invalid IPC translations, so alias them here for
48// convenience.
49// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
50using Kernel::ERR_INVALID_BUFFER_DESCRIPTOR;
51constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS;
52
46enum DescriptorType : u32 { 53enum DescriptorType : u32 {
47 // Buffer related desciptors types (mask : 0x0F) 54 // Buffer related desciptors types (mask : 0x0F)
48 StaticBuffer = 0x02, 55 StaticBuffer = 0x02,
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 06c4c5a85..d7348c09d 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -3,7 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6
6#include "core/hle/ipc.h" 7#include "core/hle/ipc.h"
8#include "core/hle/kernel/handle_table.h"
7#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
8 10
9namespace IPC { 11namespace IPC {
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 01fab123e..776d342f0 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -5,6 +5,7 @@
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/kernel/address_arbiter.h" 7#include "core/hle/kernel/address_arbiter.h"
8#include "core/hle/kernel/errors.h"
8#include "core/hle/kernel/thread.h" 9#include "core/hle/kernel/thread.h"
9#include "core/memory.h" 10#include "core/memory.h"
10 11
@@ -74,8 +75,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
74 75
75 default: 76 default:
76 LOG_ERROR(Kernel, "unknown type=%d", type); 77 LOG_ERROR(Kernel, "unknown type=%d", type);
77 return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, 78 return ERR_INVALID_ENUM_VALUE_FND;
78 ErrorSummary::WrongArgument, ErrorLevel::Usage);
79 } 79 }
80 80
81 // The calls that use a timeout seem to always return a Timeout error even if they did not put 81 // The calls that use a timeout seem to always return a Timeout error even if they did not put
@@ -83,8 +83,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
83 if (type == ArbitrationType::WaitIfLessThanWithTimeout || 83 if (type == ArbitrationType::WaitIfLessThanWithTimeout ||
84 type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) { 84 type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) {
85 85
86 return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, ErrorSummary::StatusChanged, 86 return RESULT_TIMEOUT;
87 ErrorLevel::Info);
88 } 87 }
89 return RESULT_SUCCESS; 88 return RESULT_SUCCESS;
90} 89}
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index 6a7af93a9..1d24401b1 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -6,6 +6,7 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/kernel.h"
9#include "core/hle/result.h"
9 10
10// Address arbiters are an underlying kernel synchronization object that can be created/used via 11// Address arbiters are an underlying kernel synchronization object that can be created/used via
11// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR 12// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index ddcf4c916..ce5d94e99 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -5,6 +5,8 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "core/hle/kernel/client_port.h" 6#include "core/hle/kernel/client_port.h"
7#include "core/hle/kernel/client_session.h" 7#include "core/hle/kernel/client_session.h"
8#include "core/hle/kernel/errors.h"
9#include "core/hle/kernel/hle_ipc.h"
8#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/server_port.h" 11#include "core/hle/kernel/server_port.h"
10#include "core/hle/kernel/server_session.h" 12#include "core/hle/kernel/server_session.h"
@@ -19,26 +21,22 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
19 // AcceptSession before returning from this call. 21 // AcceptSession before returning from this call.
20 22
21 if (active_sessions >= max_sessions) { 23 if (active_sessions >= max_sessions) {
22 return ResultCode(ErrorDescription::MaxConnectionsReached, ErrorModule::OS, 24 return ERR_MAX_CONNECTIONS_REACHED;
23 ErrorSummary::WouldBlock, ErrorLevel::Temporary);
24 } 25 }
25 active_sessions++; 26 active_sessions++;
26 27
27 // Create a new session pair, let the created sessions inherit the parent port's HLE handler. 28 // Create a new session pair, let the created sessions inherit the parent port's HLE handler.
28 auto sessions = 29 auto sessions = ServerSession::CreateSessionPair(server_port->GetName(), this);
29 ServerSession::CreateSessionPair(server_port->GetName(), server_port->hle_handler, this);
30 auto client_session = std::get<SharedPtr<ClientSession>>(sessions);
31 auto server_session = std::get<SharedPtr<ServerSession>>(sessions);
32 30
33 if (server_port->hle_handler) 31 if (server_port->hle_handler)
34 server_port->hle_handler->ClientConnected(server_session); 32 server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
35 else 33 else
36 server_port->pending_sessions.push_back(std::move(server_session)); 34 server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions));
37 35
38 // Wake the threads waiting on the ServerPort 36 // Wake the threads waiting on the ServerPort
39 server_port->WakeupAllWaitingThreads(); 37 server_port->WakeupAllWaitingThreads();
40 38
41 return MakeResult<SharedPtr<ClientSession>>(std::move(client_session)); 39 return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
42} 40}
43 41
44} // namespace 42} // namespace
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
index 511490c7c..8f7d6ac44 100644
--- a/src/core/hle/kernel/client_port.h
+++ b/src/core/hle/kernel/client_port.h
@@ -7,6 +7,7 @@
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
10#include "core/hle/result.h"
10 11
11namespace Kernel { 12namespace Kernel {
12 13
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index e297b7464..fef97af1f 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -5,7 +5,10 @@
5#include "common/assert.h" 5#include "common/assert.h"
6 6
7#include "core/hle/kernel/client_session.h" 7#include "core/hle/kernel/client_session.h"
8#include "core/hle/kernel/errors.h"
9#include "core/hle/kernel/hle_ipc.h"
8#include "core/hle/kernel/server_session.h" 10#include "core/hle/kernel/server_session.h"
11#include "core/hle/kernel/session.h"
9 12
10namespace Kernel { 13namespace Kernel {
11 14
@@ -14,9 +17,13 @@ ClientSession::~ClientSession() {
14 // This destructor will be called automatically when the last ClientSession handle is closed by 17 // This destructor will be called automatically when the last ClientSession handle is closed by
15 // the emulated application. 18 // the emulated application.
16 19
17 if (parent->server) { 20 // Local references to ServerSession and SessionRequestHandler are necessary to guarantee they
18 if (parent->server->hle_handler) 21 // will be kept alive until after ClientDisconnected() returns.
19 parent->server->hle_handler->ClientDisconnected(parent->server); 22 SharedPtr<ServerSession> server = parent->server;
23 if (server) {
24 std::shared_ptr<SessionRequestHandler> hle_handler = server->hle_handler;
25 if (hle_handler)
26 hle_handler->ClientDisconnected(server);
20 27
21 // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set 28 // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
22 // their WaitSynchronization result to 0xC920181A. 29 // their WaitSynchronization result to 0xC920181A.
@@ -26,12 +33,13 @@ ClientSession::~ClientSession() {
26} 33}
27 34
28ResultCode ClientSession::SendSyncRequest() { 35ResultCode ClientSession::SendSyncRequest() {
29 // Signal the server session that new data is available 36 // Keep ServerSession alive until we're done working with it.
30 if (parent->server) 37 SharedPtr<ServerSession> server = parent->server;
31 return parent->server->HandleSyncRequest(); 38 if (server == nullptr)
39 return ERR_SESSION_CLOSED_BY_REMOTE;
32 40
33 return ResultCode(ErrorDescription::SessionClosedByRemote, ErrorModule::OS, 41 // Signal the server session that new data is available
34 ErrorSummary::Canceled, ErrorLevel::Status); 42 return server->HandleSyncRequest();
35} 43}
36 44
37} // namespace 45} // namespace
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index 9f3adb72b..2de379c09 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -6,10 +6,9 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9
10#include "common/common_types.h" 9#include "common/common_types.h"
11
12#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
11#include "core/hle/result.h"
13 12
14namespace Kernel { 13namespace Kernel {
15 14
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
new file mode 100644
index 000000000..b3b60e7df
--- /dev/null
+++ b/src/core/hle/kernel/errors.h
@@ -0,0 +1,98 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8
9namespace Kernel {
10
11namespace ErrCodes {
12enum {
13 OutOfHandles = 19,
14 SessionClosedByRemote = 26,
15 PortNameTooLong = 30,
16 WrongPermission = 46,
17 InvalidBufferDescriptor = 48,
18 MaxConnectionsReached = 52,
19};
20}
21
22// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
23// double check that the code matches before re-using the constant.
24
25constexpr ResultCode ERR_OUT_OF_HANDLES(ErrCodes::OutOfHandles, ErrorModule::Kernel,
26 ErrorSummary::OutOfResource,
27 ErrorLevel::Permanent); // 0xD8600413
28constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrCodes::SessionClosedByRemote, ErrorModule::OS,
29 ErrorSummary::Canceled,
30 ErrorLevel::Status); // 0xC920181A
31constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrCodes::PortNameTooLong, ErrorModule::OS,
32 ErrorSummary::InvalidArgument,
33 ErrorLevel::Usage); // 0xE0E0181E
34constexpr ResultCode ERR_WRONG_PERMISSION(ErrCodes::WrongPermission, ErrorModule::OS,
35 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
36constexpr ResultCode ERR_INVALID_BUFFER_DESCRIPTOR(ErrCodes::InvalidBufferDescriptor,
37 ErrorModule::OS, ErrorSummary::WrongArgument,
38 ErrorLevel::Permanent);
39constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrCodes::MaxConnectionsReached, ErrorModule::OS,
40 ErrorSummary::WouldBlock,
41 ErrorLevel::Temporary); // 0xD0401834
42
43constexpr ResultCode ERR_NOT_AUTHORIZED(ErrorDescription::NotAuthorized, ErrorModule::OS,
44 ErrorSummary::WrongArgument,
45 ErrorLevel::Permanent); // 0xD9001BEA
46constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel,
47 ErrorSummary::InvalidArgument,
48 ErrorLevel::Permanent); // 0xD8E007ED
49constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(ErrorDescription::InvalidEnumValue,
50 ErrorModule::FND, ErrorSummary::InvalidArgument,
51 ErrorLevel::Permanent); // 0xD8E093ED
52constexpr ResultCode ERR_INVALID_COMBINATION(ErrorDescription::InvalidCombination, ErrorModule::OS,
53 ErrorSummary::InvalidArgument,
54 ErrorLevel::Usage); // 0xE0E01BEE
55constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorDescription::InvalidCombination,
56 ErrorModule::Kernel,
57 ErrorSummary::WrongArgument,
58 ErrorLevel::Permanent); // 0xD90007EE
59constexpr ResultCode ERR_MISALIGNED_ADDRESS(ErrorDescription::MisalignedAddress, ErrorModule::OS,
60 ErrorSummary::InvalidArgument,
61 ErrorLevel::Usage); // 0xE0E01BF1
62constexpr ResultCode ERR_MISALIGNED_SIZE(ErrorDescription::MisalignedSize, ErrorModule::OS,
63 ErrorSummary::InvalidArgument,
64 ErrorLevel::Usage); // 0xE0E01BF2
65constexpr ResultCode ERR_OUT_OF_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
66 ErrorSummary::OutOfResource,
67 ErrorLevel::Permanent); // 0xD86007F3
68constexpr ResultCode ERR_NOT_IMPLEMENTED(ErrorDescription::NotImplemented, ErrorModule::OS,
69 ErrorSummary::InvalidArgument,
70 ErrorLevel::Usage); // 0xE0E01BF4
71constexpr ResultCode ERR_INVALID_ADDRESS(ErrorDescription::InvalidAddress, ErrorModule::OS,
72 ErrorSummary::InvalidArgument,
73 ErrorLevel::Usage); // 0xE0E01BF5
74constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorDescription::InvalidAddress, ErrorModule::OS,
75 ErrorSummary::InvalidState,
76 ErrorLevel::Usage); // 0xE0A01BF5
77constexpr ResultCode ERR_INVALID_POINTER(ErrorDescription::InvalidPointer, ErrorModule::Kernel,
78 ErrorSummary::InvalidArgument,
79 ErrorLevel::Permanent); // 0xD8E007F6
80constexpr ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::Kernel,
81 ErrorSummary::InvalidArgument,
82 ErrorLevel::Permanent); // 0xD8E007F7
83/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
84constexpr ResultCode ERR_INVALID_HANDLE_OS(ErrorDescription::InvalidHandle, ErrorModule::OS,
85 ErrorSummary::WrongArgument,
86 ErrorLevel::Permanent); // 0xD9001BF7
87constexpr ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel,
88 ErrorSummary::NotFound, ErrorLevel::Permanent); // 0xD88007FA
89constexpr ResultCode ERR_OUT_OF_RANGE(ErrorDescription::OutOfRange, ErrorModule::OS,
90 ErrorSummary::InvalidArgument,
91 ErrorLevel::Usage); // 0xE0E01BFD
92constexpr ResultCode ERR_OUT_OF_RANGE_KERNEL(ErrorDescription::OutOfRange, ErrorModule::Kernel,
93 ErrorSummary::InvalidArgument,
94 ErrorLevel::Permanent); // 0xD8E007FD
95constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS,
96 ErrorSummary::StatusChanged, ErrorLevel::Info);
97
98} // namespace Kernel
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index 3e3673508..cc41abb85 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -6,6 +6,7 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/wait_object.h"
9 10
10namespace Kernel { 11namespace Kernel {
11 12
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
new file mode 100644
index 000000000..c7322d883
--- /dev/null
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -0,0 +1,97 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <utility>
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/hle/kernel/errors.h"
9#include "core/hle/kernel/handle_table.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/process.h"
12#include "core/hle/kernel/thread.h"
13
14namespace Kernel {
15
16HandleTable g_handle_table;
17
18HandleTable::HandleTable() {
19 next_generation = 1;
20 Clear();
21}
22
23ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
24 DEBUG_ASSERT(obj != nullptr);
25
26 u16 slot = next_free_slot;
27 if (slot >= generations.size()) {
28 LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
29 return ERR_OUT_OF_HANDLES;
30 }
31 next_free_slot = generations[slot];
32
33 u16 generation = next_generation++;
34
35 // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
36 // CTR-OS doesn't use generation 0, so skip straight to 1.
37 if (next_generation >= (1 << 15))
38 next_generation = 1;
39
40 generations[slot] = generation;
41 objects[slot] = std::move(obj);
42
43 Handle handle = generation | (slot << 15);
44 return MakeResult<Handle>(handle);
45}
46
47ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
48 SharedPtr<Object> object = GetGeneric(handle);
49 if (object == nullptr) {
50 LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle);
51 return ERR_INVALID_HANDLE;
52 }
53 return Create(std::move(object));
54}
55
56ResultCode HandleTable::Close(Handle handle) {
57 if (!IsValid(handle))
58 return ERR_INVALID_HANDLE;
59
60 u16 slot = GetSlot(handle);
61
62 objects[slot] = nullptr;
63
64 generations[slot] = next_free_slot;
65 next_free_slot = slot;
66 return RESULT_SUCCESS;
67}
68
69bool HandleTable::IsValid(Handle handle) const {
70 size_t slot = GetSlot(handle);
71 u16 generation = GetGeneration(handle);
72
73 return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
74}
75
76SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
77 if (handle == CurrentThread) {
78 return GetCurrentThread();
79 } else if (handle == CurrentProcess) {
80 return g_current_process;
81 }
82
83 if (!IsValid(handle)) {
84 return nullptr;
85 }
86 return objects[GetSlot(handle)];
87}
88
89void HandleTable::Clear() {
90 for (u16 i = 0; i < MAX_COUNT; ++i) {
91 generations[i] = i + 1;
92 objects[i] = nullptr;
93 }
94 next_free_slot = 0;
95}
96
97} // namespace
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
new file mode 100644
index 000000000..d6aaefbf7
--- /dev/null
+++ b/src/core/hle/kernel/handle_table.h
@@ -0,0 +1,126 @@
1// Copyright 2014 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 <array>
8#include <cstddef>
9#include "common/common_types.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/result.h"
12
13namespace Kernel {
14
15enum KernelHandle : Handle {
16 CurrentThread = 0xFFFF8000,
17 CurrentProcess = 0xFFFF8001,
18};
19
20/**
21 * This class allows the creation of Handles, which are references to objects that can be tested
22 * for validity and looked up. Here they are used to pass references to kernel objects to/from the
23 * emulated process. it has been designed so that it follows the same handle format and has
24 * approximately the same restrictions as the handle manager in the CTR-OS.
25 *
26 * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0).
27 * The slot index is used to index into the arrays in this class to access the data corresponding
28 * to the Handle.
29 *
30 * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter
31 * is kept and incremented every time a Handle is created. This is the Handle's "generation". The
32 * value of the counter is stored into the Handle as well as in the handle table (in the
33 * "generations" array). When looking up a handle, the Handle's generation must match with the
34 * value stored on the class, otherwise the Handle is considered invalid.
35 *
36 * To find free slots when allocating a Handle without needing to scan the entire object array, the
37 * generations field of unallocated slots is re-purposed as a linked list of indices to free slots.
38 * When a Handle is created, an index is popped off the list and used for the new Handle. When it
39 * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is
40 * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been
41 * verified and isn't likely to cause any problems.
42 */
43class HandleTable final : NonCopyable {
44public:
45 HandleTable();
46
47 /**
48 * Allocates a handle for the given object.
49 * @return The created Handle or one of the following errors:
50 * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
51 */
52 ResultVal<Handle> Create(SharedPtr<Object> obj);
53
54 /**
55 * Returns a new handle that points to the same object as the passed in handle.
56 * @return The duplicated Handle or one of the following errors:
57 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
58 * - Any errors returned by `Create()`.
59 */
60 ResultVal<Handle> Duplicate(Handle handle);
61
62 /**
63 * Closes a handle, removing it from the table and decreasing the object's ref-count.
64 * @return `RESULT_SUCCESS` or one of the following errors:
65 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
66 */
67 ResultCode Close(Handle handle);
68
69 /// Checks if a handle is valid and points to an existing object.
70 bool IsValid(Handle handle) const;
71
72 /**
73 * Looks up a handle.
74 * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid.
75 */
76 SharedPtr<Object> GetGeneric(Handle handle) const;
77
78 /**
79 * Looks up a handle while verifying its type.
80 * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
81 * type differs from the requested one.
82 */
83 template <class T>
84 SharedPtr<T> Get(Handle handle) const {
85 return DynamicObjectCast<T>(GetGeneric(handle));
86 }
87
88 /// Closes all handles held in this table.
89 void Clear();
90
91private:
92 /**
93 * This is the maximum limit of handles allowed per process in CTR-OS. It can be further
94 * reduced by ExHeader values, but this is not emulated here.
95 */
96 static const size_t MAX_COUNT = 4096;
97
98 static u16 GetSlot(Handle handle) {
99 return handle >> 15;
100 }
101 static u16 GetGeneration(Handle handle) {
102 return handle & 0x7FFF;
103 }
104
105 /// Stores the Object referenced by the handle or null if the slot is empty.
106 std::array<SharedPtr<Object>, MAX_COUNT> objects;
107
108 /**
109 * The value of `next_generation` when the handle was created, used to check for validity. For
110 * empty slots, contains the index of the next free slot in the list.
111 */
112 std::array<u16, MAX_COUNT> generations;
113
114 /**
115 * Global counter of the number of created handles. Stored in `generations` when a handle is
116 * created, and wraps around to 1 when it hits 0x8000.
117 */
118 u16 next_generation;
119
120 /// Head of the free slots linked list.
121 u16 next_free_slot;
122};
123
124extern HandleTable g_handle_table;
125
126} // namespace
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
new file mode 100644
index 000000000..a60b8ef00
--- /dev/null
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -0,0 +1,26 @@
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 <boost/range/algorithm_ext/erase.hpp>
6#include "common/assert.h"
7#include "common/common_types.h"
8#include "core/hle/kernel/hle_ipc.h"
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/server_session.h"
11
12namespace Kernel {
13
14void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) {
15 server_session->SetHleHandler(shared_from_this());
16 connected_sessions.push_back(server_session);
17}
18
19void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_session) {
20 server_session->SetHleHandler(nullptr);
21 boost::range::remove_erase(connected_sessions, server_session);
22}
23
24HLERequestContext::~HLERequestContext() = default;
25
26} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
new file mode 100644
index 000000000..c30184eab
--- /dev/null
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -0,0 +1,87 @@
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 <memory>
8#include <vector>
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/server_session.h"
11
12namespace Service {
13class ServiceFrameworkBase;
14}
15
16namespace Kernel {
17
18/**
19 * Interface implemented by HLE Session handlers.
20 * This can be provided to a ServerSession in order to hook into several relevant events
21 * (such as a new connection or a SyncRequest) so they can be implemented in the emulator.
22 */
23class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
24public:
25 virtual ~SessionRequestHandler() = default;
26
27 /**
28 * Handles a sync request from the emulated application.
29 * @param server_session The ServerSession that was triggered for this sync request,
30 * it should be used to differentiate which client (As in ClientSession) we're answering to.
31 * TODO(Subv): Use a wrapper structure to hold all the information relevant to
32 * this request (ServerSession, Originator thread, Translated command buffer, etc).
33 * @returns ResultCode the result code of the translate operation.
34 */
35 virtual void HandleSyncRequest(SharedPtr<ServerSession> server_session) = 0;
36
37 /**
38 * Signals that a client has just connected to this HLE handler and keeps the
39 * associated ServerSession alive for the duration of the connection.
40 * @param server_session Owning pointer to the ServerSession associated with the connection.
41 */
42 void ClientConnected(SharedPtr<ServerSession> server_session);
43
44 /**
45 * Signals that a client has just disconnected from this HLE handler and releases the
46 * associated ServerSession.
47 * @param server_session ServerSession associated with the connection.
48 */
49 void ClientDisconnected(SharedPtr<ServerSession> server_session);
50
51protected:
52 /// List of sessions that are connected to this handler.
53 /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list
54 // for the duration of the connection.
55 std::vector<SharedPtr<ServerSession>> connected_sessions;
56};
57
58/**
59 * Class containing information about an in-flight IPC request being handled by an HLE service
60 * implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and
61 * when possible use the APIs in this class to service the request.
62 */
63class HLERequestContext {
64public:
65 ~HLERequestContext();
66
67 /// Returns a pointer to the IPC command buffer for this request.
68 u32* CommandBuffer() const {
69 return cmd_buf;
70 }
71
72 /**
73 * Returns the session through which this request was made. This can be used as a map key to
74 * access per-client data on services.
75 */
76 SharedPtr<ServerSession> Session() const {
77 return session;
78 }
79
80private:
81 friend class Service::ServiceFrameworkBase;
82
83 u32* cmd_buf = nullptr;
84 SharedPtr<ServerSession> session;
85};
86
87} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index f599916f0..7470a97ca 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -2,10 +2,8 @@
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>
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/hle/config_mem.h" 5#include "core/hle/config_mem.h"
6#include "core/hle/kernel/handle_table.h"
9#include "core/hle/kernel/kernel.h" 7#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/memory.h" 8#include "core/hle/kernel/memory.h"
11#include "core/hle/kernel/process.h" 9#include "core/hle/kernel/process.h"
@@ -17,165 +15,6 @@
17namespace Kernel { 15namespace Kernel {
18 16
19unsigned int Object::next_object_id; 17unsigned int Object::next_object_id;
20HandleTable g_handle_table;
21
22void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
23 auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
24 if (itr == waiting_threads.end())
25 waiting_threads.push_back(std::move(thread));
26}
27
28void WaitObject::RemoveWaitingThread(Thread* thread) {
29 auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
30 // If a thread passed multiple handles to the same object,
31 // the kernel might attempt to remove the thread from the object's
32 // waiting threads list multiple times.
33 if (itr != waiting_threads.end())
34 waiting_threads.erase(itr);
35}
36
37SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
38 Thread* candidate = nullptr;
39 s32 candidate_priority = THREADPRIO_LOWEST + 1;
40
41 for (const auto& thread : waiting_threads) {
42 // The list of waiting threads must not contain threads that are not waiting to be awakened.
43 ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
44 thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
45 "Inconsistent thread statuses in waiting_threads");
46
47 if (thread->current_priority >= candidate_priority)
48 continue;
49
50 if (ShouldWait(thread.get()))
51 continue;
52
53 // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or
54 // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready.
55 bool ready_to_run = true;
56 if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) {
57 ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(),
58 [&thread](const SharedPtr<WaitObject>& object) {
59 return object->ShouldWait(thread.get());
60 });
61 }
62
63 if (ready_to_run) {
64 candidate = thread.get();
65 candidate_priority = thread->current_priority;
66 }
67 }
68
69 return candidate;
70}
71
72void WaitObject::WakeupAllWaitingThreads() {
73 while (auto thread = GetHighestPriorityReadyThread()) {
74 if (!thread->IsSleepingOnWaitAll()) {
75 Acquire(thread.get());
76 // Set the output index of the WaitSynchronizationN call to the index of this object.
77 if (thread->wait_set_output) {
78 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this));
79 thread->wait_set_output = false;
80 }
81 } else {
82 for (auto& object : thread->wait_objects) {
83 object->Acquire(thread.get());
84 }
85 // Note: This case doesn't update the output index of WaitSynchronizationN.
86 }
87
88 for (auto& object : thread->wait_objects)
89 object->RemoveWaitingThread(thread.get());
90 thread->wait_objects.clear();
91
92 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
93 thread->ResumeFromWait();
94 }
95}
96
97const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const {
98 return waiting_threads;
99}
100
101HandleTable::HandleTable() {
102 next_generation = 1;
103 Clear();
104}
105
106ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
107 DEBUG_ASSERT(obj != nullptr);
108
109 u16 slot = next_free_slot;
110 if (slot >= generations.size()) {
111 LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
112 return ERR_OUT_OF_HANDLES;
113 }
114 next_free_slot = generations[slot];
115
116 u16 generation = next_generation++;
117
118 // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
119 // CTR-OS doesn't use generation 0, so skip straight to 1.
120 if (next_generation >= (1 << 15))
121 next_generation = 1;
122
123 generations[slot] = generation;
124 objects[slot] = std::move(obj);
125
126 Handle handle = generation | (slot << 15);
127 return MakeResult<Handle>(handle);
128}
129
130ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
131 SharedPtr<Object> object = GetGeneric(handle);
132 if (object == nullptr) {
133 LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle);
134 return ERR_INVALID_HANDLE;
135 }
136 return Create(std::move(object));
137}
138
139ResultCode HandleTable::Close(Handle handle) {
140 if (!IsValid(handle))
141 return ERR_INVALID_HANDLE;
142
143 u16 slot = GetSlot(handle);
144
145 objects[slot] = nullptr;
146
147 generations[slot] = next_free_slot;
148 next_free_slot = slot;
149 return RESULT_SUCCESS;
150}
151
152bool HandleTable::IsValid(Handle handle) const {
153 size_t slot = GetSlot(handle);
154 u16 generation = GetGeneration(handle);
155
156 return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
157}
158
159SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
160 if (handle == CurrentThread) {
161 return GetCurrentThread();
162 } else if (handle == CurrentProcess) {
163 return g_current_process;
164 }
165
166 if (!IsValid(handle)) {
167 return nullptr;
168 }
169 return objects[GetSlot(handle)];
170}
171
172void HandleTable::Clear() {
173 for (u16 i = 0; i < MAX_COUNT; ++i) {
174 generations[i] = i + 1;
175 objects[i] = nullptr;
176 }
177 next_free_slot = 0;
178}
179 18
180/// Initialize the kernel 19/// Initialize the kernel
181void Init(u32 system_mode) { 20void Init(u32 system_mode) {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index bb8b99bb5..9cf288b08 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -4,33 +4,16 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
8#include <array>
9#include <cstddef> 7#include <cstddef>
10#include <string> 8#include <string>
11#include <vector> 9#include <utility>
12#include <boost/smart_ptr/intrusive_ptr.hpp> 10#include <boost/smart_ptr/intrusive_ptr.hpp>
13#include "common/common_types.h" 11#include "common/common_types.h"
14#include "core/hle/result.h"
15 12
16namespace Kernel { 13namespace Kernel {
17 14
18using Handle = u32; 15using Handle = u32;
19 16
20class Thread;
21
22// TODO: Verify code
23const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
24 ErrorSummary::OutOfResource, ErrorLevel::Temporary);
25// TOOD: Verify code
26const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::Kernel,
27 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
28
29enum KernelHandle : Handle {
30 CurrentThread = 0xFFFF8000,
31 CurrentProcess = 0xFFFF8001,
32};
33
34enum class HandleType : u32 { 17enum class HandleType : u32 {
35 Unknown, 18 Unknown,
36 Event, 19 Event,
@@ -128,170 +111,17 @@ inline void intrusive_ptr_release(Object* object) {
128template <typename T> 111template <typename T>
129using SharedPtr = boost::intrusive_ptr<T>; 112using SharedPtr = boost::intrusive_ptr<T>;
130 113
131/// Class that represents a Kernel object that a thread can be waiting on
132class WaitObject : public Object {
133public:
134 /**
135 * Check if the specified thread should wait until the object is available
136 * @param thread The thread about which we're deciding.
137 * @return True if the current thread should wait due to this object being unavailable
138 */
139 virtual bool ShouldWait(Thread* thread) const = 0;
140
141 /// Acquire/lock the object for the specified thread if it is available
142 virtual void Acquire(Thread* thread) = 0;
143
144 /**
145 * Add a thread to wait on this object
146 * @param thread Pointer to thread to add
147 */
148 virtual void AddWaitingThread(SharedPtr<Thread> thread);
149
150 /**
151 * Removes a thread from waiting on this object (e.g. if it was resumed already)
152 * @param thread Pointer to thread to remove
153 */
154 virtual void RemoveWaitingThread(Thread* thread);
155
156 /**
157 * Wake up all threads waiting on this object that can be awoken, in priority order,
158 * and set the synchronization result and output of the thread.
159 */
160 virtual void WakeupAllWaitingThreads();
161
162 /// Obtains the highest priority thread that is ready to run from this object's waiting list.
163 SharedPtr<Thread> GetHighestPriorityReadyThread();
164
165 /// Get a const reference to the waiting threads list for debug use
166 const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const;
167
168private:
169 /// Threads waiting for this object to become available
170 std::vector<SharedPtr<Thread>> waiting_threads;
171};
172
173/** 114/**
174 * This class allows the creation of Handles, which are references to objects that can be tested 115 * Attempts to downcast the given Object pointer to a pointer to T.
175 * for validity and looked up. Here they are used to pass references to kernel objects to/from the 116 * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T.
176 * emulated process. it has been designed so that it follows the same handle format and has
177 * approximately the same restrictions as the handle manager in the CTR-OS.
178 *
179 * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0).
180 * The slot index is used to index into the arrays in this class to access the data corresponding
181 * to the Handle.
182 *
183 * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter
184 * is kept and incremented every time a Handle is created. This is the Handle's "generation". The
185 * value of the counter is stored into the Handle as well as in the handle table (in the
186 * "generations" array). When looking up a handle, the Handle's generation must match with the
187 * value stored on the class, otherwise the Handle is considered invalid.
188 *
189 * To find free slots when allocating a Handle without needing to scan the entire object array, the
190 * generations field of unallocated slots is re-purposed as a linked list of indices to free slots.
191 * When a Handle is created, an index is popped off the list and used for the new Handle. When it
192 * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is
193 * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been
194 * verified and isn't likely to cause any problems.
195 */ 117 */
196class HandleTable final : NonCopyable { 118template <typename T>
197public: 119inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
198 HandleTable(); 120 if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
199 121 return boost::static_pointer_cast<T>(std::move(object));
200 /**
201 * Allocates a handle for the given object.
202 * @return The created Handle or one of the following errors:
203 * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
204 */
205 ResultVal<Handle> Create(SharedPtr<Object> obj);
206
207 /**
208 * Returns a new handle that points to the same object as the passed in handle.
209 * @return The duplicated Handle or one of the following errors:
210 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
211 * - Any errors returned by `Create()`.
212 */
213 ResultVal<Handle> Duplicate(Handle handle);
214
215 /**
216 * Closes a handle, removing it from the table and decreasing the object's ref-count.
217 * @return `RESULT_SUCCESS` or one of the following errors:
218 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
219 */
220 ResultCode Close(Handle handle);
221
222 /// Checks if a handle is valid and points to an existing object.
223 bool IsValid(Handle handle) const;
224
225 /**
226 * Looks up a handle.
227 * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid.
228 */
229 SharedPtr<Object> GetGeneric(Handle handle) const;
230
231 /**
232 * Looks up a handle while verifying its type.
233 * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
234 * type differs from the handle type `T::HANDLE_TYPE`.
235 */
236 template <class T>
237 SharedPtr<T> Get(Handle handle) const {
238 SharedPtr<Object> object = GetGeneric(handle);
239 if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
240 return boost::static_pointer_cast<T>(std::move(object));
241 }
242 return nullptr;
243 }
244
245 /**
246 * Looks up a handle while verifying that it is an object that a thread can wait on
247 * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or it is
248 * not a waitable object.
249 */
250 SharedPtr<WaitObject> GetWaitObject(Handle handle) const {
251 SharedPtr<Object> object = GetGeneric(handle);
252 if (object != nullptr && object->IsWaitable()) {
253 return boost::static_pointer_cast<WaitObject>(std::move(object));
254 }
255 return nullptr;
256 }
257
258 /// Closes all handles held in this table.
259 void Clear();
260
261private:
262 /**
263 * This is the maximum limit of handles allowed per process in CTR-OS. It can be further
264 * reduced by ExHeader values, but this is not emulated here.
265 */
266 static const size_t MAX_COUNT = 4096;
267
268 static u16 GetSlot(Handle handle) {
269 return handle >> 15;
270 }
271 static u16 GetGeneration(Handle handle) {
272 return handle & 0x7FFF;
273 } 122 }
274 123 return nullptr;
275 /// Stores the Object referenced by the handle or null if the slot is empty. 124}
276 std::array<SharedPtr<Object>, MAX_COUNT> objects;
277
278 /**
279 * The value of `next_generation` when the handle was created, used to check for validity. For
280 * empty slots, contains the index of the next free slot in the list.
281 */
282 std::array<u16, MAX_COUNT> generations;
283
284 /**
285 * Global counter of the number of created handles. Stored in `generations` when a handle is
286 * created, and wraps around to 1 when it hits 0x8000.
287 */
288 u16 next_generation;
289
290 /// Head of the free slots linked list.
291 u16 next_free_slot;
292};
293
294extern HandleTable g_handle_table;
295 125
296/// Initialize the kernel with the specified system mode. 126/// Initialize the kernel with the specified system mode.
297void Init(u32 system_mode); 127void Init(u32 system_mode);
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 8250a90b5..804f23b1c 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.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 <cinttypes> 6#include <cinttypes>
6#include <map> 7#include <map>
7#include <memory> 8#include <memory>
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index c57adf400..bacacd690 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -7,6 +7,7 @@
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/wait_object.h"
10 11
11namespace Kernel { 12namespace Kernel {
12 13
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 32cb25fb7..1c31ec950 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -6,6 +6,7 @@
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/hle/kernel/errors.h"
9#include "core/hle/kernel/memory.h" 10#include "core/hle/kernel/memory.h"
10#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
11#include "core/hle/kernel/resource_limit.h" 12#include "core/hle/kernel/resource_limit.h"
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index 3f51bc5de..a8f10a3ee 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6#include "common/assert.h"
6#include "common/logging/log.h" 7#include "common/logging/log.h"
7#include "core/hle/kernel/resource_limit.h" 8#include "core/hle/kernel/resource_limit.h"
8 9
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
index 8bda2f75d..fcf586728 100644
--- a/src/core/hle/kernel/semaphore.cpp
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "core/hle/kernel/errors.h"
6#include "core/hle/kernel/kernel.h" 7#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/semaphore.h" 8#include "core/hle/kernel/semaphore.h"
8#include "core/hle/kernel/thread.h" 9#include "core/hle/kernel/thread.h"
@@ -16,8 +17,7 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou
16 std::string name) { 17 std::string name) {
17 18
18 if (initial_count > max_count) 19 if (initial_count > max_count)
19 return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, 20 return ERR_INVALID_COMBINATION_KERNEL;
20 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
21 21
22 SharedPtr<Semaphore> semaphore(new Semaphore); 22 SharedPtr<Semaphore> semaphore(new Semaphore);
23 23
@@ -42,8 +42,7 @@ void Semaphore::Acquire(Thread* thread) {
42 42
43ResultVal<s32> Semaphore::Release(s32 release_count) { 43ResultVal<s32> Semaphore::Release(s32 release_count) {
44 if (max_count - available_count < release_count) 44 if (max_count - available_count < release_count)
45 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, 45 return ERR_OUT_OF_RANGE_KERNEL;
46 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
47 46
48 s32 previous_count = available_count; 47 s32 previous_count = available_count;
49 available_count += release_count; 48 available_count += release_count;
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
index cde94f7cc..7b0cacf2e 100644
--- a/src/core/hle/kernel/semaphore.h
+++ b/src/core/hle/kernel/semaphore.h
@@ -8,6 +8,8 @@
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/wait_object.h"
12#include "core/hle/result.h"
11 13
12namespace Kernel { 14namespace Kernel {
13 15
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index fd3bbbcad..4d20c39a1 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -24,14 +24,12 @@ void ServerPort::Acquire(Thread* thread) {
24} 24}
25 25
26std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( 26std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair(
27 u32 max_sessions, std::string name, 27 u32 max_sessions, std::string name) {
28 std::shared_ptr<Service::SessionRequestHandler> hle_handler) {
29 28
30 SharedPtr<ServerPort> server_port(new ServerPort); 29 SharedPtr<ServerPort> server_port(new ServerPort);
31 SharedPtr<ClientPort> client_port(new ClientPort); 30 SharedPtr<ClientPort> client_port(new ClientPort);
32 31
33 server_port->name = name + "_Server"; 32 server_port->name = name + "_Server";
34 server_port->hle_handler = std::move(hle_handler);
35 client_port->name = name + "_Client"; 33 client_port->name = name + "_Client";
36 client_port->server_port = server_port; 34 client_port->server_port = server_port;
37 client_port->max_sessions = max_sessions; 35 client_port->max_sessions = max_sessions;
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index 6f8bdb6a9..f1419cd46 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -9,28 +9,24 @@
9#include <tuple> 9#include <tuple>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12 12#include "core/hle/kernel/wait_object.h"
13namespace Service {
14class SessionRequestHandler;
15}
16 13
17namespace Kernel { 14namespace Kernel {
18 15
19class ClientPort; 16class ClientPort;
17class SessionRequestHandler;
20 18
21class ServerPort final : public WaitObject { 19class ServerPort final : public WaitObject {
22public: 20public:
23 /** 21 /**
24 * Creates a pair of ServerPort and an associated ClientPort. 22 * Creates a pair of ServerPort and an associated ClientPort.
23 *
25 * @param max_sessions Maximum number of sessions to the port 24 * @param max_sessions Maximum number of sessions to the port
26 * @param name Optional name of the ports 25 * @param name Optional name of the ports
27 * @param hle_handler Optional HLE handler template for the port,
28 * ServerSessions crated from this port will inherit a reference to this handler.
29 * @return The created port tuple 26 * @return The created port tuple
30 */ 27 */
31 static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair( 28 static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair(
32 u32 max_sessions, std::string name = "UnknownPort", 29 u32 max_sessions, std::string name = "UnknownPort");
33 std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr);
34 30
35 std::string GetTypeName() const override { 31 std::string GetTypeName() const override {
36 return "ServerPort"; 32 return "ServerPort";
@@ -44,6 +40,14 @@ public:
44 return HANDLE_TYPE; 40 return HANDLE_TYPE;
45 } 41 }
46 42
43 /**
44 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
45 * will inherit a reference to this handler.
46 */
47 void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) {
48 hle_handler = std::move(hle_handler_);
49 }
50
47 std::string name; ///< Name of port (optional) 51 std::string name; ///< Name of port (optional)
48 52
49 std::vector<SharedPtr<WaitObject>> 53 std::vector<SharedPtr<WaitObject>>
@@ -51,7 +55,7 @@ public:
51 55
52 /// This session's HLE request handler template (optional) 56 /// This session's HLE request handler template (optional)
53 /// ServerSessions created from this port inherit a reference to this handler. 57 /// ServerSessions created from this port inherit a reference to this handler.
54 std::shared_ptr<Service::SessionRequestHandler> hle_handler; 58 std::shared_ptr<SessionRequestHandler> hle_handler;
55 59
56 bool ShouldWait(Thread* thread) const override; 60 bool ShouldWait(Thread* thread) const override;
57 void Acquire(Thread* thread) override; 61 void Acquire(Thread* thread) override;
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 500b909ab..2dc709bc9 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -4,8 +4,11 @@
4 4
5#include <tuple> 5#include <tuple>
6 6
7#include "core/hle/kernel/client_port.h"
7#include "core/hle/kernel/client_session.h" 8#include "core/hle/kernel/client_session.h"
9#include "core/hle/kernel/hle_ipc.h"
8#include "core/hle/kernel/server_session.h" 10#include "core/hle/kernel/server_session.h"
11#include "core/hle/kernel/session.h"
9#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
10 13
11namespace Kernel { 14namespace Kernel {
@@ -25,16 +28,14 @@ ServerSession::~ServerSession() {
25 parent->server = nullptr; 28 parent->server = nullptr;
26} 29}
27 30
28ResultVal<SharedPtr<ServerSession>> ServerSession::Create( 31ResultVal<SharedPtr<ServerSession>> ServerSession::Create(std::string name) {
29 std::string name, std::shared_ptr<Service::SessionRequestHandler> hle_handler) {
30 SharedPtr<ServerSession> server_session(new ServerSession); 32 SharedPtr<ServerSession> server_session(new ServerSession);
31 33
32 server_session->name = std::move(name); 34 server_session->name = std::move(name);
33 server_session->signaled = false; 35 server_session->signaled = false;
34 server_session->hle_handler = std::move(hle_handler);
35 server_session->parent = nullptr; 36 server_session->parent = nullptr;
36 37
37 return MakeResult<SharedPtr<ServerSession>>(std::move(server_session)); 38 return MakeResult(std::move(server_session));
38} 39}
39 40
40bool ServerSession::ShouldWait(Thread* thread) const { 41bool ServerSession::ShouldWait(Thread* thread) const {
@@ -68,13 +69,9 @@ ResultCode ServerSession::HandleSyncRequest() {
68 return RESULT_SUCCESS; 69 return RESULT_SUCCESS;
69} 70}
70 71
71ServerSession::SessionPair ServerSession::CreateSessionPair( 72ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name,
72 const std::string& name, std::shared_ptr<Service::SessionRequestHandler> hle_handler, 73 SharedPtr<ClientPort> port) {
73 SharedPtr<ClientPort> port) { 74 auto server_session = ServerSession::Create(name + "_Server").MoveFrom();
74
75 auto server_session =
76 ServerSession::Create(name + "_Server", std::move(hle_handler)).MoveFrom();
77
78 SharedPtr<ClientSession> client_session(new ClientSession); 75 SharedPtr<ClientSession> client_session(new ClientSession);
79 client_session->name = name + "_Client"; 76 client_session->name = name + "_Client";
80 77
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index c907d487c..5365605da 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -9,10 +9,8 @@
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/session.h" 12#include "core/hle/kernel/wait_object.h"
13#include "core/hle/kernel/thread.h"
14#include "core/hle/result.h" 13#include "core/hle/result.h"
15#include "core/hle/service/service.h"
16#include "core/memory.h" 14#include "core/memory.h"
17 15
18namespace Kernel { 16namespace Kernel {
@@ -20,6 +18,9 @@ namespace Kernel {
20class ClientSession; 18class ClientSession;
21class ClientPort; 19class ClientPort;
22class ServerSession; 20class ServerSession;
21class Session;
22class SessionRequestHandler;
23class Thread;
23 24
24/** 25/**
25 * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS 26 * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
@@ -49,14 +50,20 @@ public:
49 /** 50 /**
50 * Creates a pair of ServerSession and an associated ClientSession. 51 * Creates a pair of ServerSession and an associated ClientSession.
51 * @param name Optional name of the ports. 52 * @param name Optional name of the ports.
52 * @param hle_handler Optional HLE handler for this server session.
53 * @param client_port Optional The ClientPort that spawned this session. 53 * @param client_port Optional The ClientPort that spawned this session.
54 * @return The created session tuple 54 * @return The created session tuple
55 */ 55 */
56 static SessionPair CreateSessionPair( 56 static SessionPair CreateSessionPair(const std::string& name = "Unknown",
57 const std::string& name = "Unknown", 57 SharedPtr<ClientPort> client_port = nullptr);
58 std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr, 58
59 SharedPtr<ClientPort> client_port = nullptr); 59 /**
60 * Sets the HLE handler for the session. This handler will be called to service IPC requests
61 * instead of the regular IPC machinery. (The regular IPC machinery is currently not
62 * implemented.)
63 */
64 void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) {
65 hle_handler = std::move(hle_handler_);
66 }
60 67
61 /** 68 /**
62 * Handle a sync request from the emulated application. 69 * Handle a sync request from the emulated application.
@@ -71,7 +78,7 @@ public:
71 std::string name; ///< The name of this session (optional) 78 std::string name; ///< The name of this session (optional)
72 bool signaled; ///< Whether there's new data available to this ServerSession 79 bool signaled; ///< Whether there's new data available to this ServerSession
73 std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint. 80 std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint.
74 std::shared_ptr<Service::SessionRequestHandler> 81 std::shared_ptr<SessionRequestHandler>
75 hle_handler; ///< This session's HLE request handler (optional) 82 hle_handler; ///< This session's HLE request handler (optional)
76 83
77private: 84private:
@@ -82,12 +89,9 @@ private:
82 * Creates a server session. The server session can have an optional HLE handler, 89 * Creates a server session. The server session can have an optional HLE handler,
83 * which will be invoked to handle the IPC requests that this session receives. 90 * which will be invoked to handle the IPC requests that this session receives.
84 * @param name Optional name of the server session. 91 * @param name Optional name of the server session.
85 * @param hle_handler Optional HLE handler for this server session.
86 * @return The created server session 92 * @return The created server session
87 */ 93 */
88 static ResultVal<SharedPtr<ServerSession>> Create( 94 static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown");
89 std::string name = "Unknown",
90 std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr);
91}; 95};
92 96
93/** 97/**
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index bc1560d12..922e5ab58 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -4,6 +4,7 @@
4 4
5#include <cstring> 5#include <cstring>
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/hle/kernel/errors.h"
7#include "core/hle/kernel/memory.h" 8#include "core/hle/kernel/memory.h"
8#include "core/hle/kernel/shared_memory.h" 9#include "core/hle/kernel/shared_memory.h"
9#include "core/memory.h" 10#include "core/memory.h"
@@ -102,24 +103,21 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
102 103
103 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare 104 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
104 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { 105 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
105 return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, 106 return ERR_INVALID_COMBINATION;
106 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
107 } 107 }
108 108
109 // Error out if the requested permissions don't match what the creator process allows. 109 // Error out if the requested permissions don't match what the creator process allows.
110 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { 110 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
111 LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", 111 LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
112 GetObjectId(), address, name.c_str()); 112 GetObjectId(), address, name.c_str());
113 return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, 113 return ERR_INVALID_COMBINATION;
114 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
115 } 114 }
116 115
117 // Heap-backed memory blocks can not be mapped with other_permissions = DontCare 116 // Heap-backed memory blocks can not be mapped with other_permissions = DontCare
118 if (base_address != 0 && other_permissions == MemoryPermission::DontCare) { 117 if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
119 LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", 118 LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
120 GetObjectId(), address, name.c_str()); 119 GetObjectId(), address, name.c_str());
121 return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, 120 return ERR_INVALID_COMBINATION;
122 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
123 } 121 }
124 122
125 // Error out if the provided permissions are not compatible with what the creator process needs. 123 // Error out if the provided permissions are not compatible with what the creator process needs.
@@ -127,8 +125,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
127 static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) { 125 static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
128 LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match", 126 LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
129 GetObjectId(), address, name.c_str()); 127 GetObjectId(), address, name.c_str());
130 return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS, 128 return ERR_WRONG_PERMISSION;
131 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
132 } 129 }
133 130
134 // TODO(Subv): Check for the Shared Device Mem flag in the creator process. 131 // TODO(Subv): Check for the Shared Device Mem flag in the creator process.
@@ -144,8 +141,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
144 if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) { 141 if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
145 LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, invalid address", 142 LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, invalid address",
146 GetObjectId(), address, name.c_str()); 143 GetObjectId(), address, name.c_str());
147 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, 144 return ERR_INVALID_ADDRESS;
148 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
149 } 145 }
150 } 146 }
151 147
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 3b7555d87..75ce626f8 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -14,6 +14,8 @@
14#include "core/arm/skyeye_common/armstate.h" 14#include "core/arm/skyeye_common/armstate.h"
15#include "core/core.h" 15#include "core/core.h"
16#include "core/core_timing.h" 16#include "core/core_timing.h"
17#include "core/hle/kernel/errors.h"
18#include "core/hle/kernel/handle_table.h"
17#include "core/hle/kernel/kernel.h" 19#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/memory.h" 20#include "core/hle/kernel/memory.h"
19#include "core/hle/kernel/mutex.h" 21#include "core/hle/kernel/mutex.h"
@@ -241,9 +243,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
241 for (auto& object : thread->wait_objects) 243 for (auto& object : thread->wait_objects)
242 object->RemoveWaitingThread(thread.get()); 244 object->RemoveWaitingThread(thread.get());
243 thread->wait_objects.clear(); 245 thread->wait_objects.clear();
244 thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, 246 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
245 ErrorSummary::StatusChanged,
246 ErrorLevel::Info));
247 } 247 }
248 248
249 thread->ResumeFromWait(); 249 thread->ResumeFromWait();
@@ -351,10 +351,20 @@ static void ResetThreadContext(ARM_Interface::ThreadContext& context, u32 stack_
351 context.cpsr = USER32MODE | ((entry_point & 1) << 5); // Usermode and THUMB mode 351 context.cpsr = USER32MODE | ((entry_point & 1) << 5); // Usermode and THUMB mode
352} 352}
353 353
354ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, 354ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority,
355 u32 arg, s32 processor_id, VAddr stack_top) { 355 u32 arg, s32 processor_id, VAddr stack_top) {
356 ASSERT_MSG(priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST, 356 // Check if priority is in ranged. Lowest priority -> highest priority id.
357 "Invalid thread priority"); 357 if (priority > THREADPRIO_LOWEST) {
358 LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority);
359 return ERR_OUT_OF_RANGE;
360 }
361
362 if (processor_id > THREADPROCESSORID_MAX) {
363 LOG_ERROR(Kernel_SVC, "Invalid processor id: %d", processor_id);
364 return ERR_OUT_OF_RANGE_KERNEL;
365 }
366
367 // TODO(yuriks): Other checks, returning 0xD9001BEA
358 368
359 if (!Memory::IsValidVirtualAddress(entry_point)) { 369 if (!Memory::IsValidVirtualAddress(entry_point)) {
360 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); 370 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
@@ -399,8 +409,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
399 if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) { 409 if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) {
400 LOG_ERROR(Kernel_SVC, 410 LOG_ERROR(Kernel_SVC,
401 "Not enough space in region to allocate a new TLS page for thread"); 411 "Not enough space in region to allocate a new TLS page for thread");
402 return ResultCode(ErrorDescription::OutOfMemory, ErrorModule::Kernel, 412 return ERR_OUT_OF_MEMORY;
403 ErrorSummary::OutOfResource, ErrorLevel::Permanent);
404 } 413 }
405 414
406 u32 offset = linheap_memory->size(); 415 u32 offset = linheap_memory->size();
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 6ab31c70b..6a3566f15 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -12,6 +12,7 @@
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/arm/arm_interface.h" 13#include "core/arm/arm_interface.h"
14#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
15#include "core/hle/kernel/wait_object.h"
15#include "core/hle/result.h" 16#include "core/hle/result.h"
16 17
17enum ThreadPriority : s32 { 18enum ThreadPriority : s32 {
@@ -57,7 +58,7 @@ public:
57 * @param stack_top The address of the thread's stack top 58 * @param stack_top The address of the thread's stack top
58 * @return A shared pointer to the newly created thread 59 * @return A shared pointer to the newly created thread
59 */ 60 */
60 static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, 61 static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority,
61 u32 arg, s32 processor_id, VAddr stack_top); 62 u32 arg, s32 processor_id, VAddr stack_top);
62 63
63 std::string GetName() const override { 64 std::string GetName() const override {
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index a00c75679..6f2cf3b02 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -6,6 +6,7 @@
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hle/kernel/handle_table.h"
9#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/thread.h" 11#include "core/hle/kernel/thread.h"
11#include "core/hle/kernel/timer.h" 12#include "core/hle/kernel/timer.h"
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h
index b0f818933..82552372d 100644
--- a/src/core/hle/kernel/timer.h
+++ b/src/core/hle/kernel/timer.h
@@ -6,6 +6,7 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/wait_object.h"
9 10
10namespace Kernel { 11namespace Kernel {
11 12
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 6dd24f846..cef1f7fa8 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -4,6 +4,7 @@
4 4
5#include <iterator> 5#include <iterator>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/hle/kernel/errors.h"
7#include "core/hle/kernel/vm_manager.h" 8#include "core/hle/kernel/vm_manager.h"
8#include "core/memory.h" 9#include "core/memory.h"
9#include "core/memory_setup.h" 10#include "core/memory_setup.h"
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 9055664b2..38e0d74d0 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -13,14 +13,6 @@
13 13
14namespace Kernel { 14namespace Kernel {
15 15
16const ResultCode ERR_INVALID_ADDRESS{// 0xE0E01BF5
17 ErrorDescription::InvalidAddress, ErrorModule::OS,
18 ErrorSummary::InvalidArgument, ErrorLevel::Usage};
19
20const ResultCode ERR_INVALID_ADDRESS_STATE{// 0xE0A01BF5
21 ErrorDescription::InvalidAddress, ErrorModule::OS,
22 ErrorSummary::InvalidState, ErrorLevel::Usage};
23
24enum class VMAType : u8 { 16enum class VMAType : u8 {
25 /// VMA represents an unmapped region of the address space. 17 /// VMA represents an unmapped region of the address space.
26 Free, 18 Free,
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
new file mode 100644
index 000000000..f245eda6c
--- /dev/null
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -0,0 +1,99 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/hle/config_mem.h"
9#include "core/hle/kernel/errors.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/memory.h"
12#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/resource_limit.h"
14#include "core/hle/kernel/thread.h"
15#include "core/hle/kernel/timer.h"
16#include "core/hle/shared_page.h"
17
18namespace Kernel {
19
20void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
21 auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
22 if (itr == waiting_threads.end())
23 waiting_threads.push_back(std::move(thread));
24}
25
26void WaitObject::RemoveWaitingThread(Thread* thread) {
27 auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
28 // If a thread passed multiple handles to the same object,
29 // the kernel might attempt to remove the thread from the object's
30 // waiting threads list multiple times.
31 if (itr != waiting_threads.end())
32 waiting_threads.erase(itr);
33}
34
35SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
36 Thread* candidate = nullptr;
37 s32 candidate_priority = THREADPRIO_LOWEST + 1;
38
39 for (const auto& thread : waiting_threads) {
40 // The list of waiting threads must not contain threads that are not waiting to be awakened.
41 ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
42 thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
43 "Inconsistent thread statuses in waiting_threads");
44
45 if (thread->current_priority >= candidate_priority)
46 continue;
47
48 if (ShouldWait(thread.get()))
49 continue;
50
51 // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or
52 // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready.
53 bool ready_to_run = true;
54 if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) {
55 ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(),
56 [&thread](const SharedPtr<WaitObject>& object) {
57 return object->ShouldWait(thread.get());
58 });
59 }
60
61 if (ready_to_run) {
62 candidate = thread.get();
63 candidate_priority = thread->current_priority;
64 }
65 }
66
67 return candidate;
68}
69
70void WaitObject::WakeupAllWaitingThreads() {
71 while (auto thread = GetHighestPriorityReadyThread()) {
72 if (!thread->IsSleepingOnWaitAll()) {
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 {
80 for (auto& object : thread->wait_objects) {
81 object->Acquire(thread.get());
82 }
83 // Note: This case doesn't update the output index of WaitSynchronizationN.
84 }
85
86 for (auto& object : thread->wait_objects)
87 object->RemoveWaitingThread(thread.get());
88 thread->wait_objects.clear();
89
90 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
91 thread->ResumeFromWait();
92 }
93}
94
95const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const {
96 return waiting_threads;
97}
98
99} // namespace Kernel
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h
new file mode 100644
index 000000000..861578186
--- /dev/null
+++ b/src/core/hle/kernel/wait_object.h
@@ -0,0 +1,67 @@
1// Copyright 2014 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 <vector>
8#include <boost/smart_ptr/intrusive_ptr.hpp>
9#include "common/common_types.h"
10#include "core/hle/kernel/kernel.h"
11
12namespace Kernel {
13
14class Thread;
15
16/// Class that represents a Kernel object that a thread can be waiting on
17class WaitObject : public Object {
18public:
19 /**
20 * Check if the specified thread should wait until the object is available
21 * @param thread The thread about which we're deciding.
22 * @return True if the current thread should wait due to this object being unavailable
23 */
24 virtual bool ShouldWait(Thread* thread) const = 0;
25
26 /// Acquire/lock the object for the specified thread if it is available
27 virtual void Acquire(Thread* thread) = 0;
28
29 /**
30 * Add a thread to wait on this object
31 * @param thread Pointer to thread to add
32 */
33 virtual void AddWaitingThread(SharedPtr<Thread> thread);
34
35 /**
36 * Removes a thread from waiting on this object (e.g. if it was resumed already)
37 * @param thread Pointer to thread to remove
38 */
39 virtual void RemoveWaitingThread(Thread* thread);
40
41 /**
42 * Wake up all threads waiting on this object that can be awoken, in priority order,
43 * and set the synchronization result and output of the thread.
44 */
45 virtual void WakeupAllWaitingThreads();
46
47 /// Obtains the highest priority thread that is ready to run from this object's waiting list.
48 SharedPtr<Thread> GetHighestPriorityReadyThread();
49
50 /// Get a const reference to the waiting threads list for debug use
51 const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const;
52
53private:
54 /// Threads waiting for this object to become available
55 std::vector<SharedPtr<Thread>> waiting_threads;
56};
57
58// Specialization of DynamicObjectCast for WaitObjects
59template <>
60inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) {
61 if (object != nullptr && object->IsWaitable()) {
62 return boost::static_pointer_cast<WaitObject>(std::move(object));
63 }
64 return nullptr;
65}
66
67} // namespace Kernel
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 13b948871..5f2cdbb96 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -13,38 +13,15 @@
13 13
14// All the constants in this file come from http://3dbrew.org/wiki/Error_codes 14// All the constants in this file come from http://3dbrew.org/wiki/Error_codes
15 15
16/// Detailed description of the error. This listing is likely incomplete. 16/**
17 * Detailed description of the error. Code 0 always means success. Codes 1000 and above are
18 * considered "well-known" and have common values between all modules. The meaning of other codes
19 * vary by module.
20 */
17enum class ErrorDescription : u32 { 21enum class ErrorDescription : u32 {
18 Success = 0, 22 Success = 0,
19 SessionClosedByRemote = 26, 23
20 WrongPermission = 46, 24 // Codes 1000 and above are considered "well-known" and have common values between all modules.
21 OS_InvalidBufferDescriptor = 48,
22 MaxConnectionsReached = 52,
23 WrongAddress = 53,
24 FS_RomFSNotFound = 100,
25 FS_ArchiveNotMounted = 101,
26 FS_FileNotFound = 112,
27 FS_PathNotFound = 113,
28 FS_GameCardNotInserted = 141,
29 FS_NotFound = 120,
30 FS_FileAlreadyExists = 180,
31 FS_DirectoryAlreadyExists = 185,
32 FS_AlreadyExists = 190,
33 FS_InvalidOpenFlags = 230,
34 FS_DirectoryNotEmpty = 240,
35 FS_NotAFile = 250,
36 FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
37 OutofRangeOrMisalignedAddress =
38 513, // TODO(purpasmart): Check if this name fits its actual usage
39 GPU_FirstInitialization = 519,
40 FS_ExeFSSectionNotFound = 567,
41 FS_CommandNotAllowed = 630,
42 FS_InvalidReadFlag = 700,
43 FS_InvalidPath = 702,
44 FS_WriteBeyondEnd = 705,
45 FS_UnsupportedOpenFlags = 760,
46 FS_IncorrectExeFSReadSize = 761,
47 FS_UnexpectedFileOrDirectory = 770,
48 InvalidSection = 1000, 25 InvalidSection = 1000,
49 TooLarge = 1001, 26 TooLarge = 1001,
50 NotAuthorized = 1002, 27 NotAuthorized = 1002,
@@ -218,7 +195,7 @@ enum class ErrorLevel : u32 {
218union ResultCode { 195union ResultCode {
219 u32 raw; 196 u32 raw;
220 197
221 BitField<0, 10, ErrorDescription> description; 198 BitField<0, 10, u32> description;
222 BitField<10, 8, ErrorModule> module; 199 BitField<10, 8, ErrorModule> module;
223 200
224 BitField<21, 6, ErrorSummary> summary; 201 BitField<21, 6, ErrorSummary> summary;
@@ -228,45 +205,46 @@ union ResultCode {
228 // error 205 // error
229 BitField<31, 1, u32> is_error; 206 BitField<31, 1, u32> is_error;
230 207
231 explicit ResultCode(u32 raw) : raw(raw) {} 208 constexpr explicit ResultCode(u32 raw) : raw(raw) {}
232 ResultCode(ErrorDescription description_, ErrorModule module_, ErrorSummary summary_,
233 ErrorLevel level_)
234 : raw(0) {
235 description.Assign(description_);
236 module.Assign(module_);
237 summary.Assign(summary_);
238 level.Assign(level_);
239 }
240 209
241 ResultCode& operator=(const ResultCode& o) { 210 constexpr ResultCode(ErrorDescription description, ErrorModule module, ErrorSummary summary,
211 ErrorLevel level)
212 : ResultCode(static_cast<u32>(description), module, summary, level) {}
213
214 constexpr ResultCode(u32 description_, ErrorModule module_, ErrorSummary summary_,
215 ErrorLevel level_)
216 : raw(description.FormatValue(description_) | module.FormatValue(module_) |
217 summary.FormatValue(summary_) | level.FormatValue(level_)) {}
218
219 constexpr ResultCode& operator=(const ResultCode& o) {
242 raw = o.raw; 220 raw = o.raw;
243 return *this; 221 return *this;
244 } 222 }
245 223
246 bool IsSuccess() const { 224 constexpr bool IsSuccess() const {
247 return is_error == 0; 225 return is_error.ExtractValue(raw) == 0;
248 } 226 }
249 227
250 bool IsError() const { 228 constexpr bool IsError() const {
251 return is_error == 1; 229 return is_error.ExtractValue(raw) == 1;
252 } 230 }
253}; 231};
254 232
255inline bool operator==(const ResultCode& a, const ResultCode& b) { 233constexpr bool operator==(const ResultCode& a, const ResultCode& b) {
256 return a.raw == b.raw; 234 return a.raw == b.raw;
257} 235}
258 236
259inline bool operator!=(const ResultCode& a, const ResultCode& b) { 237constexpr bool operator!=(const ResultCode& a, const ResultCode& b) {
260 return a.raw != b.raw; 238 return a.raw != b.raw;
261} 239}
262 240
263// Convenience functions for creating some common kinds of errors: 241// Convenience functions for creating some common kinds of errors:
264 242
265/// The default success `ResultCode`. 243/// The default success `ResultCode`.
266const ResultCode RESULT_SUCCESS(0); 244constexpr ResultCode RESULT_SUCCESS(0);
267 245
268/// Might be returned instead of a dummy success for unimplemented APIs. 246/// Might be returned instead of a dummy success for unimplemented APIs.
269inline ResultCode UnimplementedFunction(ErrorModule module) { 247constexpr ResultCode UnimplementedFunction(ErrorModule module) {
270 return ResultCode(ErrorDescription::NotImplemented, module, ErrorSummary::NotSupported, 248 return ResultCode(ErrorDescription::NotImplemented, module, ErrorSummary::NotSupported,
271 ErrorLevel::Permanent); 249 ErrorLevel::Permanent);
272} 250}
@@ -438,6 +416,16 @@ ResultVal<T> MakeResult(Args&&... args) {
438} 416}
439 417
440/** 418/**
419 * Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just
420 * copy or move constructing.
421 */
422template <typename Arg>
423ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) {
424 return ResultVal<std::remove_reference_t<Arg>>::WithCode(RESULT_SUCCESS,
425 std::forward<Arg>(arg));
426}
427
428/**
441 * Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps 429 * Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps
442 * the contained value and assigns it to `target`, which can be either an l-value expression or a 430 * the contained value and assigns it to `target`, which can be either an l-value expression or a
443 * variable declaration. If it fails the return code is returned from the current function. Thus it 431 * variable declaration. If it fails the return code is returned from the current function. Thus it
@@ -448,3 +436,12 @@ ResultVal<T> MakeResult(Args&&... args) {
448 if (CONCAT2(check_result_L, __LINE__).Failed()) \ 436 if (CONCAT2(check_result_L, __LINE__).Failed()) \
449 return CONCAT2(check_result_L, __LINE__).Code(); \ 437 return CONCAT2(check_result_L, __LINE__).Code(); \
450 target = std::move(*CONCAT2(check_result_L, __LINE__)) 438 target = std::move(*CONCAT2(check_result_L, __LINE__))
439
440/**
441 * Analogous to CASCADE_RESULT, but for a bare ResultCode. The code will be propagated if
442 * non-success, or discarded otherwise.
443 */
444#define CASCADE_CODE(source) \
445 auto CONCAT2(check_result_L, __LINE__) = source; \
446 if (CONCAT2(check_result_L, __LINE__).IsError()) \
447 return CONCAT2(check_result_L, __LINE__);
diff --git a/src/core/hle/service/ac/ac.cpp b/src/core/hle/service/ac/ac.cpp
index aa270a2c3..e3dd23949 100644
--- a/src/core/hle/service/ac/ac.cpp
+++ b/src/core/hle/service/ac/ac.cpp
@@ -4,11 +4,16 @@
4 4
5#include <array> 5#include <array>
6 6
7#include "common/common_types.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/hle/ipc.h"
8#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/handle_table.h"
12#include "core/hle/result.h"
9#include "core/hle/service/ac/ac.h" 13#include "core/hle/service/ac/ac.h"
10#include "core/hle/service/ac/ac_i.h" 14#include "core/hle/service/ac/ac_i.h"
11#include "core/hle/service/ac/ac_u.h" 15#include "core/hle/service/ac/ac_u.h"
16#include "core/memory.h"
12 17
13namespace Service { 18namespace Service {
14namespace AC { 19namespace AC {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index d344a622f..961305e9f 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -2,8 +2,12 @@
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 <array>
5#include <cinttypes> 6#include <cinttypes>
7#include "common/common_types.h"
6#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/hle/ipc.h"
10#include "core/hle/result.h"
7#include "core/hle/service/am/am.h" 11#include "core/hle/service/am/am.h"
8#include "core/hle/service/am/am_app.h" 12#include "core/hle/service/am/am_app.h"
9#include "core/hle/service/am/am_net.h" 13#include "core/hle/service/am/am_net.h"
@@ -176,8 +180,6 @@ void GetTicketList(Service::Interface* self) {
176} 180}
177 181
178void Init() { 182void Init() {
179 using namespace Kernel;
180
181 AddService(new AM_APP_Interface); 183 AddService(new AM_APP_Interface);
182 AddService(new AM_NET_Interface); 184 AddService(new AM_NET_Interface);
183 AddService(new AM_SYS_Interface); 185 AddService(new AM_SYS_Interface);
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 366d1eacf..4c587e3c8 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -5,6 +5,7 @@
5#include "common/common_paths.h" 5#include "common/common_paths.h"
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h"
8#include "core/hle/applets/applet.h" 9#include "core/hle/applets/applet.h"
9#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/mutex.h" 11#include "core/hle/kernel/mutex.h"
@@ -74,6 +75,7 @@ void GetSharedFont(Service::Interface* self) {
74 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); 75 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
75 rb.Push<u32>(-1); // TODO: Find the right error code 76 rb.Push<u32>(-1); // TODO: Find the right error code
76 rb.Skip(1 + 2, true); 77 rb.Skip(1 + 2, true);
78 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont);
77 return; 79 return;
78 } 80 }
79 81
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index e63b61450..ee80926d2 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector>
8#include "common/common_funcs.h"
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "common/swap.h" 10#include "common/swap.h"
9#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp
index e0de037f8..2bba3aff6 100644
--- a/src/core/hle/service/boss/boss.cpp
+++ b/src/core/hle/service/boss/boss.cpp
@@ -3,6 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include "common/logging/log.h"
7#include "core/hle/ipc.h"
8#include "core/hle/result.h"
6#include "core/hle/service/boss/boss.h" 9#include "core/hle/service/boss/boss.h"
7#include "core/hle/service/boss/boss_p.h" 10#include "core/hle/service/boss/boss_p.h"
8#include "core/hle/service/boss/boss_u.h" 11#include "core/hle/service/boss/boss_u.h"
@@ -24,9 +27,7 @@ void InitializeSession(Service::Interface* self) {
24 27
25 if (translation != IPC::CallingPidDesc()) { 28 if (translation != IPC::CallingPidDesc()) {
26 cmd_buff[0] = IPC::MakeHeader(0, 0x1, 0); // 0x40 29 cmd_buff[0] = IPC::MakeHeader(0, 0x1, 0); // 0x40
27 cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, 30 cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw;
28 ErrorSummary::WrongArgument, ErrorLevel::Permanent)
29 .raw;
30 LOG_ERROR(Service_BOSS, "The translation was invalid, translation=0x%08X", translation); 31 LOG_ERROR(Service_BOSS, "The translation was invalid, translation=0x%08X", translation);
31 return; 32 return;
32 } 33 }
diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp
index 95665e754..7394c844f 100644
--- a/src/core/hle/service/cam/cam.cpp
+++ b/src/core/hle/service/cam/cam.cpp
@@ -11,13 +11,17 @@
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/core_timing.h" 12#include "core/core_timing.h"
13#include "core/frontend/camera/factory.h" 13#include "core/frontend/camera/factory.h"
14#include "core/hle/ipc.h"
15#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/event.h" 16#include "core/hle/kernel/event.h"
17#include "core/hle/result.h"
15#include "core/hle/service/cam/cam.h" 18#include "core/hle/service/cam/cam.h"
16#include "core/hle/service/cam/cam_c.h" 19#include "core/hle/service/cam/cam_c.h"
17#include "core/hle/service/cam/cam_q.h" 20#include "core/hle/service/cam/cam_q.h"
18#include "core/hle/service/cam/cam_s.h" 21#include "core/hle/service/cam/cam_s.h"
19#include "core/hle/service/cam/cam_u.h" 22#include "core/hle/service/cam/cam_u.h"
20#include "core/hle/service/service.h" 23#include "core/hle/service/service.h"
24#include "core/memory.h"
21#include "core/settings.h" 25#include "core/settings.h"
22 26
23namespace Service { 27namespace Service {
@@ -55,7 +59,7 @@ struct PortConfig {
55 u16 x1; // x-coordinate of ending position for trimming 59 u16 x1; // x-coordinate of ending position for trimming
56 u16 y1; // y-coordinate of ending position for trimming 60 u16 y1; // y-coordinate of ending position for trimming
57 61
58 u32 transfer_bytes; 62 u16 transfer_bytes;
59 63
60 Kernel::SharedPtr<Kernel::Event> completion_event; 64 Kernel::SharedPtr<Kernel::Event> completion_event;
61 Kernel::SharedPtr<Kernel::Event> buffer_error_interrupt_event; 65 Kernel::SharedPtr<Kernel::Event> buffer_error_interrupt_event;
@@ -225,8 +229,7 @@ static void ActivatePort(int port_id, int camera_id) {
225template <int max_index> 229template <int max_index>
226class CommandParamBitSet : public BitSet8 { 230class CommandParamBitSet : public BitSet8 {
227public: 231public:
228 explicit CommandParamBitSet(u32 command_param) 232 explicit CommandParamBitSet(u8 command_param) : BitSet8(command_param) {}
229 : BitSet8(static_cast<u8>(command_param & 0xFF)) {}
230 233
231 bool IsValid() const { 234 bool IsValid() const {
232 return m_val < (1 << max_index); 235 return m_val < (1 << max_index);
@@ -244,9 +247,10 @@ using CameraSet = CommandParamBitSet<3>;
244} // namespace 247} // namespace
245 248
246void StartCapture(Service::Interface* self) { 249void StartCapture(Service::Interface* self) {
247 u32* cmd_buff = Kernel::GetCommandBuffer(); 250 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 1, 0);
251 const PortSet port_select(rp.Pop<u8>());
248 252
249 const PortSet port_select(cmd_buff[1]); 253 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
250 254
251 if (port_select.IsValid()) { 255 if (port_select.IsValid()) {
252 for (int i : port_select) { 256 for (int i : port_select) {
@@ -267,21 +271,20 @@ void StartCapture(Service::Interface* self) {
267 LOG_WARNING(Service_CAM, "port %u already started", i); 271 LOG_WARNING(Service_CAM, "port %u already started", i);
268 } 272 }
269 } 273 }
270 cmd_buff[1] = RESULT_SUCCESS.raw; 274 rb.Push(RESULT_SUCCESS);
271 } else { 275 } else {
272 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 276 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
273 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 277 rb.Push(ERROR_INVALID_ENUM_VALUE);
274 } 278 }
275 279
276 cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
277
278 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); 280 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
279} 281}
280 282
281void StopCapture(Service::Interface* self) { 283void StopCapture(Service::Interface* self) {
282 u32* cmd_buff = Kernel::GetCommandBuffer(); 284 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x02, 1, 0);
285 const PortSet port_select(rp.Pop<u8>());
283 286
284 const PortSet port_select(cmd_buff[1]); 287 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
285 288
286 if (port_select.IsValid()) { 289 if (port_select.IsValid()) {
287 for (int i : port_select) { 290 for (int i : port_select) {
@@ -293,21 +296,20 @@ void StopCapture(Service::Interface* self) {
293 LOG_WARNING(Service_CAM, "port %u already stopped", i); 296 LOG_WARNING(Service_CAM, "port %u already stopped", i);
294 } 297 }
295 } 298 }
296 cmd_buff[1] = RESULT_SUCCESS.raw; 299 rb.Push(RESULT_SUCCESS);
297 } else { 300 } else {
298 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 301 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
299 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 302 rb.Push(ERROR_INVALID_ENUM_VALUE);
300 } 303 }
301 304
302 cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0);
303
304 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); 305 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
305} 306}
306 307
307void IsBusy(Service::Interface* self) { 308void IsBusy(Service::Interface* self) {
308 u32* cmd_buff = Kernel::GetCommandBuffer(); 309 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0);
310 const PortSet port_select(rp.Pop<u8>());
309 311
310 const PortSet port_select(cmd_buff[1]); 312 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
311 313
312 if (port_select.IsValid()) { 314 if (port_select.IsValid()) {
313 bool is_busy = true; 315 bool is_busy = true;
@@ -315,80 +317,74 @@ void IsBusy(Service::Interface* self) {
315 for (int i : port_select) { 317 for (int i : port_select) {
316 is_busy &= ports[i].is_busy; 318 is_busy &= ports[i].is_busy;
317 } 319 }
318 cmd_buff[1] = RESULT_SUCCESS.raw; 320 rb.Push(RESULT_SUCCESS);
319 cmd_buff[2] = is_busy ? 1 : 0; 321 rb.Push(is_busy);
320 } else { 322 } else {
321 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 323 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
322 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 324 rb.Push(ERROR_INVALID_ENUM_VALUE);
325 rb.Skip(1, false);
323 } 326 }
324 327
325 cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0);
326
327 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); 328 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
328} 329}
329 330
330void ClearBuffer(Service::Interface* self) { 331void ClearBuffer(Service::Interface* self) {
331 u32* cmd_buff = Kernel::GetCommandBuffer(); 332 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x04, 1, 0);
332 333 const PortSet port_select(rp.Pop<u8>());
333 const PortSet port_select(cmd_buff[1]);
334 334
335 cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0); 335 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
336 cmd_buff[1] = RESULT_SUCCESS.raw; 336 rb.Push(RESULT_SUCCESS);
337 337
338 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); 338 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val);
339} 339}
340 340
341void GetVsyncInterruptEvent(Service::Interface* self) { 341void GetVsyncInterruptEvent(Service::Interface* self) {
342 u32* cmd_buff = Kernel::GetCommandBuffer(); 342 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x05, 1, 0);
343 343 const PortSet port_select(rp.Pop<u8>());
344 const PortSet port_select(cmd_buff[1]);
345 344
345 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
346 if (port_select.IsSingle()) { 346 if (port_select.IsSingle()) {
347 int port = *port_select.begin(); 347 int port = *port_select.begin();
348 cmd_buff[1] = RESULT_SUCCESS.raw; 348 rb.Push(RESULT_SUCCESS);
349 cmd_buff[2] = IPC::CopyHandleDesc(); 349 rb.PushCopyHandles(
350 cmd_buff[3] = Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom(); 350 Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom());
351 } else { 351 } else {
352 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 352 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
353 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 353 rb.Push(ERROR_INVALID_ENUM_VALUE);
354 cmd_buff[2] = IPC::CopyHandleDesc(); 354 rb.PushCopyHandles(0);
355 cmd_buff[2] = 0;
356 } 355 }
357 356
358 cmd_buff[0] = IPC::MakeHeader(0x5, 1, 2);
359
360 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); 357 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val);
361} 358}
362 359
363void GetBufferErrorInterruptEvent(Service::Interface* self) { 360void GetBufferErrorInterruptEvent(Service::Interface* self) {
364 u32* cmd_buff = Kernel::GetCommandBuffer(); 361 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 0);
365 362 const PortSet port_select(rp.Pop<u8>());
366 const PortSet port_select(cmd_buff[1]);
367 363
364 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
368 if (port_select.IsSingle()) { 365 if (port_select.IsSingle()) {
369 int port = *port_select.begin(); 366 int port = *port_select.begin();
370 cmd_buff[1] = RESULT_SUCCESS.raw; 367 rb.Push(RESULT_SUCCESS);
371 cmd_buff[2] = IPC::CopyHandleDesc(); 368 rb.PushCopyHandles(
372 cmd_buff[3] = 369 Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom());
373 Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom();
374 } else { 370 } else {
375 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 371 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
376 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 372 rb.Push(ERROR_INVALID_ENUM_VALUE);
377 cmd_buff[2] = IPC::CopyHandleDesc(); 373 rb.PushCopyHandles(0);
378 cmd_buff[2] = 0;
379 } 374 }
380 375
381 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); 376 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val);
382} 377}
383 378
384void SetReceiving(Service::Interface* self) { 379void SetReceiving(Service::Interface* self) {
385 u32* cmd_buff = Kernel::GetCommandBuffer(); 380 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x07, 4, 2);
386 381 const VAddr dest = rp.Pop<u32>();
387 const VAddr dest = cmd_buff[1]; 382 const PortSet port_select(rp.Pop<u8>());
388 const PortSet port_select(cmd_buff[2]); 383 const u32 image_size = rp.Pop<u32>();
389 const u32 image_size = cmd_buff[3]; 384 const u16 trans_unit = rp.Pop<u16>();
390 const u32 trans_unit = cmd_buff[4] & 0xFFFF; 385 rp.PopHandle(); // Handle to destination process. not used
391 386
387 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
392 if (port_select.IsSingle()) { 388 if (port_select.IsSingle()) {
393 int port_id = *port_select.begin(); 389 int port_id = *port_select.begin();
394 PortConfig& port = ports[port_id]; 390 PortConfig& port = ports[port_id];
@@ -403,149 +399,145 @@ void SetReceiving(Service::Interface* self) {
403 port.is_pending_receiving = true; 399 port.is_pending_receiving = true;
404 } 400 }
405 401
406 cmd_buff[1] = RESULT_SUCCESS.raw; 402 rb.Push(RESULT_SUCCESS);
407 cmd_buff[2] = IPC::CopyHandleDesc(); 403 rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).MoveFrom());
408 cmd_buff[3] = Kernel::g_handle_table.Create(port.completion_event).MoveFrom();
409 } else { 404 } else {
410 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 405 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
411 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 406 rb.Push(ERROR_INVALID_ENUM_VALUE);
407 rb.PushCopyHandles(0);
412 } 408 }
413 409
414 cmd_buff[0] = IPC::MakeHeader(0x7, 1, 2);
415
416 LOG_DEBUG(Service_CAM, "called, addr=0x%X, port_select=%u, image_size=%u, trans_unit=%u", dest, 410 LOG_DEBUG(Service_CAM, "called, addr=0x%X, port_select=%u, image_size=%u, trans_unit=%u", dest,
417 port_select.m_val, image_size, trans_unit); 411 port_select.m_val, image_size, trans_unit);
418} 412}
419 413
420void IsFinishedReceiving(Service::Interface* self) { 414void IsFinishedReceiving(Service::Interface* self) {
421 u32* cmd_buff = Kernel::GetCommandBuffer(); 415 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 1, 0);
422 416 const PortSet port_select(rp.Pop<u8>());
423 const PortSet port_select(cmd_buff[1]);
424 417
418 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
425 if (port_select.IsSingle()) { 419 if (port_select.IsSingle()) {
426 int port = *port_select.begin(); 420 int port = *port_select.begin();
427 cmd_buff[1] = RESULT_SUCCESS.raw; 421 bool is_busy = ports[port].is_receiving || ports[port].is_pending_receiving;
428 cmd_buff[2] = (ports[port].is_receiving || ports[port].is_pending_receiving) ? 0 : 1; 422 rb.Push(RESULT_SUCCESS);
423 rb.Push(!is_busy);
429 } else { 424 } else {
430 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 425 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
431 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 426 rb.Push(ERROR_INVALID_ENUM_VALUE);
427 rb.Skip(1, false);
432 } 428 }
433 429
434 cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0);
435
436 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); 430 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
437} 431}
438 432
439void SetTransferLines(Service::Interface* self) { 433void SetTransferLines(Service::Interface* self) {
440 u32* cmd_buff = Kernel::GetCommandBuffer(); 434 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x09, 4, 0);
441 435 const PortSet port_select(rp.Pop<u8>());
442 const PortSet port_select(cmd_buff[1]); 436 const u16 transfer_lines = rp.Pop<u16>();
443 const u32 transfer_lines = cmd_buff[2] & 0xFFFF; 437 const u16 width = rp.Pop<u16>();
444 const u32 width = cmd_buff[3] & 0xFFFF; 438 const u16 height = rp.Pop<u16>();
445 const u32 height = cmd_buff[4] & 0xFFFF;
446 439
440 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
447 if (port_select.IsValid()) { 441 if (port_select.IsValid()) {
448 for (int i : port_select) { 442 for (int i : port_select) {
449 ports[i].transfer_bytes = transfer_lines * width * 2; 443 ports[i].transfer_bytes = transfer_lines * width * 2;
450 } 444 }
451 cmd_buff[1] = RESULT_SUCCESS.raw; 445 rb.Push(RESULT_SUCCESS);
452 } else { 446 } else {
453 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 447 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
454 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 448 rb.Push(ERROR_INVALID_ENUM_VALUE);
455 } 449 }
456 450
457 cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0);
458
459 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u, lines=%u, width=%u, height=%u", 451 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u, lines=%u, width=%u, height=%u",
460 port_select.m_val, transfer_lines, width, height); 452 port_select.m_val, transfer_lines, width, height);
461} 453}
462 454
463void GetMaxLines(Service::Interface* self) { 455void GetMaxLines(Service::Interface* self) {
464 u32* cmd_buff = Kernel::GetCommandBuffer(); 456 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0A, 2, 0);
457 const u16 width = rp.Pop<u16>();
458 const u16 height = rp.Pop<u16>();
465 459
466 const u32 width = cmd_buff[1] & 0xFFFF; 460 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
467 const u32 height = cmd_buff[2] & 0xFFFF;
468 461
469 // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480 462 // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480
470 constexpr u32 MIN_TRANSFER_UNIT = 256; 463 constexpr u32 MIN_TRANSFER_UNIT = 256;
471 constexpr u32 MAX_BUFFER_SIZE = 2560; 464 constexpr u32 MAX_BUFFER_SIZE = 2560;
472 if (width * height * 2 % MIN_TRANSFER_UNIT != 0) { 465 if (width * height * 2 % MIN_TRANSFER_UNIT != 0) {
473 cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; 466 rb.Push(ERROR_OUT_OF_RANGE);
467 rb.Skip(1, false);
474 } else { 468 } else {
475 u32 lines = MAX_BUFFER_SIZE / width; 469 u32 lines = MAX_BUFFER_SIZE / width;
476 if (lines > height) { 470 if (lines > height) {
477 lines = height; 471 lines = height;
478 } 472 }
479 cmd_buff[1] = RESULT_SUCCESS.raw; 473 ResultCode result = RESULT_SUCCESS;
480 while (height % lines != 0 || (lines * width * 2 % MIN_TRANSFER_UNIT != 0)) { 474 while (height % lines != 0 || (lines * width * 2 % MIN_TRANSFER_UNIT != 0)) {
481 --lines; 475 --lines;
482 if (lines == 0) { 476 if (lines == 0) {
483 cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; 477 result = ERROR_OUT_OF_RANGE;
484 break; 478 break;
485 } 479 }
486 } 480 }
487 cmd_buff[2] = lines; 481 rb.Push(result);
482 rb.Push(lines);
488 } 483 }
489 484
490 cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0);
491
492 LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height); 485 LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height);
493} 486}
494 487
495void SetTransferBytes(Service::Interface* self) { 488void SetTransferBytes(Service::Interface* self) {
496 u32* cmd_buff = Kernel::GetCommandBuffer(); 489 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0B, 4, 0);
497 490 const PortSet port_select(rp.Pop<u8>());
498 const PortSet port_select(cmd_buff[1]); 491 const u16 transfer_bytes = rp.Pop<u16>();
499 const u32 transfer_bytes = cmd_buff[2] & 0xFFFF; 492 const u16 width = rp.Pop<u16>();
500 const u32 width = cmd_buff[3] & 0xFFFF; 493 const u16 height = rp.Pop<u16>();
501 const u32 height = cmd_buff[4] & 0xFFFF;
502 494
495 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
503 if (port_select.IsValid()) { 496 if (port_select.IsValid()) {
504 for (int i : port_select) { 497 for (int i : port_select) {
505 ports[i].transfer_bytes = transfer_bytes; 498 ports[i].transfer_bytes = transfer_bytes;
506 } 499 }
507 cmd_buff[1] = RESULT_SUCCESS.raw; 500 rb.Push(RESULT_SUCCESS);
508 } else { 501 } else {
509 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 502 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
510 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 503 rb.Push(ERROR_INVALID_ENUM_VALUE);
511 } 504 }
512 505
513 cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0);
514
515 LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u, bytes=%u, width=%u, height=%u", 506 LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u, bytes=%u, width=%u, height=%u",
516 port_select.m_val, transfer_bytes, width, height); 507 port_select.m_val, transfer_bytes, width, height);
517} 508}
518 509
519void GetTransferBytes(Service::Interface* self) { 510void GetTransferBytes(Service::Interface* self) {
520 u32* cmd_buff = Kernel::GetCommandBuffer(); 511 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0C, 1, 0);
521 512 const PortSet port_select(rp.Pop<u8>());
522 const PortSet port_select(cmd_buff[1]);
523 513
514 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
524 if (port_select.IsSingle()) { 515 if (port_select.IsSingle()) {
525 int port = *port_select.begin(); 516 int port = *port_select.begin();
526 cmd_buff[1] = RESULT_SUCCESS.raw; 517 rb.Push(RESULT_SUCCESS);
527 cmd_buff[2] = ports[port].transfer_bytes; 518 rb.Push(ports[port].transfer_bytes);
528 } else { 519 } else {
529 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 520 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
530 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 521 rb.Push(ERROR_INVALID_ENUM_VALUE);
522 rb.Skip(1, false);
531 } 523 }
532 524
533 cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0);
534
535 LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u", port_select.m_val); 525 LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u", port_select.m_val);
536} 526}
537 527
538void GetMaxBytes(Service::Interface* self) { 528void GetMaxBytes(Service::Interface* self) {
539 u32* cmd_buff = Kernel::GetCommandBuffer(); 529 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0D, 2, 0);
530 const u16 width = rp.Pop<u16>();
531 const u16 height = rp.Pop<u16>();
540 532
541 const u32 width = cmd_buff[1] & 0xFFFF; 533 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
542 const u32 height = cmd_buff[2] & 0xFFFF;
543 534
544 // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480 535 // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480
545 constexpr u32 MIN_TRANSFER_UNIT = 256; 536 constexpr u32 MIN_TRANSFER_UNIT = 256;
546 constexpr u32 MAX_BUFFER_SIZE = 2560; 537 constexpr u32 MAX_BUFFER_SIZE = 2560;
547 if (width * height * 2 % MIN_TRANSFER_UNIT != 0) { 538 if (width * height * 2 % MIN_TRANSFER_UNIT != 0) {
548 cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; 539 rb.Push(ERROR_OUT_OF_RANGE);
540 rb.Skip(1, false);
549 } else { 541 } else {
550 u32 bytes = MAX_BUFFER_SIZE; 542 u32 bytes = MAX_BUFFER_SIZE;
551 543
@@ -553,63 +545,59 @@ void GetMaxBytes(Service::Interface* self) {
553 bytes -= MIN_TRANSFER_UNIT; 545 bytes -= MIN_TRANSFER_UNIT;
554 } 546 }
555 547
556 cmd_buff[1] = RESULT_SUCCESS.raw; 548 rb.Push(RESULT_SUCCESS);
557 cmd_buff[2] = bytes; 549 rb.Push(bytes);
558 } 550 }
559 cmd_buff[0] = IPC::MakeHeader(0xD, 2, 0);
560 551
561 LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height); 552 LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height);
562} 553}
563 554
564void SetTrimming(Service::Interface* self) { 555void SetTrimming(Service::Interface* self) {
565 u32* cmd_buff = Kernel::GetCommandBuffer(); 556 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0E, 2, 0);
566 557 const PortSet port_select(rp.Pop<u8>());
567 const PortSet port_select(cmd_buff[1]); 558 const bool trim = rp.Pop<bool>();
568 const bool trim = (cmd_buff[2] & 0xFF) != 0;
569 559
560 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
570 if (port_select.IsValid()) { 561 if (port_select.IsValid()) {
571 for (int i : port_select) { 562 for (int i : port_select) {
572 ports[i].is_trimming = trim; 563 ports[i].is_trimming = trim;
573 } 564 }
574 cmd_buff[1] = RESULT_SUCCESS.raw; 565 rb.Push(RESULT_SUCCESS);
575 } else { 566 } else {
576 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 567 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
577 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 568 rb.Push(ERROR_INVALID_ENUM_VALUE);
578 } 569 }
579 570
580 cmd_buff[0] = IPC::MakeHeader(0xE, 1, 0);
581
582 LOG_DEBUG(Service_CAM, "called, port_select=%u, trim=%d", port_select.m_val, trim); 571 LOG_DEBUG(Service_CAM, "called, port_select=%u, trim=%d", port_select.m_val, trim);
583} 572}
584 573
585void IsTrimming(Service::Interface* self) { 574void IsTrimming(Service::Interface* self) {
586 u32* cmd_buff = Kernel::GetCommandBuffer(); 575 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0F, 1, 0);
587 576 const PortSet port_select(rp.Pop<u8>());
588 const PortSet port_select(cmd_buff[1]);
589 577
578 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
590 if (port_select.IsSingle()) { 579 if (port_select.IsSingle()) {
591 int port = *port_select.begin(); 580 int port = *port_select.begin();
592 cmd_buff[1] = RESULT_SUCCESS.raw; 581 rb.Push(RESULT_SUCCESS);
593 cmd_buff[2] = ports[port].is_trimming; 582 rb.Push(ports[port].is_trimming);
594 } else { 583 } else {
595 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 584 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
596 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 585 rb.Push(ERROR_INVALID_ENUM_VALUE);
586 rb.Skip(1, false);
597 } 587 }
598 588
599 cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0);
600
601 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); 589 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
602} 590}
603 591
604void SetTrimmingParams(Service::Interface* self) { 592void SetTrimmingParams(Service::Interface* self) {
605 u32* cmd_buff = Kernel::GetCommandBuffer(); 593 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x10, 5, 0);
606 594 const PortSet port_select(rp.Pop<u8>());
607 const PortSet port_select(cmd_buff[1]); 595 const u16 x0 = rp.Pop<u16>();
608 const u16 x0 = static_cast<u16>(cmd_buff[2] & 0xFFFF); 596 const u16 y0 = rp.Pop<u16>();
609 const u16 y0 = static_cast<u16>(cmd_buff[3] & 0xFFFF); 597 const u16 x1 = rp.Pop<u16>();
610 const u16 x1 = static_cast<u16>(cmd_buff[4] & 0xFFFF); 598 const u16 y1 = rp.Pop<u16>();
611 const u16 y1 = static_cast<u16>(cmd_buff[5] & 0xFFFF); 599
612 600 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
613 if (port_select.IsValid()) { 601 if (port_select.IsValid()) {
614 for (int i : port_select) { 602 for (int i : port_select) {
615 ports[i].x0 = x0; 603 ports[i].x0 = x0;
@@ -617,49 +605,46 @@ void SetTrimmingParams(Service::Interface* self) {
617 ports[i].x1 = x1; 605 ports[i].x1 = x1;
618 ports[i].y1 = y1; 606 ports[i].y1 = y1;
619 } 607 }
620 cmd_buff[1] = RESULT_SUCCESS.raw; 608 rb.Push(RESULT_SUCCESS);
621 } else { 609 } else {
622 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 610 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
623 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 611 rb.Push(ERROR_INVALID_ENUM_VALUE);
624 } 612 }
625 613
626 cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0);
627
628 LOG_DEBUG(Service_CAM, "called, port_select=%u, x0=%u, y0=%u, x1=%u, y1=%u", port_select.m_val, 614 LOG_DEBUG(Service_CAM, "called, port_select=%u, x0=%u, y0=%u, x1=%u, y1=%u", port_select.m_val,
629 x0, y0, x1, y1); 615 x0, y0, x1, y1);
630} 616}
631 617
632void GetTrimmingParams(Service::Interface* self) { 618void GetTrimmingParams(Service::Interface* self) {
633 u32* cmd_buff = Kernel::GetCommandBuffer(); 619 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x11, 1, 0);
634 620 const PortSet port_select(rp.Pop<u8>());
635 const PortSet port_select(cmd_buff[1]);
636 621
622 IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
637 if (port_select.IsSingle()) { 623 if (port_select.IsSingle()) {
638 int port = *port_select.begin(); 624 int port = *port_select.begin();
639 cmd_buff[1] = RESULT_SUCCESS.raw; 625 rb.Push(RESULT_SUCCESS);
640 cmd_buff[2] = ports[port].x0; 626 rb.Push(ports[port].x0);
641 cmd_buff[3] = ports[port].y0; 627 rb.Push(ports[port].y0);
642 cmd_buff[4] = ports[port].x1; 628 rb.Push(ports[port].x1);
643 cmd_buff[5] = ports[port].y1; 629 rb.Push(ports[port].y1);
644 } else { 630 } else {
645 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 631 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
646 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 632 rb.Push(ERROR_INVALID_ENUM_VALUE);
633 rb.Skip(4, false);
647 } 634 }
648 635
649 cmd_buff[0] = IPC::MakeHeader(0x11, 5, 0);
650
651 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); 636 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
652} 637}
653 638
654void SetTrimmingParamsCenter(Service::Interface* self) { 639void SetTrimmingParamsCenter(Service::Interface* self) {
655 u32* cmd_buff = Kernel::GetCommandBuffer(); 640 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 5, 0);
656 641 const PortSet port_select(rp.Pop<u8>());
657 const PortSet port_select(cmd_buff[1]); 642 const u16 trim_w = rp.Pop<u16>();
658 const u16 trim_w = static_cast<u16>(cmd_buff[2] & 0xFFFF); 643 const u16 trim_h = rp.Pop<u16>();
659 const u16 trim_h = static_cast<u16>(cmd_buff[3] & 0xFFFF); 644 const u16 cam_w = rp.Pop<u16>();
660 const u16 cam_w = static_cast<u16>(cmd_buff[4] & 0xFFFF); 645 const u16 cam_h = rp.Pop<u16>();
661 const u16 cam_h = static_cast<u16>(cmd_buff[5] & 0xFFFF); 646
662 647 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
663 if (port_select.IsValid()) { 648 if (port_select.IsValid()) {
664 for (int i : port_select) { 649 for (int i : port_select) {
665 ports[i].x0 = (cam_w - trim_w) / 2; 650 ports[i].x0 = (cam_w - trim_w) / 2;
@@ -667,23 +652,21 @@ void SetTrimmingParamsCenter(Service::Interface* self) {
667 ports[i].x1 = ports[i].x0 + trim_w; 652 ports[i].x1 = ports[i].x0 + trim_w;
668 ports[i].y1 = ports[i].y0 + trim_h; 653 ports[i].y1 = ports[i].y0 + trim_h;
669 } 654 }
670 cmd_buff[1] = RESULT_SUCCESS.raw; 655 rb.Push(RESULT_SUCCESS);
671 } else { 656 } else {
672 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); 657 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
673 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 658 rb.Push(ERROR_INVALID_ENUM_VALUE);
674 } 659 }
675 660
676 cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0);
677
678 LOG_DEBUG(Service_CAM, "called, port_select=%u, trim_w=%u, trim_h=%u, cam_w=%u, cam_h=%u", 661 LOG_DEBUG(Service_CAM, "called, port_select=%u, trim_w=%u, trim_h=%u, cam_w=%u, cam_h=%u",
679 port_select.m_val, trim_w, trim_h, cam_w, cam_h); 662 port_select.m_val, trim_w, trim_h, cam_w, cam_h);
680} 663}
681 664
682void Activate(Service::Interface* self) { 665void Activate(Service::Interface* self) {
683 u32* cmd_buff = Kernel::GetCommandBuffer(); 666 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x13, 1, 0);
684 667 const CameraSet camera_select(rp.Pop<u8>());
685 const CameraSet camera_select(cmd_buff[1]);
686 668
669 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
687 if (camera_select.IsValid()) { 670 if (camera_select.IsValid()) {
688 if (camera_select.m_val == 0) { // deactive all 671 if (camera_select.m_val == 0) { // deactive all
689 for (int i = 0; i < 2; ++i) { 672 for (int i = 0; i < 2; ++i) {
@@ -694,10 +677,10 @@ void Activate(Service::Interface* self) {
694 } 677 }
695 ports[i].is_active = false; 678 ports[i].is_active = false;
696 } 679 }
697 cmd_buff[1] = RESULT_SUCCESS.raw; 680 rb.Push(RESULT_SUCCESS);
698 } else if (camera_select[0] && camera_select[1]) { 681 } else if (camera_select[0] && camera_select[1]) {
699 LOG_ERROR(Service_CAM, "camera 0 and 1 can't be both activated"); 682 LOG_ERROR(Service_CAM, "camera 0 and 1 can't be both activated");
700 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 683 rb.Push(ERROR_INVALID_ENUM_VALUE);
701 } else { 684 } else {
702 if (camera_select[0]) { 685 if (camera_select[0]) {
703 ActivatePort(0, 0); 686 ActivatePort(0, 0);
@@ -708,24 +691,22 @@ void Activate(Service::Interface* self) {
708 if (camera_select[2]) { 691 if (camera_select[2]) {
709 ActivatePort(1, 2); 692 ActivatePort(1, 2);
710 } 693 }
711 cmd_buff[1] = RESULT_SUCCESS.raw; 694 rb.Push(RESULT_SUCCESS);
712 } 695 }
713 } else { 696 } else {
714 LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val); 697 LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val);
715 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 698 rb.Push(ERROR_INVALID_ENUM_VALUE);
716 } 699 }
717 700
718 cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0);
719
720 LOG_DEBUG(Service_CAM, "called, camera_select=%u", camera_select.m_val); 701 LOG_DEBUG(Service_CAM, "called, camera_select=%u", camera_select.m_val);
721} 702}
722 703
723void SwitchContext(Service::Interface* self) { 704void SwitchContext(Service::Interface* self) {
724 u32* cmd_buff = Kernel::GetCommandBuffer(); 705 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x14, 2, 0);
725 706 const CameraSet camera_select(rp.Pop<u8>());
726 const CameraSet camera_select(cmd_buff[1]); 707 const ContextSet context_select(rp.Pop<u8>());
727 const ContextSet context_select(cmd_buff[2]);
728 708
709 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
729 if (camera_select.IsValid() && context_select.IsSingle()) { 710 if (camera_select.IsValid() && context_select.IsSingle()) {
730 int context = *context_select.begin(); 711 int context = *context_select.begin();
731 for (int camera : camera_select) { 712 for (int camera : camera_select) {
@@ -736,26 +717,24 @@ void SwitchContext(Service::Interface* self) {
736 cameras[camera].impl->SetFormat(context_config.format); 717 cameras[camera].impl->SetFormat(context_config.format);
737 cameras[camera].impl->SetResolution(context_config.resolution); 718 cameras[camera].impl->SetResolution(context_config.resolution);
738 } 719 }
739 cmd_buff[1] = RESULT_SUCCESS.raw; 720 rb.Push(RESULT_SUCCESS);
740 } else { 721 } else {
741 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, 722 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
742 context_select.m_val); 723 context_select.m_val);
743 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 724 rb.Push(ERROR_INVALID_ENUM_VALUE);
744 } 725 }
745 726
746 cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0);
747
748 LOG_DEBUG(Service_CAM, "called, camera_select=%u, context_select=%u", camera_select.m_val, 727 LOG_DEBUG(Service_CAM, "called, camera_select=%u, context_select=%u", camera_select.m_val,
749 context_select.m_val); 728 context_select.m_val);
750} 729}
751 730
752void FlipImage(Service::Interface* self) { 731void FlipImage(Service::Interface* self) {
753 u32* cmd_buff = Kernel::GetCommandBuffer(); 732 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 3, 0);
754 733 const CameraSet camera_select(rp.Pop<u8>());
755 const CameraSet camera_select(cmd_buff[1]); 734 const Flip flip = static_cast<Flip>(rp.Pop<u8>());
756 const Flip flip = static_cast<Flip>(cmd_buff[2] & 0xFF); 735 const ContextSet context_select(rp.Pop<u8>());
757 const ContextSet context_select(cmd_buff[3]);
758 736
737 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
759 if (camera_select.IsValid() && context_select.IsValid()) { 738 if (camera_select.IsValid() && context_select.IsValid()) {
760 for (int camera : camera_select) { 739 for (int camera : camera_select) {
761 for (int context : context_select) { 740 for (int context : context_select) {
@@ -765,32 +744,30 @@ void FlipImage(Service::Interface* self) {
765 } 744 }
766 } 745 }
767 } 746 }
768 cmd_buff[1] = RESULT_SUCCESS.raw; 747 rb.Push(RESULT_SUCCESS);
769 } else { 748 } else {
770 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, 749 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
771 context_select.m_val); 750 context_select.m_val);
772 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 751 rb.Push(ERROR_INVALID_ENUM_VALUE);
773 } 752 }
774 753
775 cmd_buff[0] = IPC::MakeHeader(0x1D, 1, 0);
776
777 LOG_DEBUG(Service_CAM, "called, camera_select=%u, flip=%d, context_select=%u", 754 LOG_DEBUG(Service_CAM, "called, camera_select=%u, flip=%d, context_select=%u",
778 camera_select.m_val, static_cast<int>(flip), context_select.m_val); 755 camera_select.m_val, static_cast<int>(flip), context_select.m_val);
779} 756}
780 757
781void SetDetailSize(Service::Interface* self) { 758void SetDetailSize(Service::Interface* self) {
782 u32* cmd_buff = Kernel::GetCommandBuffer(); 759 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 8, 0);
783 760 const CameraSet camera_select(rp.Pop<u8>());
784 const CameraSet camera_select(cmd_buff[1]);
785 Resolution resolution; 761 Resolution resolution;
786 resolution.width = static_cast<u16>(cmd_buff[2] & 0xFFFF); 762 resolution.width = rp.Pop<u16>();
787 resolution.height = static_cast<u16>(cmd_buff[3] & 0xFFFF); 763 resolution.height = rp.Pop<u16>();
788 resolution.crop_x0 = static_cast<u16>(cmd_buff[4] & 0xFFFF); 764 resolution.crop_x0 = rp.Pop<u16>();
789 resolution.crop_y0 = static_cast<u16>(cmd_buff[5] & 0xFFFF); 765 resolution.crop_y0 = rp.Pop<u16>();
790 resolution.crop_x1 = static_cast<u16>(cmd_buff[6] & 0xFFFF); 766 resolution.crop_x1 = rp.Pop<u16>();
791 resolution.crop_y1 = static_cast<u16>(cmd_buff[7] & 0xFFFF); 767 resolution.crop_y1 = rp.Pop<u16>();
792 const ContextSet context_select(cmd_buff[8]); 768 const ContextSet context_select(rp.Pop<u8>());
793 769
770 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
794 if (camera_select.IsValid() && context_select.IsValid()) { 771 if (camera_select.IsValid() && context_select.IsValid()) {
795 for (int camera : camera_select) { 772 for (int camera : camera_select) {
796 for (int context : context_select) { 773 for (int context : context_select) {
@@ -800,15 +777,13 @@ void SetDetailSize(Service::Interface* self) {
800 } 777 }
801 } 778 }
802 } 779 }
803 cmd_buff[1] = RESULT_SUCCESS.raw; 780 rb.Push(RESULT_SUCCESS);
804 } else { 781 } else {
805 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, 782 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
806 context_select.m_val); 783 context_select.m_val);
807 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 784 rb.Push(ERROR_INVALID_ENUM_VALUE);
808 } 785 }
809 786
810 cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0);
811
812 LOG_DEBUG(Service_CAM, "called, camera_select=%u, width=%u, height=%u, crop_x0=%u, crop_y0=%u, " 787 LOG_DEBUG(Service_CAM, "called, camera_select=%u, width=%u, height=%u, crop_x0=%u, crop_y0=%u, "
813 "crop_x1=%u, crop_y1=%u, context_select=%u", 788 "crop_x1=%u, crop_y1=%u, context_select=%u",
814 camera_select.m_val, resolution.width, resolution.height, resolution.crop_x0, 789 camera_select.m_val, resolution.width, resolution.height, resolution.crop_x0,
@@ -816,12 +791,12 @@ void SetDetailSize(Service::Interface* self) {
816} 791}
817 792
818void SetSize(Service::Interface* self) { 793void SetSize(Service::Interface* self) {
819 u32* cmd_buff = Kernel::GetCommandBuffer(); 794 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1F, 3, 0);
820 795 const CameraSet camera_select(rp.Pop<u8>());
821 const CameraSet camera_select(cmd_buff[1]); 796 const u8 size = rp.Pop<u8>();
822 const u32 size = cmd_buff[2] & 0xFF; 797 const ContextSet context_select(rp.Pop<u8>());
823 const ContextSet context_select(cmd_buff[3]);
824 798
799 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
825 if (camera_select.IsValid() && context_select.IsValid()) { 800 if (camera_select.IsValid() && context_select.IsValid()) {
826 for (int camera : camera_select) { 801 for (int camera : camera_select) {
827 for (int context : context_select) { 802 for (int context : context_select) {
@@ -831,49 +806,45 @@ void SetSize(Service::Interface* self) {
831 } 806 }
832 } 807 }
833 } 808 }
834 cmd_buff[1] = RESULT_SUCCESS.raw; 809 rb.Push(RESULT_SUCCESS);
835 } else { 810 } else {
836 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, 811 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
837 context_select.m_val); 812 context_select.m_val);
838 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 813 rb.Push(ERROR_INVALID_ENUM_VALUE);
839 } 814 }
840 815
841 cmd_buff[0] = IPC::MakeHeader(0x1F, 1, 0);
842
843 LOG_DEBUG(Service_CAM, "called, camera_select=%u, size=%u, context_select=%u", 816 LOG_DEBUG(Service_CAM, "called, camera_select=%u, size=%u, context_select=%u",
844 camera_select.m_val, size, context_select.m_val); 817 camera_select.m_val, size, context_select.m_val);
845} 818}
846 819
847void SetFrameRate(Service::Interface* self) { 820void SetFrameRate(Service::Interface* self) {
848 u32* cmd_buff = Kernel::GetCommandBuffer(); 821 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x20, 2, 0);
849 822 const CameraSet camera_select(rp.Pop<u8>());
850 const CameraSet camera_select(cmd_buff[1]); 823 const FrameRate frame_rate = static_cast<FrameRate>(rp.Pop<u8>());
851 const FrameRate frame_rate = static_cast<FrameRate>(cmd_buff[2] & 0xFF);
852 824
825 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
853 if (camera_select.IsValid()) { 826 if (camera_select.IsValid()) {
854 for (int camera : camera_select) { 827 for (int camera : camera_select) {
855 cameras[camera].frame_rate = frame_rate; 828 cameras[camera].frame_rate = frame_rate;
856 // TODO(wwylele): consider hinting the actual camera with the expected frame rate 829 // TODO(wwylele): consider hinting the actual camera with the expected frame rate
857 } 830 }
858 cmd_buff[1] = RESULT_SUCCESS.raw; 831 rb.Push(RESULT_SUCCESS);
859 } else { 832 } else {
860 LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val); 833 LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val);
861 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 834 rb.Push(ERROR_INVALID_ENUM_VALUE);
862 } 835 }
863 836
864 cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0);
865
866 LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select=%u, frame_rate=%d", 837 LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select=%u, frame_rate=%d",
867 camera_select.m_val, static_cast<int>(frame_rate)); 838 camera_select.m_val, static_cast<int>(frame_rate));
868} 839}
869 840
870void SetEffect(Service::Interface* self) { 841void SetEffect(Service::Interface* self) {
871 u32* cmd_buff = Kernel::GetCommandBuffer(); 842 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x22, 3, 0);
872 843 const CameraSet camera_select(rp.Pop<u8>());
873 const CameraSet camera_select(cmd_buff[1]); 844 const Effect effect = static_cast<Effect>(rp.Pop<u8>());
874 const Effect effect = static_cast<Effect>(cmd_buff[2] & 0xFF); 845 const ContextSet context_select(rp.Pop<u8>());
875 const ContextSet context_select(cmd_buff[3]);
876 846
847 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
877 if (camera_select.IsValid() && context_select.IsValid()) { 848 if (camera_select.IsValid() && context_select.IsValid()) {
878 for (int camera : camera_select) { 849 for (int camera : camera_select) {
879 for (int context : context_select) { 850 for (int context : context_select) {
@@ -883,26 +854,24 @@ void SetEffect(Service::Interface* self) {
883 } 854 }
884 } 855 }
885 } 856 }
886 cmd_buff[1] = RESULT_SUCCESS.raw; 857 rb.Push(RESULT_SUCCESS);
887 } else { 858 } else {
888 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, 859 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
889 context_select.m_val); 860 context_select.m_val);
890 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 861 rb.Push(ERROR_INVALID_ENUM_VALUE);
891 } 862 }
892 863
893 cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0);
894
895 LOG_DEBUG(Service_CAM, "called, camera_select=%u, effect=%d, context_select=%u", 864 LOG_DEBUG(Service_CAM, "called, camera_select=%u, effect=%d, context_select=%u",
896 camera_select.m_val, static_cast<int>(effect), context_select.m_val); 865 camera_select.m_val, static_cast<int>(effect), context_select.m_val);
897} 866}
898 867
899void SetOutputFormat(Service::Interface* self) { 868void SetOutputFormat(Service::Interface* self) {
900 u32* cmd_buff = Kernel::GetCommandBuffer(); 869 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x25, 3, 0);
901 870 const CameraSet camera_select(rp.Pop<u8>());
902 const CameraSet camera_select(cmd_buff[1]); 871 const OutputFormat format = static_cast<OutputFormat>(rp.Pop<u8>());
903 const OutputFormat format = static_cast<OutputFormat>(cmd_buff[2] & 0xFF); 872 const ContextSet context_select(rp.Pop<u8>());
904 const ContextSet context_select(cmd_buff[3]);
905 873
874 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
906 if (camera_select.IsValid() && context_select.IsValid()) { 875 if (camera_select.IsValid() && context_select.IsValid()) {
907 for (int camera : camera_select) { 876 for (int camera : camera_select) {
908 for (int context : context_select) { 877 for (int context : context_select) {
@@ -912,34 +881,32 @@ void SetOutputFormat(Service::Interface* self) {
912 } 881 }
913 } 882 }
914 } 883 }
915 cmd_buff[1] = RESULT_SUCCESS.raw; 884 rb.Push(RESULT_SUCCESS);
916 } else { 885 } else {
917 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, 886 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
918 context_select.m_val); 887 context_select.m_val);
919 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 888 rb.Push(ERROR_INVALID_ENUM_VALUE);
920 } 889 }
921 890
922 cmd_buff[0] = IPC::MakeHeader(0x25, 1, 0);
923
924 LOG_DEBUG(Service_CAM, "called, camera_select=%u, format=%d, context_select=%u", 891 LOG_DEBUG(Service_CAM, "called, camera_select=%u, format=%d, context_select=%u",
925 camera_select.m_val, static_cast<int>(format), context_select.m_val); 892 camera_select.m_val, static_cast<int>(format), context_select.m_val);
926} 893}
927 894
928void SynchronizeVsyncTiming(Service::Interface* self) { 895void SynchronizeVsyncTiming(Service::Interface* self) {
929 u32* cmd_buff = Kernel::GetCommandBuffer(); 896 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x29, 2, 0);
897 const u8 camera_select1 = rp.Pop<u8>();
898 const u8 camera_select2 = rp.Pop<u8>();
930 899
931 const u32 camera_select1 = cmd_buff[1] & 0xFF; 900 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
932 const u32 camera_select2 = cmd_buff[2] & 0xFF; 901 rb.Push(RESULT_SUCCESS);
933
934 cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0);
935 cmd_buff[1] = RESULT_SUCCESS.raw;
936 902
937 LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select1=%u, camera_select2=%u", 903 LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select1=%u, camera_select2=%u",
938 camera_select1, camera_select2); 904 camera_select1, camera_select2);
939} 905}
940 906
941void GetStereoCameraCalibrationData(Service::Interface* self) { 907void GetStereoCameraCalibrationData(Service::Interface* self) {
942 u32* cmd_buff = Kernel::GetCommandBuffer(); 908 IPC::RequestBuilder rb =
909 IPC::RequestParser(Kernel::GetCommandBuffer(), 0x2B, 0, 0).MakeBuilder(17, 0);
943 910
944 // Default values taken from yuriks' 3DS. Valid data is required here or games using the 911 // Default values taken from yuriks' 3DS. Valid data is required here or games using the
945 // calibration get stuck in an infinite CPU loop. 912 // calibration get stuck in an infinite CPU loop.
@@ -958,34 +925,28 @@ void GetStereoCameraCalibrationData(Service::Interface* self) {
958 data.imageWidth = 640; 925 data.imageWidth = 640;
959 data.imageHeight = 480; 926 data.imageHeight = 480;
960 927
961 cmd_buff[0] = IPC::MakeHeader(0x2B, 17, 0); 928 rb.Push(RESULT_SUCCESS);
962 cmd_buff[1] = RESULT_SUCCESS.raw; 929 rb.PushRaw(data);
963 memcpy(&cmd_buff[2], &data, sizeof(data));
964 930
965 LOG_TRACE(Service_CAM, "called"); 931 LOG_TRACE(Service_CAM, "called");
966} 932}
967 933
968void SetPackageParameterWithoutContext(Service::Interface* self) { 934void SetPackageParameterWithoutContext(Service::Interface* self) {
969 u32* cmd_buff = Kernel::GetCommandBuffer(); 935 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x33, 11, 0);
970 936
971 PackageParameterWithoutContext package; 937 PackageParameterWithoutContext package;
972 std::memcpy(&package, cmd_buff + 1, sizeof(package)); 938 rp.PopRaw(package);
973 939
974 cmd_buff[0] = IPC::MakeHeader(0x33, 1, 0); 940 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
975 cmd_buff[1] = RESULT_SUCCESS.raw; 941 rb.Push(RESULT_SUCCESS);
976 942
977 LOG_WARNING(Service_CAM, "(STUBBED) called"); 943 LOG_WARNING(Service_CAM, "(STUBBED) called");
978} 944}
979 945
980template <typename PackageParameterType, int command_id> 946template <typename PackageParameterType>
981static void SetPackageParameter() { 947static ResultCode SetPackageParameter(const PackageParameterType& package) {
982 u32* cmd_buff = Kernel::GetCommandBuffer(); 948 const CameraSet camera_select(package.camera_select);
983 949 const ContextSet context_select(package.context_select);
984 PackageParameterType package;
985 std::memcpy(&package, cmd_buff + 1, sizeof(package));
986
987 const CameraSet camera_select(static_cast<u32>(package.camera_select));
988 const ContextSet context_select(static_cast<u32>(package.context_select));
989 950
990 if (camera_select.IsValid() && context_select.IsValid()) { 951 if (camera_select.IsValid() && context_select.IsValid()) {
991 for (int camera_id : camera_select) { 952 for (int camera_id : camera_select) {
@@ -1002,53 +963,66 @@ static void SetPackageParameter() {
1002 } 963 }
1003 } 964 }
1004 } 965 }
1005 cmd_buff[1] = RESULT_SUCCESS.raw; 966 return RESULT_SUCCESS;
1006 } else { 967 } else {
1007 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", package.camera_select, 968 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", package.camera_select,
1008 package.context_select); 969 package.context_select);
1009 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; 970 return ERROR_INVALID_ENUM_VALUE;
1010 } 971 }
1011
1012 cmd_buff[0] = IPC::MakeHeader(command_id, 1, 0);
1013
1014 LOG_DEBUG(Service_CAM, "called");
1015} 972}
1016 973
1017Resolution PackageParameterWithContext::GetResolution() { 974Resolution PackageParameterWithContext::GetResolution() const {
1018 return PRESET_RESOLUTION[static_cast<int>(size)]; 975 return PRESET_RESOLUTION[static_cast<int>(size)];
1019} 976}
1020 977
1021void SetPackageParameterWithContext(Service::Interface* self) { 978void SetPackageParameterWithContext(Service::Interface* self) {
1022 SetPackageParameter<PackageParameterWithContext, 0x34>(); 979 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x34, 5, 0);
980
981 PackageParameterWithContext package;
982 rp.PopRaw(package);
983
984 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
985 ResultCode result = SetPackageParameter(package);
986 rb.Push(result);
987
988 LOG_DEBUG(Service_CAM, "called");
1023} 989}
1024 990
1025void SetPackageParameterWithContextDetail(Service::Interface* self) { 991void SetPackageParameterWithContextDetail(Service::Interface* self) {
1026 SetPackageParameter<PackageParameterWithContextDetail, 0x35>(); 992 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x35, 7, 0);
993
994 PackageParameterWithContextDetail package;
995 rp.PopRaw(package);
996
997 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
998 ResultCode result = SetPackageParameter(package);
999 rb.Push(result);
1000
1001 LOG_DEBUG(Service_CAM, "called");
1027} 1002}
1028 1003
1029void GetSuitableY2rStandardCoefficient(Service::Interface* self) { 1004void GetSuitableY2rStandardCoefficient(Service::Interface* self) {
1030 u32* cmd_buff = Kernel::GetCommandBuffer(); 1005 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x36, 0, 0);
1031 1006 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
1032 cmd_buff[0] = IPC::MakeHeader(0x36, 2, 0); 1007 rb.Push(RESULT_SUCCESS);
1033 cmd_buff[1] = RESULT_SUCCESS.raw; 1008 rb.Push<u32>(0);
1034 cmd_buff[2] = 0;
1035 1009
1036 LOG_WARNING(Service_CAM, "(STUBBED) called"); 1010 LOG_WARNING(Service_CAM, "(STUBBED) called");
1037} 1011}
1038 1012
1039void PlayShutterSound(Service::Interface* self) { 1013void PlayShutterSound(Service::Interface* self) {
1040 u32* cmd_buff = Kernel::GetCommandBuffer(); 1014 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x38, 1, 0);
1041 1015 u8 sound_id = rp.Pop<u8>();
1042 u8 sound_id = cmd_buff[1] & 0xFF;
1043 1016
1044 cmd_buff[0] = IPC::MakeHeader(0x38, 1, 0); 1017 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
1045 cmd_buff[1] = RESULT_SUCCESS.raw; 1018 rb.Push(RESULT_SUCCESS);
1046 1019
1047 LOG_WARNING(Service_CAM, "(STUBBED) called, sound_id=%d", sound_id); 1020 LOG_WARNING(Service_CAM, "(STUBBED) called, sound_id=%d", sound_id);
1048} 1021}
1049 1022
1050void DriverInitialize(Service::Interface* self) { 1023void DriverInitialize(Service::Interface* self) {
1051 u32* cmd_buff = Kernel::GetCommandBuffer(); 1024 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x39, 0, 0);
1025 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
1052 1026
1053 for (int camera_id = 0; camera_id < NumCameras; ++camera_id) { 1027 for (int camera_id = 0; camera_id < NumCameras; ++camera_id) {
1054 CameraConfig& camera = cameras[camera_id]; 1028 CameraConfig& camera = cameras[camera_id];
@@ -1074,14 +1048,14 @@ void DriverInitialize(Service::Interface* self) {
1074 port.Clear(); 1048 port.Clear();
1075 } 1049 }
1076 1050
1077 cmd_buff[0] = IPC::MakeHeader(0x39, 1, 0); 1051 rb.Push(RESULT_SUCCESS);
1078 cmd_buff[1] = RESULT_SUCCESS.raw;
1079 1052
1080 LOG_DEBUG(Service_CAM, "called"); 1053 LOG_DEBUG(Service_CAM, "called");
1081} 1054}
1082 1055
1083void DriverFinalize(Service::Interface* self) { 1056void DriverFinalize(Service::Interface* self) {
1084 u32* cmd_buff = Kernel::GetCommandBuffer(); 1057 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3A, 0, 0);
1058 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
1085 1059
1086 CancelReceiving(0); 1060 CancelReceiving(0);
1087 CancelReceiving(1); 1061 CancelReceiving(1);
@@ -1090,8 +1064,7 @@ void DriverFinalize(Service::Interface* self) {
1090 camera.impl = nullptr; 1064 camera.impl = nullptr;
1091 } 1065 }
1092 1066
1093 cmd_buff[0] = IPC::MakeHeader(0x3A, 1, 0); 1067 rb.Push(RESULT_SUCCESS);
1094 cmd_buff[1] = RESULT_SUCCESS.raw;
1095 1068
1096 LOG_DEBUG(Service_CAM, "called"); 1069 LOG_DEBUG(Service_CAM, "called");
1097} 1070}
diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h
index 34a9c8479..b6da721d8 100644
--- a/src/core/hle/service/cam/cam.h
+++ b/src/core/hle/service/cam/cam.h
@@ -184,9 +184,10 @@ struct PackageParameterWithoutContext {
184 s16 auto_white_balance_window_y; 184 s16 auto_white_balance_window_y;
185 s16 auto_white_balance_window_width; 185 s16 auto_white_balance_window_width;
186 s16 auto_white_balance_window_height; 186 s16 auto_white_balance_window_height;
187 INSERT_PADDING_WORDS(4);
187}; 188};
188 189
189static_assert(sizeof(PackageParameterWithoutContext) == 28, 190static_assert(sizeof(PackageParameterWithoutContext) == 44,
190 "PackageParameterCameraWithoutContext structure size is wrong"); 191 "PackageParameterCameraWithoutContext structure size is wrong");
191 192
192struct PackageParameterWithContext { 193struct PackageParameterWithContext {
@@ -196,11 +197,12 @@ struct PackageParameterWithContext {
196 Effect effect; 197 Effect effect;
197 Size size; 198 Size size;
198 INSERT_PADDING_BYTES(3); 199 INSERT_PADDING_BYTES(3);
200 INSERT_PADDING_WORDS(3);
199 201
200 Resolution GetResolution(); 202 Resolution GetResolution() const;
201}; 203};
202 204
203static_assert(sizeof(PackageParameterWithContext) == 8, 205static_assert(sizeof(PackageParameterWithContext) == 20,
204 "PackageParameterWithContext structure size is wrong"); 206 "PackageParameterWithContext structure size is wrong");
205 207
206struct PackageParameterWithContextDetail { 208struct PackageParameterWithContextDetail {
@@ -209,13 +211,14 @@ struct PackageParameterWithContextDetail {
209 Flip flip; 211 Flip flip;
210 Effect effect; 212 Effect effect;
211 Resolution resolution; 213 Resolution resolution;
214 INSERT_PADDING_WORDS(3);
212 215
213 Resolution GetResolution() { 216 Resolution GetResolution() const {
214 return resolution; 217 return resolution;
215 } 218 }
216}; 219};
217 220
218static_assert(sizeof(PackageParameterWithContextDetail) == 16, 221static_assert(sizeof(PackageParameterWithContextDetail) == 28,
219 "PackageParameterWithContextDetail structure size is wrong"); 222 "PackageParameterWithContextDetail structure size is wrong");
220 223
221/** 224/**
diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp
index eb04273db..bd9814244 100644
--- a/src/core/hle/service/cecd/cecd.cpp
+++ b/src/core/hle/service/cecd/cecd.cpp
@@ -3,7 +3,10 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/hle/ipc.h"
6#include "core/hle/kernel/event.h" 7#include "core/hle/kernel/event.h"
8#include "core/hle/kernel/handle_table.h"
9#include "core/hle/result.h"
7#include "core/hle/service/cecd/cecd.h" 10#include "core/hle/service/cecd/cecd.h"
8#include "core/hle/service/cecd/cecd_ndm.h" 11#include "core/hle/service/cecd/cecd_ndm.h"
9#include "core/hle/service/cecd/cecd_s.h" 12#include "core/hle/service/cecd/cecd_s.h"
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 8c8c1ec77..5a7878b31 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -11,7 +11,10 @@
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/file_sys/archive_systemsavedata.h" 13#include "core/file_sys/archive_systemsavedata.h"
14#include "core/file_sys/errors.h"
14#include "core/file_sys/file_backend.h" 15#include "core/file_sys/file_backend.h"
16#include "core/hle/ipc.h"
17#include "core/hle/ipc_helpers.h"
15#include "core/hle/result.h" 18#include "core/hle/result.h"
16#include "core/hle/service/cfg/cfg.h" 19#include "core/hle/service/cfg/cfg.h"
17#include "core/hle/service/cfg/cfg_i.h" 20#include "core/hle/service/cfg/cfg_i.h"
@@ -20,6 +23,7 @@
20#include "core/hle/service/cfg/cfg_u.h" 23#include "core/hle/service/cfg/cfg_u.h"
21#include "core/hle/service/fs/archive.h" 24#include "core/hle/service/fs/archive.h"
22#include "core/hle/service/service.h" 25#include "core/hle/service/service.h"
26#include "core/memory.h"
23#include "core/settings.h" 27#include "core/settings.h"
24 28
25namespace Service { 29namespace Service {
@@ -411,7 +415,7 @@ ResultCode UpdateConfigNANDSavegame() {
411ResultCode FormatConfig() { 415ResultCode FormatConfig() {
412 ResultCode res = DeleteConfigNANDSaveFile(); 416 ResultCode res = DeleteConfigNANDSaveFile();
413 // The delete command fails if the file doesn't exist, so we have to check that too 417 // The delete command fails if the file doesn't exist, so we have to check that too
414 if (!res.IsSuccess() && res.description != ErrorDescription::FS_FileNotFound) { 418 if (!res.IsSuccess() && res != FileSys::ERROR_FILE_NOT_FOUND) {
415 return res; 419 return res;
416 } 420 }
417 // Delete the old data 421 // Delete the old data
@@ -534,7 +538,7 @@ ResultCode LoadConfigNANDSaveFile() {
534 Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); 538 Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
535 539
536 // If the archive didn't exist, create the files inside 540 // If the archive didn't exist, create the files inside
537 if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { 541 if (archive_result.Code() == FileSys::ERR_NOT_FORMATTED) {
538 // Format the archive to create the directories 542 // Format the archive to create the directories
539 Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, 543 Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData,
540 FileSys::ArchiveFormatInfo(), archive_path); 544 FileSys::ArchiveFormatInfo(), archive_path);
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 6cf62f9bc..1455f20ca 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -4,9 +4,12 @@
4 4
5#include <cstring> 5#include <cstring>
6#include "common/alignment.h" 6#include "common/alignment.h"
7#include "core/hle/ipc.h"
8#include "core/hle/kernel/handle_table.h"
7#include "core/hle/kernel/mutex.h" 9#include "core/hle/kernel/mutex.h"
8#include "core/hle/kernel/shared_memory.h" 10#include "core/hle/kernel/shared_memory.h"
9#include "core/hle/service/csnd_snd.h" 11#include "core/hle/service/csnd_snd.h"
12#include "core/memory.h"
10 13
11namespace Service { 14namespace Service {
12namespace CSND { 15namespace CSND {
diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp
index 25c07f401..32cfa2c44 100644
--- a/src/core/hle/service/dlp/dlp_srvr.cpp
+++ b/src/core/hle/service/dlp/dlp_srvr.cpp
@@ -4,6 +4,7 @@
4 4
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/result.h" 8#include "core/hle/result.h"
8#include "core/hle/service/dlp/dlp_srvr.h" 9#include "core/hle/service/dlp/dlp_srvr.h"
9 10
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 39711ea97..363066d14 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -3,12 +3,18 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array>
6#include <cinttypes> 7#include <cinttypes>
7#include "audio_core/hle/pipe.h" 8#include "audio_core/hle/pipe.h"
9#include "common/assert.h"
8#include "common/hash.h" 10#include "common/hash.h"
9#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/hle/ipc.h"
10#include "core/hle/kernel/event.h" 13#include "core/hle/kernel/event.h"
14#include "core/hle/kernel/handle_table.h"
15#include "core/hle/result.h"
11#include "core/hle/service/dsp_dsp.h" 16#include "core/hle/service/dsp_dsp.h"
17#include "core/memory.h"
12 18
13using DspPipe = DSP::HLE::DspPipe; 19using DspPipe = DSP::HLE::DspPipe;
14 20
@@ -289,9 +295,7 @@ static void WriteProcessPipe(Service::Interface* self) {
289 "size=0x%X, buffer=0x%08X", 295 "size=0x%X, buffer=0x%08X",
290 cmd_buff[3], pipe_index, size, buffer); 296 cmd_buff[3], pipe_index, size, buffer);
291 cmd_buff[0] = IPC::MakeHeader(0, 1, 0); 297 cmd_buff[0] = IPC::MakeHeader(0, 1, 0);
292 cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, 298 cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw;
293 ErrorSummary::WrongArgument, ErrorLevel::Permanent)
294 .raw;
295 return; 299 return;
296 } 300 }
297 301
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index 9da55f328..7c8f4339f 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -6,10 +6,11 @@
6#include <chrono> 6#include <chrono>
7#include <iomanip> 7#include <iomanip>
8#include <sstream> 8#include <sstream>
9
10#include "common/bit_field.h" 9#include "common/bit_field.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/hle/ipc.h"
13#include "core/hle/result.h" 14#include "core/hle/result.h"
14#include "core/hle/service/err_f.h" 15#include "core/hle/service/err_f.h"
15 16
@@ -172,6 +173,7 @@ static void ThrowFatalError(Interface* self) {
172 const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]); 173 const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]);
173 LOG_CRITICAL(Service_ERR, "Fatal error type: %s", 174 LOG_CRITICAL(Service_ERR, "Fatal error type: %s",
174 GetErrType(errinfo->errinfo_common.specifier).c_str()); 175 GetErrType(errinfo->errinfo_common.specifier).c_str());
176 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorUnknown);
175 177
176 // Generic Info 178 // Generic Info
177 LogGenericInfo(errinfo->errinfo_common); 179 LogGenericInfo(errinfo->errinfo_common);
diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp
index 34fdf7f53..76ecda8b7 100644
--- a/src/core/hle/service/frd/frd.cpp
+++ b/src/core/hle/service/frd/frd.cpp
@@ -2,11 +2,16 @@
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 "common/assert.h"
6#include "common/logging/log.h"
5#include "common/string_util.h" 7#include "common/string_util.h"
8#include "core/hle/ipc.h"
9#include "core/hle/result.h"
6#include "core/hle/service/frd/frd.h" 10#include "core/hle/service/frd/frd.h"
7#include "core/hle/service/frd/frd_a.h" 11#include "core/hle/service/frd/frd_a.h"
8#include "core/hle/service/frd/frd_u.h" 12#include "core/hle/service/frd/frd_u.h"
9#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
14#include "core/memory.h"
10 15
11namespace Service { 16namespace Service {
12namespace FRD { 17namespace FRD {
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 6cddc1fdb..3605ef175 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -22,8 +22,13 @@
22#include "core/file_sys/archive_sdmcwriteonly.h" 22#include "core/file_sys/archive_sdmcwriteonly.h"
23#include "core/file_sys/archive_systemsavedata.h" 23#include "core/file_sys/archive_systemsavedata.h"
24#include "core/file_sys/directory_backend.h" 24#include "core/file_sys/directory_backend.h"
25#include "core/file_sys/errors.h"
25#include "core/file_sys/file_backend.h" 26#include "core/file_sys/file_backend.h"
27#include "core/hle/ipc.h"
28#include "core/hle/kernel/client_port.h"
26#include "core/hle/kernel/client_session.h" 29#include "core/hle/kernel/client_session.h"
30#include "core/hle/kernel/handle_table.h"
31#include "core/hle/kernel/server_session.h"
27#include "core/hle/result.h" 32#include "core/hle/result.h"
28#include "core/hle/service/fs/archive.h" 33#include "core/hle/service/fs/archive.h"
29#include "core/hle/service/fs/fs_user.h" 34#include "core/hle/service/fs/fs_user.h"
@@ -50,16 +55,6 @@ static constexpr Kernel::Handle INVALID_HANDLE{};
50namespace Service { 55namespace Service {
51namespace FS { 56namespace FS {
52 57
53// TODO: Verify code
54/// Returned when a function is passed an invalid handle.
55const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::FS,
56 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
57
58/// Returned when a function is passed an invalid archive handle.
59const ResultCode ERR_INVALID_ARCHIVE_HANDLE(ErrorDescription::FS_ArchiveNotMounted, ErrorModule::FS,
60 ErrorSummary::NotFound,
61 ErrorLevel::Status); // 0xC8804465
62
63// Command to access archive file 58// Command to access archive file
64enum class FileCommand : u32 { 59enum class FileCommand : u32 {
65 Dummy1 = 0x000100C6, 60 Dummy1 = 0x000100C6,
@@ -92,6 +87,10 @@ File::File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path&
92File::~File() {} 87File::~File() {}
93 88
94void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { 89void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) {
90 using Kernel::ClientSession;
91 using Kernel::ServerSession;
92 using Kernel::SharedPtr;
93
95 u32* cmd_buff = Kernel::GetCommandBuffer(); 94 u32* cmd_buff = Kernel::GetCommandBuffer();
96 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); 95 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
97 switch (cmd) { 96 switch (cmd) {
@@ -170,10 +169,9 @@ void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_ses
170 169
171 case FileCommand::OpenLinkFile: { 170 case FileCommand::OpenLinkFile: {
172 LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile %s", GetName().c_str()); 171 LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile %s", GetName().c_str());
173 auto sessions = Kernel::ServerSession::CreateSessionPair(GetName(), shared_from_this()); 172 auto sessions = ServerSession::CreateSessionPair(GetName());
174 ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); 173 ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
175 cmd_buff[3] = Kernel::g_handle_table 174 cmd_buff[3] = Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions))
176 .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions))
177 .ValueOr(INVALID_HANDLE); 175 .ValueOr(INVALID_HANDLE);
178 break; 176 break;
179 } 177 }
@@ -267,9 +265,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi
267 265
268 auto itr = id_code_map.find(id_code); 266 auto itr = id_code_map.find(id_code);
269 if (itr == id_code_map.end()) { 267 if (itr == id_code_map.end()) {
270 // TODO: Verify error against hardware 268 return FileSys::ERROR_NOT_FOUND;
271 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, ErrorSummary::NotFound,
272 ErrorLevel::Permanent);
273 } 269 }
274 270
275 CASCADE_RESULT(std::unique_ptr<ArchiveBackend> res, itr->second->Open(archive_path)); 271 CASCADE_RESULT(std::unique_ptr<ArchiveBackend> res, itr->second->Open(archive_path));
@@ -284,7 +280,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi
284 280
285ResultCode CloseArchive(ArchiveHandle handle) { 281ResultCode CloseArchive(ArchiveHandle handle) {
286 if (handle_map.erase(handle) == 0) 282 if (handle_map.erase(handle) == 0)
287 return ERR_INVALID_ARCHIVE_HANDLE; 283 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
288 else 284 else
289 return RESULT_SUCCESS; 285 return RESULT_SUCCESS;
290} 286}
@@ -309,7 +305,7 @@ ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handl
309 const FileSys::Mode mode) { 305 const FileSys::Mode mode) {
310 ArchiveBackend* archive = GetArchive(archive_handle); 306 ArchiveBackend* archive = GetArchive(archive_handle);
311 if (archive == nullptr) 307 if (archive == nullptr)
312 return ERR_INVALID_ARCHIVE_HANDLE; 308 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
313 309
314 auto backend = archive->OpenFile(path, mode); 310 auto backend = archive->OpenFile(path, mode);
315 if (backend.Failed()) 311 if (backend.Failed())
@@ -322,7 +318,7 @@ ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handl
322ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { 318ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
323 ArchiveBackend* archive = GetArchive(archive_handle); 319 ArchiveBackend* archive = GetArchive(archive_handle);
324 if (archive == nullptr) 320 if (archive == nullptr)
325 return ERR_INVALID_ARCHIVE_HANDLE; 321 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
326 322
327 return archive->DeleteFile(path); 323 return archive->DeleteFile(path);
328} 324}
@@ -334,7 +330,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle,
334 ArchiveBackend* src_archive = GetArchive(src_archive_handle); 330 ArchiveBackend* src_archive = GetArchive(src_archive_handle);
335 ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); 331 ArchiveBackend* dest_archive = GetArchive(dest_archive_handle);
336 if (src_archive == nullptr || dest_archive == nullptr) 332 if (src_archive == nullptr || dest_archive == nullptr)
337 return ERR_INVALID_ARCHIVE_HANDLE; 333 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
338 334
339 if (src_archive == dest_archive) { 335 if (src_archive == dest_archive) {
340 return src_archive->RenameFile(src_path, dest_path); 336 return src_archive->RenameFile(src_path, dest_path);
@@ -347,7 +343,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle,
347ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { 343ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
348 ArchiveBackend* archive = GetArchive(archive_handle); 344 ArchiveBackend* archive = GetArchive(archive_handle);
349 if (archive == nullptr) 345 if (archive == nullptr)
350 return ERR_INVALID_ARCHIVE_HANDLE; 346 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
351 347
352 return archive->DeleteDirectory(path); 348 return archive->DeleteDirectory(path);
353} 349}
@@ -356,7 +352,7 @@ ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle,
356 const FileSys::Path& path) { 352 const FileSys::Path& path) {
357 ArchiveBackend* archive = GetArchive(archive_handle); 353 ArchiveBackend* archive = GetArchive(archive_handle);
358 if (archive == nullptr) 354 if (archive == nullptr)
359 return ERR_INVALID_ARCHIVE_HANDLE; 355 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
360 356
361 return archive->DeleteDirectoryRecursively(path); 357 return archive->DeleteDirectoryRecursively(path);
362} 358}
@@ -365,7 +361,7 @@ ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path
365 u64 file_size) { 361 u64 file_size) {
366 ArchiveBackend* archive = GetArchive(archive_handle); 362 ArchiveBackend* archive = GetArchive(archive_handle);
367 if (archive == nullptr) 363 if (archive == nullptr)
368 return ERR_INVALID_ARCHIVE_HANDLE; 364 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
369 365
370 return archive->CreateFile(path, file_size); 366 return archive->CreateFile(path, file_size);
371} 367}
@@ -373,7 +369,7 @@ ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path
373ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { 369ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
374 ArchiveBackend* archive = GetArchive(archive_handle); 370 ArchiveBackend* archive = GetArchive(archive_handle);
375 if (archive == nullptr) 371 if (archive == nullptr)
376 return ERR_INVALID_ARCHIVE_HANDLE; 372 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
377 373
378 return archive->CreateDirectory(path); 374 return archive->CreateDirectory(path);
379} 375}
@@ -385,7 +381,7 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle,
385 ArchiveBackend* src_archive = GetArchive(src_archive_handle); 381 ArchiveBackend* src_archive = GetArchive(src_archive_handle);
386 ArchiveBackend* dest_archive = GetArchive(dest_archive_handle); 382 ArchiveBackend* dest_archive = GetArchive(dest_archive_handle);
387 if (src_archive == nullptr || dest_archive == nullptr) 383 if (src_archive == nullptr || dest_archive == nullptr)
388 return ERR_INVALID_ARCHIVE_HANDLE; 384 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
389 385
390 if (src_archive == dest_archive) { 386 if (src_archive == dest_archive) {
391 return src_archive->RenameDirectory(src_path, dest_path); 387 return src_archive->RenameDirectory(src_path, dest_path);
@@ -399,7 +395,7 @@ ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle arc
399 const FileSys::Path& path) { 395 const FileSys::Path& path) {
400 ArchiveBackend* archive = GetArchive(archive_handle); 396 ArchiveBackend* archive = GetArchive(archive_handle);
401 if (archive == nullptr) 397 if (archive == nullptr)
402 return ERR_INVALID_ARCHIVE_HANDLE; 398 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
403 399
404 auto backend = archive->OpenDirectory(path); 400 auto backend = archive->OpenDirectory(path);
405 if (backend.Failed()) 401 if (backend.Failed())
@@ -412,7 +408,7 @@ ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle arc
412ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) { 408ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) {
413 ArchiveBackend* archive = GetArchive(archive_handle); 409 ArchiveBackend* archive = GetArchive(archive_handle);
414 if (archive == nullptr) 410 if (archive == nullptr)
415 return ERR_INVALID_ARCHIVE_HANDLE; 411 return FileSys::ERR_INVALID_ARCHIVE_HANDLE;
416 return MakeResult<u64>(archive->GetFreeBytes()); 412 return MakeResult<u64>(archive->GetFreeBytes());
417} 413}
418 414
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 2ea956e0b..3a3371c88 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -8,7 +8,7 @@
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/file_sys/archive_backend.h" 10#include "core/file_sys/archive_backend.h"
11#include "core/hle/kernel/server_session.h" 11#include "core/hle/kernel/hle_ipc.h"
12#include "core/hle/result.h" 12#include "core/hle/result.h"
13 13
14namespace FileSys { 14namespace FileSys {
@@ -43,7 +43,7 @@ enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 };
43 43
44typedef u64 ArchiveHandle; 44typedef u64 ArchiveHandle;
45 45
46class File final : public SessionRequestHandler, public std::enable_shared_from_this<File> { 46class File final : public Kernel::SessionRequestHandler {
47public: 47public:
48 File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path); 48 File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path);
49 ~File(); 49 ~File();
@@ -60,7 +60,7 @@ protected:
60 void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; 60 void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override;
61}; 61};
62 62
63class Directory final : public SessionRequestHandler { 63class Directory final : public Kernel::SessionRequestHandler {
64public: 64public:
65 Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path); 65 Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path);
66 ~Directory(); 66 ~Directory();
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 33b290699..34e1783ec 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -8,7 +8,13 @@
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/scope_exit.h" 9#include "common/scope_exit.h"
10#include "common/string_util.h" 10#include "common/string_util.h"
11#include "core/core.h"
12#include "core/file_sys/errors.h"
13#include "core/hle/ipc.h"
14#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/client_port.h"
11#include "core/hle/kernel/client_session.h" 16#include "core/hle/kernel/client_session.h"
17#include "core/hle/kernel/server_session.h"
12#include "core/hle/result.h" 18#include "core/hle/result.h"
13#include "core/hle/service/fs/archive.h" 19#include "core/hle/service/fs/archive.h"
14#include "core/hle/service/fs/fs_user.h" 20#include "core/hle/service/fs/fs_user.h"
@@ -17,8 +23,9 @@
17//////////////////////////////////////////////////////////////////////////////////////////////////// 23////////////////////////////////////////////////////////////////////////////////////////////////////
18// Namespace FS_User 24// Namespace FS_User
19 25
20using Kernel::SharedPtr; 26using Kernel::ClientSession;
21using Kernel::ServerSession; 27using Kernel::ServerSession;
28using Kernel::SharedPtr;
22 29
23namespace Service { 30namespace Service {
24namespace FS { 31namespace FS {
@@ -76,11 +83,11 @@ static void OpenFile(Service::Interface* self) {
76 rb.Push(file_res.Code()); 83 rb.Push(file_res.Code());
77 if (file_res.Succeeded()) { 84 if (file_res.Succeeded()) {
78 std::shared_ptr<File> file = *file_res; 85 std::shared_ptr<File> file = *file_res;
79 auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); 86 auto sessions = ServerSession::CreateSessionPair(file->GetName());
80 file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); 87 file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
81 rb.PushMoveHandles(Kernel::g_handle_table 88
82 .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) 89 rb.PushMoveHandles(
83 .MoveFrom()); 90 Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom());
84 } else { 91 } else {
85 rb.PushMoveHandles(0); 92 rb.PushMoveHandles(0);
86 LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); 93 LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
@@ -129,7 +136,7 @@ static void OpenFileDirectly(Service::Interface* self) {
129 ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); 136 ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path);
130 if (archive_handle.Failed()) { 137 if (archive_handle.Failed()) {
131 LOG_ERROR(Service_FS, 138 LOG_ERROR(Service_FS,
132 "failed to get a handle for archive archive_id=0x%08X archive_path=%s", 139 "Failed to get a handle for archive archive_id=0x%08X archive_path=%s",
133 static_cast<u32>(archive_id), archive_path.DebugStr().c_str()); 140 static_cast<u32>(archive_id), archive_path.DebugStr().c_str());
134 cmd_buff[1] = archive_handle.Code().raw; 141 cmd_buff[1] = archive_handle.Code().raw;
135 cmd_buff[3] = 0; 142 cmd_buff[3] = 0;
@@ -142,11 +149,11 @@ static void OpenFileDirectly(Service::Interface* self) {
142 cmd_buff[1] = file_res.Code().raw; 149 cmd_buff[1] = file_res.Code().raw;
143 if (file_res.Succeeded()) { 150 if (file_res.Succeeded()) {
144 std::shared_ptr<File> file = *file_res; 151 std::shared_ptr<File> file = *file_res;
145 auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); 152 auto sessions = ServerSession::CreateSessionPair(file->GetName());
146 file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); 153 file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
147 cmd_buff[3] = Kernel::g_handle_table 154
148 .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) 155 cmd_buff[3] =
149 .MoveFrom(); 156 Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom();
150 } else { 157 } else {
151 cmd_buff[3] = 0; 158 cmd_buff[3] = 0;
152 LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%u", 159 LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%u",
@@ -409,11 +416,11 @@ static void OpenDirectory(Service::Interface* self) {
409 cmd_buff[1] = dir_res.Code().raw; 416 cmd_buff[1] = dir_res.Code().raw;
410 if (dir_res.Succeeded()) { 417 if (dir_res.Succeeded()) {
411 std::shared_ptr<Directory> directory = *dir_res; 418 std::shared_ptr<Directory> directory = *dir_res;
412 auto sessions = ServerSession::CreateSessionPair(directory->GetName(), directory); 419 auto sessions = ServerSession::CreateSessionPair(directory->GetName());
413 directory->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); 420 directory->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
414 cmd_buff[3] = Kernel::g_handle_table 421
415 .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) 422 cmd_buff[3] =
416 .MoveFrom(); 423 Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom();
417 } else { 424 } else {
418 LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s", 425 LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s",
419 dirname_type, dirname_size, dir_path.DebugStr().c_str()); 426 dirname_type, dirname_size, dir_path.DebugStr().c_str());
@@ -539,9 +546,7 @@ static void FormatSaveData(Service::Interface* self) {
539 if (archive_id != FS::ArchiveIdCode::SaveData) { 546 if (archive_id != FS::ArchiveIdCode::SaveData) {
540 LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", 547 LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u",
541 static_cast<u32>(archive_id)); 548 static_cast<u32>(archive_id));
542 cmd_buff[1] = ResultCode(ErrorDescription::FS_InvalidPath, ErrorModule::FS, 549 cmd_buff[1] = FileSys::ERROR_INVALID_PATH.raw;
543 ErrorSummary::InvalidArgument, ErrorLevel::Usage)
544 .raw;
545 return; 550 return;
546 } 551 }
547 552
@@ -802,9 +807,7 @@ static void InitializeWithSdkVersion(Service::Interface* self) {
802 cmd_buff[1] = RESULT_SUCCESS.raw; 807 cmd_buff[1] = RESULT_SUCCESS.raw;
803 } else { 808 } else {
804 LOG_ERROR(Service_FS, "ProcessId Header must be 0x20"); 809 LOG_ERROR(Service_FS, "ProcessId Header must be 0x20");
805 cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, 810 cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw;
806 ErrorSummary::WrongArgument, ErrorLevel::Permanent)
807 .raw;
808 } 811 }
809} 812}
810 813
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index a960778a7..6ff0f4812 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -5,14 +5,16 @@
5#include "common/bit_field.h" 5#include "common/bit_field.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/ipc.h"
8#include "core/hle/kernel/event.h" 9#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/handle_table.h"
9#include "core/hle/kernel/shared_memory.h" 11#include "core/hle/kernel/shared_memory.h"
10#include "core/hle/result.h" 12#include "core/hle/result.h"
13#include "core/hle/service/gsp_gpu.h"
11#include "core/hw/gpu.h" 14#include "core/hw/gpu.h"
12#include "core/hw/hw.h" 15#include "core/hw/hw.h"
13#include "core/hw/lcd.h" 16#include "core/hw/lcd.h"
14#include "core/memory.h" 17#include "core/memory.h"
15#include "gsp_gpu.h"
16#include "video_core/debug_utils/debug_utils.h" 18#include "video_core/debug_utils/debug_utils.h"
17#include "video_core/gpu_debugger.h" 19#include "video_core/gpu_debugger.h"
18 20
@@ -25,13 +27,24 @@ namespace GSP {
25// Beginning address of HW regs 27// Beginning address of HW regs
26const u32 REGS_BEGIN = 0x1EB00000; 28const u32 REGS_BEGIN = 0x1EB00000;
27 29
28const ResultCode ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED( 30namespace ErrCodes {
29 ErrorDescription::OutofRangeOrMisalignedAddress, ErrorModule::GX, ErrorSummary::InvalidArgument, 31enum {
30 ErrorLevel::Usage); // 0xE0E02A01 32 // TODO(purpasmart): Check if this name fits its actual usage
31const ResultCode ERR_GSP_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX, 33 OutofRangeOrMisalignedAddress = 513,
34 FirstInitialization = 519,
35};
36}
37
38constexpr ResultCode RESULT_FIRST_INITIALIZATION(ErrCodes::FirstInitialization, ErrorModule::GX,
39 ErrorSummary::Success, ErrorLevel::Success);
40constexpr ResultCode ERR_REGS_OUTOFRANGE_OR_MISALIGNED(ErrCodes::OutofRangeOrMisalignedAddress,
41 ErrorModule::GX,
42 ErrorSummary::InvalidArgument,
43 ErrorLevel::Usage); // 0xE0E02A01
44constexpr ResultCode ERR_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX,
32 ErrorSummary::InvalidArgument, 45 ErrorSummary::InvalidArgument,
33 ErrorLevel::Usage); // 0xE0E02BF2 46 ErrorLevel::Usage); // 0xE0E02BF2
34const ResultCode ERR_GSP_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX, 47constexpr ResultCode ERR_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX,
35 ErrorSummary::InvalidArgument, 48 ErrorSummary::InvalidArgument,
36 ErrorLevel::Usage); // 0xE0E02BEC 49 ErrorLevel::Usage); // 0xE0E02BEC
37 50
@@ -93,11 +106,11 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, VAddr data_va
93 LOG_ERROR(Service_GSP, 106 LOG_ERROR(Service_GSP,
94 "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", 107 "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)",
95 base_address, size_in_bytes); 108 base_address, size_in_bytes);
96 return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; 109 return ERR_REGS_OUTOFRANGE_OR_MISALIGNED;
97 } else if (size_in_bytes <= max_size_in_bytes) { 110 } else if (size_in_bytes <= max_size_in_bytes) {
98 if (size_in_bytes & 3) { 111 if (size_in_bytes & 3) {
99 LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); 112 LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes);
100 return ERR_GSP_REGS_MISALIGNED; 113 return ERR_REGS_MISALIGNED;
101 } else { 114 } else {
102 while (size_in_bytes > 0) { 115 while (size_in_bytes > 0) {
103 WriteSingleHWReg(base_address, Memory::Read32(data_vaddr)); 116 WriteSingleHWReg(base_address, Memory::Read32(data_vaddr));
@@ -111,7 +124,7 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, VAddr data_va
111 124
112 } else { 125 } else {
113 LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); 126 LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes);
114 return ERR_GSP_REGS_INVALID_SIZE; 127 return ERR_REGS_INVALID_SIZE;
115 } 128 }
116} 129}
117 130
@@ -134,11 +147,11 @@ static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr
134 LOG_ERROR(Service_GSP, 147 LOG_ERROR(Service_GSP,
135 "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", 148 "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)",
136 base_address, size_in_bytes); 149 base_address, size_in_bytes);
137 return ERR_GSP_REGS_OUTOFRANGE_OR_MISALIGNED; 150 return ERR_REGS_OUTOFRANGE_OR_MISALIGNED;
138 } else if (size_in_bytes <= max_size_in_bytes) { 151 } else if (size_in_bytes <= max_size_in_bytes) {
139 if (size_in_bytes & 3) { 152 if (size_in_bytes & 3) {
140 LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); 153 LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes);
141 return ERR_GSP_REGS_MISALIGNED; 154 return ERR_REGS_MISALIGNED;
142 } else { 155 } else {
143 while (size_in_bytes > 0) { 156 while (size_in_bytes > 0) {
144 const u32 reg_address = base_address + REGS_BEGIN; 157 const u32 reg_address = base_address + REGS_BEGIN;
@@ -164,7 +177,7 @@ static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr
164 177
165 } else { 178 } else {
166 LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); 179 LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes);
167 return ERR_GSP_REGS_INVALID_SIZE; 180 return ERR_REGS_INVALID_SIZE;
168 } 181 }
169} 182}
170 183
@@ -372,9 +385,7 @@ static void RegisterInterruptRelayQueue(Interface* self) {
372 if (first_initialization) { 385 if (first_initialization) {
373 // This specific code is required for a successful initialization, rather than 0 386 // This specific code is required for a successful initialization, rather than 0
374 first_initialization = false; 387 first_initialization = false;
375 cmd_buff[1] = ResultCode(ErrorDescription::GPU_FirstInitialization, ErrorModule::GX, 388 cmd_buff[1] = RESULT_FIRST_INITIALIZATION.raw;
376 ErrorSummary::Success, ErrorLevel::Success)
377 .raw;
378 } else { 389 } else {
379 cmd_buff[1] = RESULT_SUCCESS.raw; 390 cmd_buff[1] = RESULT_SUCCESS.raw;
380 } 391 }
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 64d01cdd7..5255f6dc8 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -10,7 +10,9 @@
10#include "core/core_timing.h" 10#include "core/core_timing.h"
11#include "core/frontend/emu_window.h" 11#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h" 12#include "core/frontend/input.h"
13#include "core/hle/ipc.h"
13#include "core/hle/kernel/event.h" 14#include "core/hle/kernel/event.h"
15#include "core/hle/kernel/handle_table.h"
14#include "core/hle/kernel/shared_memory.h" 16#include "core/hle/kernel/shared_memory.h"
15#include "core/hle/service/hid/hid.h" 17#include "core/hle/service/hid/hid.h"
16#include "core/hle/service/hid/hid_spvr.h" 18#include "core/hle/service/hid/hid_spvr.h"
diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp
index 53807cd91..0de698003 100644
--- a/src/core/hle/service/ir/ir_rst.cpp
+++ b/src/core/hle/service/ir/ir_rst.cpp
@@ -6,6 +6,7 @@
6#include "common/bit_field.h" 6#include "common/bit_field.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/frontend/input.h" 8#include "core/frontend/input.h"
9#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/shared_memory.h" 11#include "core/hle/kernel/shared_memory.h"
11#include "core/hle/service/hid/hid.h" 12#include "core/hle/service/hid/hid.h"
diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp
index 226af0083..fdecdce64 100644
--- a/src/core/hle/service/ir/ir_user.cpp
+++ b/src/core/hle/service/ir/ir_user.cpp
@@ -7,6 +7,7 @@
7#include <boost/optional.hpp> 7#include <boost/optional.hpp>
8#include "common/string_util.h" 8#include "common/string_util.h"
9#include "common/swap.h" 9#include "common/swap.h"
10#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/event.h" 11#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/shared_memory.h" 12#include "core/hle/kernel/shared_memory.h"
12#include "core/hle/service/ir/extra_hid.h" 13#include "core/hle/service/ir/extra_hid.h"
@@ -267,8 +268,7 @@ static void InitializeIrNopShared(Interface* self) {
267 shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); 268 shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle);
268 if (!shared_memory) { 269 if (!shared_memory) {
269 LOG_CRITICAL(Service_IR, "invalid shared memory handle 0x%08X", handle); 270 LOG_CRITICAL(Service_IR, "invalid shared memory handle 0x%08X", handle);
270 rb.Push(ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, 271 rb.Push(IPC::ERR_INVALID_HANDLE);
271 ErrorSummary::WrongArgument, ErrorLevel::Permanent));
272 return; 272 return;
273 } 273 }
274 shared_memory->name = "IR_USER: shared memory"; 274 shared_memory->name = "IR_USER: shared memory";
diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp
index d1e6d869f..7255ea026 100644
--- a/src/core/hle/service/ldr_ro/ldr_ro.cpp
+++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp
@@ -7,6 +7,7 @@
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
11#include "core/hle/kernel/vm_manager.h" 12#include "core/hle/kernel/vm_manager.h"
12#include "core/hle/service/ldr_ro/cro_helper.h" 13#include "core/hle/service/ldr_ro/cro_helper.h"
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index e98388560..35212b59b 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -3,7 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/hle/ipc.h"
6#include "core/hle/kernel/event.h" 7#include "core/hle/kernel/event.h"
8#include "core/hle/kernel/handle_table.h"
7#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/shared_memory.h" 10#include "core/hle/kernel/shared_memory.h"
9#include "core/hle/service/mic_u.h" 11#include "core/hle/service/mic_u.h"
diff --git a/src/core/hle/service/ndm/ndm.cpp b/src/core/hle/service/ndm/ndm.cpp
index 5eb97f0d3..096c0cdac 100644
--- a/src/core/hle/service/ndm/ndm.cpp
+++ b/src/core/hle/service/ndm/ndm.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 <array>
5#include "common/common_types.h" 6#include "common/common_types.h"
6#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/ipc.h"
7#include "core/hle/service/ndm/ndm.h" 9#include "core/hle/service/ndm/ndm.h"
8#include "core/hle/service/ndm/ndm_u.h" 10#include "core/hle/service/ndm/ndm_u.h"
9#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index fd3c7d9c2..b44a9f668 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -2,7 +2,9 @@
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 "core/hle/ipc.h"
5#include "core/hle/kernel/event.h" 6#include "core/hle/kernel/event.h"
7#include "core/hle/kernel/handle_table.h"
6#include "core/hle/service/nfc/nfc.h" 8#include "core/hle/service/nfc/nfc.h"
7#include "core/hle/service/nfc/nfc_m.h" 9#include "core/hle/service/nfc/nfc_m.h"
8#include "core/hle/service/nfc/nfc_u.h" 10#include "core/hle/service/nfc/nfc_u.h"
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 63c334cb2..d5624fe54 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -4,6 +4,7 @@
4 4
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/service/nim/nim.h" 8#include "core/hle/service/nim/nim.h"
8#include "core/hle/service/nim/nim_aoc.h" 9#include "core/hle/service/nim/nim_aoc.h"
9#include "core/hle/service/nim/nim_s.h" 10#include "core/hle/service/nim/nim_s.h"
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index 581816e81..6c4600f25 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -9,6 +9,7 @@
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 "core/core_timing.h" 11#include "core/core_timing.h"
12#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/event.h" 13#include "core/hle/kernel/event.h"
13#include "core/hle/kernel/shared_memory.h" 14#include "core/hle/kernel/shared_memory.h"
14#include "core/hle/result.h" 15#include "core/hle/result.h"
@@ -422,6 +423,102 @@ static void SetApplicationData(Interface* self) {
422 rb.Push(RESULT_SUCCESS); 423 rb.Push(RESULT_SUCCESS);
423} 424}
424 425
426/**
427 * NWM_UDS::DecryptBeaconData service function.
428 * Decrypts the encrypted data tags contained in the 802.11 beacons.
429 * Inputs:
430 * 1 : Input network struct buffer descriptor.
431 * 2 : Input network struct buffer ptr.
432 * 3 : Input tag0 encrypted buffer descriptor.
433 * 4 : Input tag0 encrypted buffer ptr.
434 * 5 : Input tag1 encrypted buffer descriptor.
435 * 6 : Input tag1 encrypted buffer ptr.
436 * 64 : Output buffer descriptor.
437 * 65 : Output buffer ptr.
438 * Outputs:
439 * 0 : Return header
440 * 1 : Result of function, 0 on success, otherwise error code
441 */
442static void DecryptBeaconData(Interface* self) {
443 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1F, 0, 6);
444
445 size_t desc_size;
446 const VAddr network_struct_addr = rp.PopStaticBuffer(&desc_size);
447 ASSERT(desc_size == sizeof(NetworkInfo));
448
449 size_t data0_size;
450 const VAddr encrypted_data0_addr = rp.PopStaticBuffer(&data0_size);
451
452 size_t data1_size;
453 const VAddr encrypted_data1_addr = rp.PopStaticBuffer(&data1_size);
454
455 size_t output_buffer_size;
456 const VAddr output_buffer_addr = rp.PeekStaticBuffer(0, &output_buffer_size);
457
458 // This size is hardcoded in the 3DS UDS code.
459 ASSERT(output_buffer_size == sizeof(NodeInfo) * UDSMaxNodes);
460
461 LOG_WARNING(Service_NWM, "called in0=%08X in1=%08X out=%08X", encrypted_data0_addr,
462 encrypted_data1_addr, output_buffer_addr);
463
464 NetworkInfo net_info;
465 Memory::ReadBlock(network_struct_addr, &net_info, sizeof(net_info));
466
467 // Read the encrypted data.
468 // The first 4 bytes should be the OUI and the OUI Type of the tags.
469 std::array<u8, 3> oui;
470 Memory::ReadBlock(encrypted_data0_addr, oui.data(), oui.size());
471 ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI");
472 Memory::ReadBlock(encrypted_data1_addr, oui.data(), oui.size());
473 ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI");
474
475 ASSERT_MSG(Memory::Read8(encrypted_data0_addr + 3) ==
476 static_cast<u8>(NintendoTagId::EncryptedData0),
477 "Unexpected tag id");
478 ASSERT_MSG(Memory::Read8(encrypted_data1_addr + 3) ==
479 static_cast<u8>(NintendoTagId::EncryptedData1),
480 "Unexpected tag id");
481
482 std::vector<u8> beacon_data(data0_size + data1_size);
483 Memory::ReadBlock(encrypted_data0_addr + 4, beacon_data.data(), data0_size);
484 Memory::ReadBlock(encrypted_data1_addr + 4, beacon_data.data() + data0_size, data1_size);
485
486 // Decrypt the data
487 DecryptBeaconData(net_info, beacon_data);
488
489 // The beacon data header contains the MD5 hash of the data.
490 BeaconData beacon_header;
491 std::memcpy(&beacon_header, beacon_data.data(), sizeof(beacon_header));
492
493 // TODO(Subv): Verify the MD5 hash of the data and return 0xE1211005 if invalid.
494
495 u8 num_nodes = net_info.max_nodes;
496
497 std::vector<NodeInfo> nodes;
498
499 for (int i = 0; i < num_nodes; ++i) {
500 BeaconNodeInfo info;
501 std::memcpy(&info, beacon_data.data() + sizeof(beacon_header) + i * sizeof(info),
502 sizeof(info));
503
504 // Deserialize the node information.
505 NodeInfo node{};
506 node.friend_code_seed = info.friend_code_seed;
507 node.network_node_id = info.network_node_id;
508 for (int i = 0; i < info.username.size(); ++i)
509 node.username[i] = info.username[i];
510
511 nodes.push_back(node);
512 }
513
514 Memory::ZeroBlock(output_buffer_addr, sizeof(NodeInfo) * UDSMaxNodes);
515 Memory::WriteBlock(output_buffer_addr, nodes.data(), sizeof(NodeInfo) * nodes.size());
516
517 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
518 rb.PushStaticBuffer(output_buffer_addr, output_buffer_size, 0);
519 rb.Push(RESULT_SUCCESS);
520}
521
425// Sends a 802.11 beacon frame with information about the current network. 522// Sends a 802.11 beacon frame with information about the current network.
426static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { 523static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
427 // Don't do anything if we're not actually hosting a network 524 // Don't do anything if we're not actually hosting a network
@@ -462,7 +559,7 @@ const Interface::FunctionInfo FunctionTable[] = {
462 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, 559 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"},
463 {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"}, 560 {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"},
464 {0x001E0084, nullptr, "ConnectToNetwork"}, 561 {0x001E0084, nullptr, "ConnectToNetwork"},
465 {0x001F0006, nullptr, "DecryptBeaconData"}, 562 {0x001F0006, DecryptBeaconData, "DecryptBeaconData"},
466 {0x00200040, nullptr, "Flush"}, 563 {0x00200040, nullptr, "Flush"},
467 {0x00210080, nullptr, "SetProbeResponseParam"}, 564 {0x00210080, nullptr, "SetProbeResponseParam"},
468 {0x00220402, nullptr, "ScanOnConnection"}, 565 {0x00220402, nullptr, "ScanOnConnection"},
diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp
index c6e5bc5f1..6332b404c 100644
--- a/src/core/hle/service/nwm/uds_beacon.cpp
+++ b/src/core/hle/service/nwm/uds_beacon.cpp
@@ -3,14 +3,13 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6
7#include "core/hle/service/nwm/nwm_uds.h"
8#include "core/hle/service/nwm/uds_beacon.h"
9
10#include <cryptopp/aes.h> 6#include <cryptopp/aes.h>
11#include <cryptopp/md5.h> 7#include <cryptopp/md5.h>
12#include <cryptopp/modes.h> 8#include <cryptopp/modes.h>
13#include <cryptopp/sha.h> 9#include <cryptopp/sha.h>
10#include "common/assert.h"
11#include "core/hle/service/nwm/nwm_uds.h"
12#include "core/hle/service/nwm/uds_beacon.h"
14 13
15namespace Service { 14namespace Service {
16namespace NWM { 15namespace NWM {
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 319e8c946..39382ef09 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/file_sys/errors.h"
6#include "core/file_sys/file_backend.h" 7#include "core/file_sys/file_backend.h"
7#include "core/hle/service/fs/archive.h" 8#include "core/hle/service/fs/archive.h"
8#include "core/hle/service/ptm/ptm.h" 9#include "core/hle/service/ptm/ptm.h"
@@ -134,7 +135,7 @@ void Init() {
134 auto archive_result = 135 auto archive_result =
135 Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); 136 Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
136 // If the archive didn't exist, create the files inside 137 // If the archive didn't exist, create the files inside
137 if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { 138 if (archive_result.Code() == FileSys::ERR_NOT_FORMATTED) {
138 // Format the archive to create the directories 139 // Format the archive to create the directories
139 Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, 140 Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData,
140 FileSys::ArchiveFormatInfo(), archive_path); 141 FileSys::ArchiveFormatInfo(), archive_path);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 0672ac2e3..d34968428 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -2,11 +2,12 @@
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 <boost/range/algorithm_ext/erase.hpp> 5#include <fmt/format.h>
6
7#include "common/logging/log.h" 6#include "common/logging/log.h"
8#include "common/string_util.h" 7#include "common/string_util.h"
8#include "core/hle/kernel/client_port.h"
9#include "core/hle/kernel/server_port.h" 9#include "core/hle/kernel/server_port.h"
10#include "core/hle/kernel/server_session.h"
10#include "core/hle/service/ac/ac.h" 11#include "core/hle/service/ac/ac.h"
11#include "core/hle/service/act/act.h" 12#include "core/hle/service/act/act.h"
12#include "core/hle/service/am/am.h" 13#include "core/hle/service/am/am.h"
@@ -39,15 +40,20 @@
39#include "core/hle/service/ptm/ptm.h" 40#include "core/hle/service/ptm/ptm.h"
40#include "core/hle/service/qtm/qtm.h" 41#include "core/hle/service/qtm/qtm.h"
41#include "core/hle/service/service.h" 42#include "core/hle/service/service.h"
43#include "core/hle/service/sm/sm.h"
44#include "core/hle/service/sm/srv.h"
42#include "core/hle/service/soc_u.h" 45#include "core/hle/service/soc_u.h"
43#include "core/hle/service/srv.h"
44#include "core/hle/service/ssl_c.h" 46#include "core/hle/service/ssl_c.h"
45#include "core/hle/service/y2r_u.h" 47#include "core/hle/service/y2r_u.h"
46 48
49using Kernel::ClientPort;
50using Kernel::ServerPort;
51using Kernel::ServerSession;
52using Kernel::SharedPtr;
53
47namespace Service { 54namespace Service {
48 55
49std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; 56std::unordered_map<std::string, SharedPtr<ClientPort>> g_kernel_named_ports;
50std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_srv_services;
51 57
52/** 58/**
53 * Creates a function string for logging, complete with the name (or header code, depending 59 * Creates a function string for logging, complete with the name (or header code, depending
@@ -66,20 +72,10 @@ static std::string MakeFunctionString(const char* name, const char* port_name,
66 return function_string; 72 return function_string;
67} 73}
68 74
69void SessionRequestHandler::ClientConnected(
70 Kernel::SharedPtr<Kernel::ServerSession> server_session) {
71 connected_sessions.push_back(server_session);
72}
73
74void SessionRequestHandler::ClientDisconnected(
75 Kernel::SharedPtr<Kernel::ServerSession> server_session) {
76 boost::range::remove_erase(connected_sessions, server_session);
77}
78
79Interface::Interface(u32 max_sessions) : max_sessions(max_sessions) {} 75Interface::Interface(u32 max_sessions) : max_sessions(max_sessions) {}
80Interface::~Interface() = default; 76Interface::~Interface() = default;
81 77
82void Interface::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { 78void Interface::HandleSyncRequest(SharedPtr<ServerSession> server_session) {
83 // TODO(Subv): Make use of the server_session in the HLE service handlers to distinguish which 79 // TODO(Subv): Make use of the server_session in the HLE service handlers to distinguish which
84 // session triggered each command. 80 // session triggered each command.
85 81
@@ -113,27 +109,106 @@ void Interface::Register(const FunctionInfo* functions, size_t n) {
113} 109}
114 110
115//////////////////////////////////////////////////////////////////////////////////////////////////// 111////////////////////////////////////////////////////////////////////////////////////////////////////
112
113ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions,
114 InvokerFn* handler_invoker)
115 : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {}
116
117ServiceFrameworkBase::~ServiceFrameworkBase() = default;
118
119void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
120 ASSERT(port == nullptr);
121 port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
122 port->SetHleHandler(shared_from_this());
123}
124
125void ServiceFrameworkBase::InstallAsNamedPort() {
126 ASSERT(port == nullptr);
127 SharedPtr<ServerPort> server_port;
128 SharedPtr<ClientPort> client_port;
129 std::tie(server_port, client_port) = ServerPort::CreatePortPair(max_sessions, service_name);
130 server_port->SetHleHandler(shared_from_this());
131 AddNamedPort(service_name, std::move(client_port));
132}
133
134void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, size_t n) {
135 handlers.reserve(handlers.size() + n);
136 for (size_t i = 0; i < n; ++i) {
137 // Usually this array is sorted by id already, so hint to insert at the end
138 handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]);
139 }
140}
141
142void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info) {
143 IPC::Header header{cmd_buf[0]};
144 int num_params = header.normal_params_size + header.translate_params_size;
145 std::string function_name = info == nullptr ? fmt::format("{:#08x}", cmd_buf[0]) : info->name;
146
147 fmt::MemoryWriter w;
148 w.write("function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name,
149 cmd_buf[0]);
150 for (int i = 1; i <= num_params; ++i) {
151 w.write(", [{}]={:#x}", i, cmd_buf[i]);
152 }
153 w << '}';
154
155 LOG_ERROR(Service, "unknown / unimplemented %s", w.c_str());
156 // TODO(bunnei): Hack - ignore error
157 cmd_buf[1] = 0;
158}
159
160void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) {
161 u32* cmd_buf = Kernel::GetCommandBuffer();
162
163 // TODO(yuriks): The kernel should be the one handling this as part of translation after
164 // everything else is migrated
165 Kernel::HLERequestContext context;
166 context.cmd_buf = cmd_buf;
167 context.session = std::move(server_session);
168
169 u32 header_code = cmd_buf[0];
170 auto itr = handlers.find(header_code);
171 const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second;
172 if (info == nullptr || info->handler_callback == nullptr) {
173 return ReportUnimplementedFunction(cmd_buf, info);
174 }
175
176 LOG_TRACE(Service, "%s",
177 MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str());
178 handler_invoker(this, info->handler_callback, context);
179}
180
181////////////////////////////////////////////////////////////////////////////////////////////////////
116// Module interface 182// Module interface
117 183
184// TODO(yuriks): Move to kernel
185void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
186 g_kernel_named_ports.emplace(std::move(name), std::move(port));
187}
188
118static void AddNamedPort(Interface* interface_) { 189static void AddNamedPort(Interface* interface_) {
119 auto ports = 190 SharedPtr<ServerPort> server_port;
120 Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName(), 191 SharedPtr<ClientPort> client_port;
121 std::shared_ptr<Interface>(interface_)); 192 std::tie(server_port, client_port) =
122 auto client_port = std::get<Kernel::SharedPtr<Kernel::ClientPort>>(ports); 193 ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName());
123 g_kernel_named_ports.emplace(interface_->GetPortName(), std::move(client_port)); 194
195 server_port->SetHleHandler(std::shared_ptr<Interface>(interface_));
196 AddNamedPort(interface_->GetPortName(), std::move(client_port));
124} 197}
125 198
126void AddService(Interface* interface_) { 199void AddService(Interface* interface_) {
127 auto ports = 200 auto server_port =
128 Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName(), 201 SM::g_service_manager
129 std::shared_ptr<Interface>(interface_)); 202 ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions())
130 auto client_port = std::get<Kernel::SharedPtr<Kernel::ClientPort>>(ports); 203 .MoveFrom();
131 g_srv_services.emplace(interface_->GetPortName(), std::move(client_port)); 204 server_port->SetHleHandler(std::shared_ptr<Interface>(interface_));
132} 205}
133 206
134/// Initialize ServiceManager 207/// Initialize ServiceManager
135void Init() { 208void Init() {
136 AddNamedPort(new SRV::SRV); 209 SM::g_service_manager = std::make_shared<SM::ServiceManager>();
210 SM::ServiceManager::InstallInterfaces(SM::g_service_manager);
211
137 AddNamedPort(new ERR::ERR_F); 212 AddNamedPort(new ERR::ERR_F);
138 213
139 FS::ArchiveInit(); 214 FS::ArchiveInit();
@@ -194,7 +269,7 @@ void Shutdown() {
194 AC::Shutdown(); 269 AC::Shutdown();
195 FS::ArchiveShutdown(); 270 FS::ArchiveShutdown();
196 271
197 g_srv_services.clear(); 272 SM::g_service_manager = nullptr;
198 g_kernel_named_ports.clear(); 273 g_kernel_named_ports.clear();
199 LOG_DEBUG(Service, "shutdown OK"); 274 LOG_DEBUG(Service, "shutdown OK");
200} 275}
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index e6a5f1417..281ff99bb 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -8,70 +8,38 @@
8#include <string> 8#include <string>
9#include <unordered_map> 9#include <unordered_map>
10#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
11#include "common/bit_field.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "core/hle/ipc.h" 13#include "core/hle/kernel/hle_ipc.h"
13#include "core/hle/ipc_helpers.h" 14#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/client_port.h" 15
15#include "core/hle/kernel/thread.h" 16////////////////////////////////////////////////////////////////////////////////////////////////////
16#include "core/hle/result.h" 17// Namespace Service
17#include "core/memory.h"
18 18
19namespace Kernel { 19namespace Kernel {
20class ClientPort;
21class ServerPort;
20class ServerSession; 22class ServerSession;
21} 23}
22 24
23////////////////////////////////////////////////////////////////////////////////////////////////////
24// Namespace Service
25
26namespace Service { 25namespace Service {
27 26
27namespace SM {
28class ServiceManager;
29}
30
28static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) 31static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
29/// Arbitrary default number of maximum connections to an HLE service. 32/// Arbitrary default number of maximum connections to an HLE service.
30static const u32 DefaultMaxSessions = 10; 33static const u32 DefaultMaxSessions = 10;
31 34
32/** 35/**
33 * Interface implemented by HLE Session handlers.
34 * This can be provided to a ServerSession in order to hook into several relevant events
35 * (such as a new connection or a SyncRequest) so they can be implemented in the emulator.
36 */
37class SessionRequestHandler {
38public:
39 /**
40 * Handles a sync request from the emulated application.
41 * @param server_session The ServerSession that was triggered for this sync request,
42 * it should be used to differentiate which client (As in ClientSession) we're answering to.
43 * TODO(Subv): Use a wrapper structure to hold all the information relevant to
44 * this request (ServerSession, Originator thread, Translated command buffer, etc).
45 * @returns ResultCode the result code of the translate operation.
46 */
47 virtual void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) = 0;
48
49 /**
50 * Signals that a client has just connected to this HLE handler and keeps the
51 * associated ServerSession alive for the duration of the connection.
52 * @param server_session Owning pointer to the ServerSession associated with the connection.
53 */
54 void ClientConnected(Kernel::SharedPtr<Kernel::ServerSession> server_session);
55
56 /**
57 * Signals that a client has just disconnected from this HLE handler and releases the
58 * associated ServerSession.
59 * @param server_session ServerSession associated with the connection.
60 */
61 void ClientDisconnected(Kernel::SharedPtr<Kernel::ServerSession> server_session);
62
63protected:
64 /// List of sessions that are connected to this handler.
65 /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list
66 // for the duration of the connection.
67 std::vector<Kernel::SharedPtr<Kernel::ServerSession>> connected_sessions;
68};
69
70/**
71 * Framework for implementing HLE service handlers which dispatch incoming SyncRequests based on a 36 * Framework for implementing HLE service handlers which dispatch incoming SyncRequests based on a
72 * table mapping header ids to handler functions. 37 * table mapping header ids to handler functions.
38 *
39 * @deprecated Use ServiceFramework for new services instead. It allows services to be stateful and
40 * is more extensible going forward.
73 */ 41 */
74class Interface : public SessionRequestHandler { 42class Interface : public Kernel::SessionRequestHandler {
75public: 43public:
76 /** 44 /**
77 * Creates an HLE interface with the specified max sessions. 45 * Creates an HLE interface with the specified max sessions.
@@ -141,6 +109,146 @@ private:
141 boost::container::flat_map<u32, FunctionInfo> m_functions; 109 boost::container::flat_map<u32, FunctionInfo> m_functions;
142}; 110};
143 111
112/**
113 * This is an non-templated base of ServiceFramework to reduce code bloat and compilation times, it
114 * is not meant to be used directly.
115 *
116 * @see ServiceFramework
117 */
118class ServiceFrameworkBase : public Kernel::SessionRequestHandler {
119public:
120 /// Returns the string identifier used to connect to the service.
121 std::string GetServiceName() const {
122 return service_name;
123 }
124
125 /**
126 * Returns the maximum number of sessions that can be connected to this service at the same
127 * time.
128 */
129 u32 GetMaxSessions() const {
130 return max_sessions;
131 }
132
133 /// Creates a port pair and registers this service with the given ServiceManager.
134 void InstallAsService(SM::ServiceManager& service_manager);
135 /// Creates a port pair and registers it on the kernel's global port registry.
136 void InstallAsNamedPort();
137
138 void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override;
139
140protected:
141 /// Member-function pointer type of SyncRequest handlers.
142 template <typename Self>
143 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
144
145private:
146 template <typename T>
147 friend class ServiceFramework;
148
149 struct FunctionInfoBase {
150 u32 expected_header;
151 HandlerFnP<ServiceFrameworkBase> handler_callback;
152 const char* name;
153 };
154
155 using InvokerFn = void(ServiceFrameworkBase* object, HandlerFnP<ServiceFrameworkBase> member,
156 Kernel::HLERequestContext& ctx);
157
158 ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker);
159 ~ServiceFrameworkBase();
160
161 void RegisterHandlersBase(const FunctionInfoBase* functions, size_t n);
162 void ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info);
163
164 /// Identifier string used to connect to the service.
165 std::string service_name;
166 /// Maximum number of concurrent sessions that this service can handle.
167 u32 max_sessions;
168
169 /**
170 * Port where incoming connections will be received. Only created when InstallAsService() or
171 * InstallAsNamedPort() are called.
172 */
173 Kernel::SharedPtr<Kernel::ServerPort> port;
174
175 /// Function used to safely up-cast pointers to the derived class before invoking a handler.
176 InvokerFn* handler_invoker;
177 boost::container::flat_map<u32, FunctionInfoBase> handlers;
178};
179
180/**
181 * Framework for implementing HLE services. Dispatches on the header id of incoming SyncRequests
182 * based on a table mapping header ids to handler functions. Service implementations should inherit
183 * from ServiceFramework using the CRTP (`class Foo : public ServiceFramework<Foo> { ... };`) and
184 * populate it with handlers by calling #RegisterHandlers.
185 *
186 * In order to avoid duplicating code in the binary and exposing too many implementation details in
187 * the header, this class is split into a non-templated base (ServiceFrameworkBase) and a template
188 * deriving from it (ServiceFramework). The functions in this class will mostly only erase the type
189 * of the passed in function pointers and then delegate the actual work to the implementation in the
190 * base class.
191 */
192template <typename Self>
193class ServiceFramework : public ServiceFrameworkBase {
194protected:
195 /// Contains information about a request type which is handled by the service.
196 struct FunctionInfo : FunctionInfoBase {
197 // TODO(yuriks): This function could be constexpr, but clang is the only compiler that
198 // doesn't emit an ICE or a wrong diagnostic because of the static_cast.
199
200 /**
201 * Constructs a FunctionInfo for a function.
202 *
203 * @param expected_header request header in the command buffer which will trigger dispatch
204 * to this handler
205 * @param handler_callback member function in this service which will be called to handle
206 * the request
207 * @param name human-friendly name for the request. Used mostly for logging purposes.
208 */
209 FunctionInfo(u32 expected_header, HandlerFnP<Self> handler_callback, const char* name)
210 : FunctionInfoBase{
211 expected_header,
212 // Type-erase member function pointer by casting it down to the base class.
213 static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback), name} {}
214 };
215
216 /**
217 * Initializes the handler with no functions installed.
218 * @param max_sessions Maximum number of sessions that can be
219 * connected to this service at the same time.
220 */
221 ServiceFramework(const char* service_name, u32 max_sessions = DefaultMaxSessions)
222 : ServiceFrameworkBase(service_name, max_sessions, Invoker) {}
223
224 /// Registers handlers in the service.
225 template <size_t N>
226 void RegisterHandlers(const FunctionInfo (&functions)[N]) {
227 RegisterHandlers(functions, N);
228 }
229
230 /**
231 * Registers handlers in the service. Usually prefer using the other RegisterHandlers
232 * overload in order to avoid needing to specify the array size.
233 */
234 void RegisterHandlers(const FunctionInfo* functions, size_t n) {
235 RegisterHandlersBase(functions, n);
236 }
237
238private:
239 /**
240 * This function is used to allow invocation of pointers to handlers stored in the base class
241 * without needing to expose the type of this derived class. Pointers-to-member may require a
242 * fixup when being up or downcast, and thus code that does that needs to know the concrete type
243 * of the derived class in order to invoke one of it's functions through a pointer.
244 */
245 static void Invoker(ServiceFrameworkBase* object, HandlerFnP<ServiceFrameworkBase> member,
246 Kernel::HLERequestContext& ctx) {
247 // Cast back up to our original types and call the member function
248 (static_cast<Self*>(object)->*static_cast<HandlerFnP<Self>>(member))(ctx);
249 }
250};
251
144/// Initialize ServiceManager 252/// Initialize ServiceManager
145void Init(); 253void Init();
146 254
@@ -149,9 +257,9 @@ void Shutdown();
149 257
150/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC. 258/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC.
151extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; 259extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports;
152/// Map of services registered with the "srv:" service, retrieved using GetServiceHandle.
153extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_srv_services;
154 260
261/// Adds a port to the named port table
262void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port);
155/// Adds a service to the services table 263/// Adds a service to the services table
156void AddService(Interface* interface_); 264void AddService(Interface* interface_);
157 265
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
new file mode 100644
index 000000000..5e7fc68f9
--- /dev/null
+++ b/src/core/hle/service/sm/sm.cpp
@@ -0,0 +1,69 @@
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 <tuple>
6#include "common/assert.h"
7#include "core/hle/kernel/client_port.h"
8#include "core/hle/kernel/client_session.h"
9#include "core/hle/kernel/server_port.h"
10#include "core/hle/result.h"
11#include "core/hle/service/sm/sm.h"
12#include "core/hle/service/sm/srv.h"
13
14namespace Service {
15namespace SM {
16
17static ResultCode ValidateServiceName(const std::string& name) {
18 if (name.size() <= 0 || name.size() > 8) {
19 return ERR_INVALID_NAME_SIZE;
20 }
21 if (name.find('\0') != std::string::npos) {
22 return ERR_NAME_CONTAINS_NUL;
23 }
24 return RESULT_SUCCESS;
25}
26
27void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self) {
28 ASSERT(self->srv_interface.expired());
29
30 auto srv = std::make_shared<SRV>(self);
31 srv->InstallAsNamedPort();
32 self->srv_interface = srv;
33}
34
35ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService(
36 std::string name, unsigned int max_sessions) {
37
38 CASCADE_CODE(ValidateServiceName(name));
39 Kernel::SharedPtr<Kernel::ServerPort> server_port;
40 Kernel::SharedPtr<Kernel::ClientPort> client_port;
41 std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name);
42
43 registered_services.emplace(std::move(name), std::move(client_port));
44 return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port));
45}
46
47ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort(
48 const std::string& name) {
49
50 CASCADE_CODE(ValidateServiceName(name));
51 auto it = registered_services.find(name);
52 if (it == registered_services.end()) {
53 return ERR_SERVICE_NOT_REGISTERED;
54 }
55
56 return MakeResult<Kernel::SharedPtr<Kernel::ClientPort>>(it->second);
57}
58
59ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToService(
60 const std::string& name) {
61
62 CASCADE_RESULT(auto client_port, GetServicePort(name));
63 return client_port->Connect();
64}
65
66std::shared_ptr<ServiceManager> g_service_manager;
67
68} // namespace SM
69} // namespace Service
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
new file mode 100644
index 000000000..8f0dbf2db
--- /dev/null
+++ b/src/core/hle/service/sm/sm.h
@@ -0,0 +1,55 @@
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 <string>
8#include <unordered_map>
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/result.h"
11#include "core/hle/service/service.h"
12
13namespace Kernel {
14class ClientPort;
15class ClientSession;
16class ServerPort;
17class SessionRequestHandler;
18} // namespace Kernel
19
20namespace Service {
21namespace SM {
22
23class SRV;
24
25constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(1, ErrorModule::SRV, ErrorSummary::WouldBlock,
26 ErrorLevel::Temporary); // 0xD0406401
27constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(2, ErrorModule::SRV, ErrorSummary::WouldBlock,
28 ErrorLevel::Temporary); // 0xD0406402
29constexpr ResultCode ERR_INVALID_NAME_SIZE(5, ErrorModule::SRV, ErrorSummary::WrongArgument,
30 ErrorLevel::Permanent); // 0xD9006405
31constexpr ResultCode ERR_ACCESS_DENIED(6, ErrorModule::SRV, ErrorSummary::InvalidArgument,
32 ErrorLevel::Permanent); // 0xD8E06406
33constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument,
34 ErrorLevel::Permanent); // 0xD9006407
35
36class ServiceManager {
37public:
38 static void InstallInterfaces(std::shared_ptr<ServiceManager> self);
39
40 ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name,
41 unsigned int max_sessions);
42 ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name);
43 ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name);
44
45private:
46 std::weak_ptr<SRV> srv_interface;
47
48 /// Map of registered services, retrieved using GetServicePort or ConnectToService.
49 std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> registered_services;
50};
51
52extern std::shared_ptr<ServiceManager> g_service_manager;
53
54} // namespace SM
55} // namespace Service
diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp
new file mode 100644
index 000000000..b8b62b068
--- /dev/null
+++ b/src/core/hle/service/sm/srv.cpp
@@ -0,0 +1,204 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <tuple>
6
7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "core/hle/ipc.h"
10#include "core/hle/kernel/client_port.h"
11#include "core/hle/kernel/client_session.h"
12#include "core/hle/kernel/handle_table.h"
13#include "core/hle/kernel/semaphore.h"
14#include "core/hle/kernel/server_session.h"
15#include "core/hle/service/sm/sm.h"
16#include "core/hle/service/sm/srv.h"
17
18namespace Service {
19namespace SM {
20
21constexpr int MAX_PENDING_NOTIFICATIONS = 16;
22
23/**
24 * SRV::RegisterClient service function
25 * Inputs:
26 * 0: 0x00010002
27 * 1: ProcessId Header (must be 0x20)
28 * Outputs:
29 * 0: 0x00010040
30 * 1: ResultCode
31 */
32void SRV::RegisterClient(Kernel::HLERequestContext& ctx) {
33 u32* cmd_buff = ctx.CommandBuffer();
34
35 if (cmd_buff[1] != IPC::CallingPidDesc()) {
36 cmd_buff[0] = IPC::MakeHeader(0x0, 0x1, 0); // 0x40
37 cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw;
38 return;
39 }
40 cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); // 0x10040
41 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
42 LOG_WARNING(Service_SRV, "(STUBBED) called");
43}
44
45/**
46 * SRV::EnableNotification service function
47 * Inputs:
48 * 0: 0x00020000
49 * Outputs:
50 * 0: 0x00020042
51 * 1: ResultCode
52 * 2: Translation descriptor: 0x20
53 * 3: Handle to semaphore signaled on process notification
54 */
55void SRV::EnableNotification(Kernel::HLERequestContext& ctx) {
56 u32* cmd_buff = ctx.CommandBuffer();
57
58 notification_semaphore =
59 Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap();
60
61 cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0x2); // 0x20042
62 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
63 cmd_buff[2] = IPC::CopyHandleDesc(1);
64 cmd_buff[3] = Kernel::g_handle_table.Create(notification_semaphore).MoveFrom();
65 LOG_WARNING(Service_SRV, "(STUBBED) called");
66}
67
68/**
69 * SRV::GetServiceHandle service function
70 * Inputs:
71 * 0: 0x00050100
72 * 1-2: 8-byte UTF-8 service name
73 * 3: Name length
74 * 4: Flags (bit0: if not set, return port-handle if session-handle unavailable)
75 * Outputs:
76 * 1: ResultCode
77 * 3: Service handle
78 */
79void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
80 ResultCode res = RESULT_SUCCESS;
81 u32* cmd_buff = ctx.CommandBuffer();
82
83 size_t name_len = cmd_buff[3];
84 if (name_len > Service::kMaxPortSize) {
85 cmd_buff[1] = ERR_INVALID_NAME_SIZE.raw;
86 LOG_ERROR(Service_SRV, "called name_len=0x%X, failed with code=0x%08X", name_len,
87 cmd_buff[1]);
88 return;
89 }
90 std::string name(reinterpret_cast<const char*>(&cmd_buff[1]), name_len);
91 bool return_port_on_failure = (cmd_buff[4] & 1) == 0;
92
93 // TODO(yuriks): Permission checks go here
94
95 auto client_port = service_manager->GetServicePort(name);
96 if (client_port.Failed()) {
97 cmd_buff[1] = client_port.Code().raw;
98 LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(),
99 cmd_buff[1]);
100 return;
101 }
102
103 auto session = client_port.Unwrap()->Connect();
104 cmd_buff[1] = session.Code().raw;
105 if (session.Succeeded()) {
106 cmd_buff[3] = Kernel::g_handle_table.Create(session.MoveFrom()).MoveFrom();
107 LOG_DEBUG(Service_SRV, "called service=%s, session handle=0x%08X", name.c_str(),
108 cmd_buff[3]);
109 } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) {
110 cmd_buff[1] = ERR_MAX_CONNECTIONS_REACHED.raw;
111 cmd_buff[3] = Kernel::g_handle_table.Create(client_port.MoveFrom()).MoveFrom();
112 LOG_WARNING(Service_SRV, "called service=%s, *port* handle=0x%08X", name.c_str(),
113 cmd_buff[3]);
114 } else {
115 LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(),
116 cmd_buff[1]);
117 }
118}
119
120/**
121 * SRV::Subscribe service function
122 * Inputs:
123 * 0: 0x00090040
124 * 1: Notification ID
125 * Outputs:
126 * 0: 0x00090040
127 * 1: ResultCode
128 */
129void SRV::Subscribe(Kernel::HLERequestContext& ctx) {
130 u32* cmd_buff = ctx.CommandBuffer();
131
132 u32 notification_id = cmd_buff[1];
133
134 cmd_buff[0] = IPC::MakeHeader(0x9, 0x1, 0); // 0x90040
135 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
136 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id);
137}
138
139/**
140 * SRV::Unsubscribe service function
141 * Inputs:
142 * 0: 0x000A0040
143 * 1: Notification ID
144 * Outputs:
145 * 0: 0x000A0040
146 * 1: ResultCode
147 */
148void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) {
149 u32* cmd_buff = ctx.CommandBuffer();
150
151 u32 notification_id = cmd_buff[1];
152
153 cmd_buff[0] = IPC::MakeHeader(0xA, 0x1, 0); // 0xA0040
154 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
155 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id);
156}
157
158/**
159 * SRV::PublishToSubscriber service function
160 * Inputs:
161 * 0: 0x000C0080
162 * 1: Notification ID
163 * 2: Flags (bit0: only fire if not fired, bit1: report errors)
164 * Outputs:
165 * 0: 0x000C0040
166 * 1: ResultCode
167 */
168void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) {
169 u32* cmd_buff = ctx.CommandBuffer();
170
171 u32 notification_id = cmd_buff[1];
172 u8 flags = cmd_buff[2] & 0xFF;
173
174 cmd_buff[0] = IPC::MakeHeader(0xC, 0x1, 0); // 0xC0040
175 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
176 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id,
177 flags);
178}
179
180SRV::SRV(std::shared_ptr<ServiceManager> service_manager)
181 : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) {
182 static const FunctionInfo functions[] = {
183 {0x00010002, &SRV::RegisterClient, "RegisterClient"},
184 {0x00020000, &SRV::EnableNotification, "EnableNotification"},
185 {0x00030100, nullptr, "RegisterService"},
186 {0x000400C0, nullptr, "UnregisterService"},
187 {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"},
188 {0x000600C2, nullptr, "RegisterPort"},
189 {0x000700C0, nullptr, "UnregisterPort"},
190 {0x00080100, nullptr, "GetPort"},
191 {0x00090040, &SRV::Subscribe, "Subscribe"},
192 {0x000A0040, &SRV::Unsubscribe, "Unsubscribe"},
193 {0x000B0000, nullptr, "ReceiveNotification"},
194 {0x000C0080, &SRV::PublishToSubscriber, "PublishToSubscriber"},
195 {0x000D0040, nullptr, "PublishAndGetSubscriber"},
196 {0x000E00C0, nullptr, "IsServiceRegistered"},
197 };
198 RegisterHandlers(functions);
199}
200
201SRV::~SRV() = default;
202
203} // namespace SM
204} // namespace Service
diff --git a/src/core/hle/service/sm/srv.h b/src/core/hle/service/sm/srv.h
new file mode 100644
index 000000000..75cca5184
--- /dev/null
+++ b/src/core/hle/service/sm/srv.h
@@ -0,0 +1,37 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/service/service.h"
9
10namespace Kernel {
11class HLERequestContext;
12class Semaphore;
13}
14
15namespace Service {
16namespace SM {
17
18/// Interface to "srv:" service
19class SRV final : public ServiceFramework<SRV> {
20public:
21 explicit SRV(std::shared_ptr<ServiceManager> service_manager);
22 ~SRV();
23
24private:
25 void RegisterClient(Kernel::HLERequestContext& ctx);
26 void EnableNotification(Kernel::HLERequestContext& ctx);
27 void GetServiceHandle(Kernel::HLERequestContext& ctx);
28 void Subscribe(Kernel::HLERequestContext& ctx);
29 void Unsubscribe(Kernel::HLERequestContext& ctx);
30 void PublishToSubscriber(Kernel::HLERequestContext& ctx);
31
32 std::shared_ptr<ServiceManager> service_manager;
33 Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore;
34};
35
36} // namespace SM
37} // namespace Service
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index 530614e6f..3d215d42d 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -11,6 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/scope_exit.h" 13#include "common/scope_exit.h"
14#include "core/hle/ipc.h"
14#include "core/hle/kernel/server_session.h" 15#include "core/hle/kernel/server_session.h"
15#include "core/hle/result.h" 16#include "core/hle/result.h"
16#include "core/hle/service/soc_u.h" 17#include "core/hle/service/soc_u.h"
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
deleted file mode 100644
index 3bd787147..000000000
--- a/src/core/hle/service/srv.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <tuple>
6
7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "core/hle/kernel/client_session.h"
10#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/server_session.h"
12#include "core/hle/service/srv.h"
13
14namespace Service {
15namespace SRV {
16
17static Kernel::SharedPtr<Kernel::Event> event_handle;
18
19/**
20 * SRV::RegisterClient service function
21 * Inputs:
22 * 0: 0x00010002
23 * 1: ProcessId Header (must be 0x20)
24 * Outputs:
25 * 0: 0x00010040
26 * 1: ResultCode
27 */
28static void RegisterClient(Interface* self) {
29 u32* cmd_buff = Kernel::GetCommandBuffer();
30
31 if (cmd_buff[1] != IPC::CallingPidDesc()) {
32 cmd_buff[0] = IPC::MakeHeader(0x0, 0x1, 0); // 0x40
33 cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS,
34 ErrorSummary::WrongArgument, ErrorLevel::Permanent)
35 .raw;
36 return;
37 }
38 cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); // 0x10040
39 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
40 LOG_WARNING(Service_SRV, "(STUBBED) called");
41}
42
43/**
44 * SRV::EnableNotification service function
45 * Inputs:
46 * 0: 0x00020000
47 * Outputs:
48 * 0: 0x00020042
49 * 1: ResultCode
50 * 2: Translation descriptor: 0x20
51 * 3: Handle to semaphore signaled on process notification
52 */
53static void EnableNotification(Interface* self) {
54 u32* cmd_buff = Kernel::GetCommandBuffer();
55
56 // TODO(bunnei): Change to a semaphore once these have been implemented
57 event_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "SRV:Event");
58 event_handle->Clear();
59
60 cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0x2); // 0x20042
61 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
62 cmd_buff[2] = IPC::CopyHandleDesc(1);
63 cmd_buff[3] = Kernel::g_handle_table.Create(event_handle).MoveFrom();
64 LOG_WARNING(Service_SRV, "(STUBBED) called");
65}
66
67/**
68 * SRV::GetServiceHandle service function
69 * Inputs:
70 * 0: 0x00050100
71 * 1-2: 8-byte UTF-8 service name
72 * 3: Name length
73 * 4: Flags (bit0: if not set, return port-handle if session-handle unavailable)
74 * Outputs:
75 * 1: ResultCode
76 * 3: Service handle
77 */
78static void GetServiceHandle(Interface* self) {
79 ResultCode res = RESULT_SUCCESS;
80 u32* cmd_buff = Kernel::GetCommandBuffer();
81
82 std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
83 auto it = Service::g_srv_services.find(port_name);
84
85 if (it != Service::g_srv_services.end()) {
86 auto client_port = it->second;
87
88 auto client_session = client_port->Connect();
89 res = client_session.Code();
90
91 if (client_session.Succeeded()) {
92 // Return the client session
93 cmd_buff[3] = Kernel::g_handle_table.Create(*client_session).MoveFrom();
94 }
95 LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
96 } else {
97 LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
98 res = UnimplementedFunction(ErrorModule::SRV);
99 }
100 cmd_buff[1] = res.raw;
101}
102
103/**
104 * SRV::Subscribe service function
105 * Inputs:
106 * 0: 0x00090040
107 * 1: Notification ID
108 * Outputs:
109 * 0: 0x00090040
110 * 1: ResultCode
111 */
112static void Subscribe(Interface* self) {
113 u32* cmd_buff = Kernel::GetCommandBuffer();
114
115 u32 notification_id = cmd_buff[1];
116
117 cmd_buff[0] = IPC::MakeHeader(0x9, 0x1, 0); // 0x90040
118 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
119 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id);
120}
121
122/**
123 * SRV::Unsubscribe service function
124 * Inputs:
125 * 0: 0x000A0040
126 * 1: Notification ID
127 * Outputs:
128 * 0: 0x000A0040
129 * 1: ResultCode
130 */
131static void Unsubscribe(Interface* self) {
132 u32* cmd_buff = Kernel::GetCommandBuffer();
133
134 u32 notification_id = cmd_buff[1];
135
136 cmd_buff[0] = IPC::MakeHeader(0xA, 0x1, 0); // 0xA0040
137 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
138 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id);
139}
140
141/**
142 * SRV::PublishToSubscriber service function
143 * Inputs:
144 * 0: 0x000C0080
145 * 1: Notification ID
146 * 2: Flags (bit0: only fire if not fired, bit1: report errors)
147 * Outputs:
148 * 0: 0x000C0040
149 * 1: ResultCode
150 */
151static void PublishToSubscriber(Interface* self) {
152 u32* cmd_buff = Kernel::GetCommandBuffer();
153
154 u32 notification_id = cmd_buff[1];
155 u8 flags = cmd_buff[2] & 0xFF;
156
157 cmd_buff[0] = IPC::MakeHeader(0xC, 0x1, 0); // 0xC0040
158 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
159 LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id,
160 flags);
161}
162
163const Interface::FunctionInfo FunctionTable[] = {
164 {0x00010002, RegisterClient, "RegisterClient"},
165 {0x00020000, EnableNotification, "EnableNotification"},
166 {0x00030100, nullptr, "RegisterService"},
167 {0x000400C0, nullptr, "UnregisterService"},
168 {0x00050100, GetServiceHandle, "GetServiceHandle"},
169 {0x000600C2, nullptr, "RegisterPort"},
170 {0x000700C0, nullptr, "UnregisterPort"},
171 {0x00080100, nullptr, "GetPort"},
172 {0x00090040, Subscribe, "Subscribe"},
173 {0x000A0040, Unsubscribe, "Unsubscribe"},
174 {0x000B0000, nullptr, "ReceiveNotification"},
175 {0x000C0080, PublishToSubscriber, "PublishToSubscriber"},
176 {0x000D0040, nullptr, "PublishAndGetSubscriber"},
177 {0x000E00C0, nullptr, "IsServiceRegistered"},
178};
179
180SRV::SRV() {
181 Register(FunctionTable);
182 event_handle = nullptr;
183}
184
185SRV::~SRV() {
186 event_handle = nullptr;
187}
188
189} // namespace SRV
190} // namespace Service
diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h
deleted file mode 100644
index d3a9de879..000000000
--- a/src/core/hle/service/srv.h
+++ /dev/null
@@ -1,24 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Service {
10namespace SRV {
11
12/// Interface to "srv:" service
13class SRV final : public Interface {
14public:
15 SRV();
16 ~SRV() override;
17
18 std::string GetPortName() const override {
19 return "srv:";
20 }
21};
22
23} // namespace SRV
24} // namespace Service
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp
index 09ced9d7a..300acca75 100644
--- a/src/core/hle/service/ssl_c.cpp
+++ b/src/core/hle/service/ssl_c.cpp
@@ -4,7 +4,9 @@
4 4
5#include <random> 5#include <random>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/ipc.h"
7#include "core/hle/service/ssl_c.h" 8#include "core/hle/service/ssl_c.h"
9#include "core/memory.h"
8 10
9namespace Service { 11namespace Service {
10namespace SSL { 12namespace SSL {
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index c0837d49d..bb7bf2d67 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -6,6 +6,8 @@
6#include "common/common_funcs.h" 6#include "common/common_funcs.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/hle/ipc.h"
10#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/event.h" 11#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
11#include "core/hle/service/y2r_u.h" 13#include "core/hle/service/y2r_u.h"
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 8538cfc9d..e68b9f16a 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.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 <cinttypes> 6#include <cinttypes>
6#include <map> 7#include <map>
7#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -14,7 +15,9 @@
14#include "core/hle/kernel/address_arbiter.h" 15#include "core/hle/kernel/address_arbiter.h"
15#include "core/hle/kernel/client_port.h" 16#include "core/hle/kernel/client_port.h"
16#include "core/hle/kernel/client_session.h" 17#include "core/hle/kernel/client_session.h"
18#include "core/hle/kernel/errors.h"
17#include "core/hle/kernel/event.h" 19#include "core/hle/kernel/event.h"
20#include "core/hle/kernel/handle_table.h"
18#include "core/hle/kernel/memory.h" 21#include "core/hle/kernel/memory.h"
19#include "core/hle/kernel/mutex.h" 22#include "core/hle/kernel/mutex.h"
20#include "core/hle/kernel/process.h" 23#include "core/hle/kernel/process.h"
@@ -26,6 +29,7 @@
26#include "core/hle/kernel/thread.h" 29#include "core/hle/kernel/thread.h"
27#include "core/hle/kernel/timer.h" 30#include "core/hle/kernel/timer.h"
28#include "core/hle/kernel/vm_manager.h" 31#include "core/hle/kernel/vm_manager.h"
32#include "core/hle/kernel/wait_object.h"
29#include "core/hle/result.h" 33#include "core/hle/result.h"
30#include "core/hle/service/service.h" 34#include "core/hle/service/service.h"
31 35
@@ -37,25 +41,6 @@ using Kernel::ERR_INVALID_HANDLE;
37 41
38namespace SVC { 42namespace SVC {
39 43
40const ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel,
41 ErrorSummary::NotFound, ErrorLevel::Permanent); // 0xD88007FA
42const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS,
43 ErrorSummary::InvalidArgument,
44 ErrorLevel::Usage); // 0xE0E0181E
45
46const ResultCode ERR_SYNC_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS,
47 ErrorSummary::StatusChanged, ErrorLevel::Info);
48
49const ResultCode ERR_MISALIGNED_ADDRESS{// 0xE0E01BF1
50 ErrorDescription::MisalignedAddress, ErrorModule::OS,
51 ErrorSummary::InvalidArgument, ErrorLevel::Usage};
52const ResultCode ERR_MISALIGNED_SIZE{// 0xE0E01BF2
53 ErrorDescription::MisalignedSize, ErrorModule::OS,
54 ErrorSummary::InvalidArgument, ErrorLevel::Usage};
55const ResultCode ERR_INVALID_COMBINATION{// 0xE0E01BEE
56 ErrorDescription::InvalidCombination, ErrorModule::OS,
57 ErrorSummary::InvalidArgument, ErrorLevel::Usage};
58
59enum ControlMemoryOperation { 44enum ControlMemoryOperation {
60 MEMOP_FREE = 1, 45 MEMOP_FREE = 1,
61 MEMOP_RESERVE = 2, // This operation seems to be unsupported in the kernel 46 MEMOP_RESERVE = 2, // This operation seems to be unsupported in the kernel
@@ -195,8 +180,7 @@ static ResultCode MapMemoryBlock(Kernel::Handle handle, u32 addr, u32 permission
195 LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); 180 LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
196 } 181 }
197 182
198 return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, 183 return Kernel::ERR_INVALID_COMBINATION;
199 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
200} 184}
201 185
202static ResultCode UnmapMemoryBlock(Kernel::Handle handle, u32 addr) { 186static ResultCode UnmapMemoryBlock(Kernel::Handle handle, u32 addr) {
@@ -216,16 +200,16 @@ static ResultCode UnmapMemoryBlock(Kernel::Handle handle, u32 addr) {
216/// Connect to an OS service given the port name, returns the handle to the port to out 200/// Connect to an OS service given the port name, returns the handle to the port to out
217static ResultCode ConnectToPort(Kernel::Handle* out_handle, const char* port_name) { 201static ResultCode ConnectToPort(Kernel::Handle* out_handle, const char* port_name) {
218 if (port_name == nullptr) 202 if (port_name == nullptr)
219 return ERR_NOT_FOUND; 203 return Kernel::ERR_NOT_FOUND;
220 if (std::strlen(port_name) > 11) 204 if (std::strlen(port_name) > 11)
221 return ERR_PORT_NAME_TOO_LONG; 205 return Kernel::ERR_PORT_NAME_TOO_LONG;
222 206
223 LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name); 207 LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name);
224 208
225 auto it = Service::g_kernel_named_ports.find(port_name); 209 auto it = Service::g_kernel_named_ports.find(port_name);
226 if (it == Service::g_kernel_named_ports.end()) { 210 if (it == Service::g_kernel_named_ports.end()) {
227 LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name); 211 LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name);
228 return ERR_NOT_FOUND; 212 return Kernel::ERR_NOT_FOUND;
229 } 213 }
230 214
231 auto client_port = it->second; 215 auto client_port = it->second;
@@ -263,7 +247,7 @@ static ResultCode CloseHandle(Kernel::Handle handle) {
263 247
264/// Wait for a handle to synchronize, timeout after the specified nanoseconds 248/// Wait for a handle to synchronize, timeout after the specified nanoseconds
265static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) { 249static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) {
266 auto object = Kernel::g_handle_table.GetWaitObject(handle); 250 auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handle);
267 Kernel::Thread* thread = Kernel::GetCurrentThread(); 251 Kernel::Thread* thread = Kernel::GetCurrentThread();
268 252
269 if (object == nullptr) 253 if (object == nullptr)
@@ -275,7 +259,7 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds)
275 if (object->ShouldWait(thread)) { 259 if (object->ShouldWait(thread)) {
276 260
277 if (nano_seconds == 0) 261 if (nano_seconds == 0)
278 return ERR_SYNC_TIMEOUT; 262 return Kernel::RESULT_TIMEOUT;
279 263
280 thread->wait_objects = {object}; 264 thread->wait_objects = {object};
281 object->AddWaitingThread(thread); 265 object->AddWaitingThread(thread);
@@ -289,7 +273,7 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds)
289 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread 273 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread
290 // resumes due to a signal in its wait objects. 274 // resumes due to a signal in its wait objects.
291 // Otherwise we retain the default value of timeout. 275 // Otherwise we retain the default value of timeout.
292 return ERR_SYNC_TIMEOUT; 276 return Kernel::RESULT_TIMEOUT;
293 } 277 }
294 278
295 object->Acquire(thread); 279 object->Acquire(thread);
@@ -304,8 +288,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
304 288
305 // Check if 'handles' is invalid 289 // Check if 'handles' is invalid
306 if (handles == nullptr) 290 if (handles == nullptr)
307 return ResultCode(ErrorDescription::InvalidPointer, ErrorModule::Kernel, 291 return Kernel::ERR_INVALID_POINTER;
308 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
309 292
310 // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If 293 // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If
311 // this happens, the running application will crash. 294 // this happens, the running application will crash.
@@ -313,14 +296,13 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
313 296
314 // Check if 'handle_count' is invalid 297 // Check if 'handle_count' is invalid
315 if (handle_count < 0) 298 if (handle_count < 0)
316 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, 299 return Kernel::ERR_OUT_OF_RANGE;
317 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
318 300
319 using ObjectPtr = Kernel::SharedPtr<Kernel::WaitObject>; 301 using ObjectPtr = Kernel::SharedPtr<Kernel::WaitObject>;
320 std::vector<ObjectPtr> objects(handle_count); 302 std::vector<ObjectPtr> objects(handle_count);
321 303
322 for (int i = 0; i < handle_count; ++i) { 304 for (int i = 0; i < handle_count; ++i) {
323 auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); 305 auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]);
324 if (object == nullptr) 306 if (object == nullptr)
325 return ERR_INVALID_HANDLE; 307 return ERR_INVALID_HANDLE;
326 objects[i] = object; 308 objects[i] = object;
@@ -344,7 +326,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
344 // If a timeout value of 0 was provided, just return the Timeout error code instead of 326 // If a timeout value of 0 was provided, just return the Timeout error code instead of
345 // suspending the thread. 327 // suspending the thread.
346 if (nano_seconds == 0) 328 if (nano_seconds == 0)
347 return ERR_SYNC_TIMEOUT; 329 return Kernel::RESULT_TIMEOUT;
348 330
349 // Put the thread to sleep 331 // Put the thread to sleep
350 thread->status = THREADSTATUS_WAIT_SYNCH_ALL; 332 thread->status = THREADSTATUS_WAIT_SYNCH_ALL;
@@ -365,7 +347,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
365 *out = -1; 347 *out = -1;
366 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to 348 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to
367 // a signal in one of its wait objects. 349 // a signal in one of its wait objects.
368 return ERR_SYNC_TIMEOUT; 350 return Kernel::RESULT_TIMEOUT;
369 } else { 351 } else {
370 // Find the first object that is acquirable in the provided list of objects 352 // Find the first object that is acquirable in the provided list of objects
371 auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { 353 auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) {
@@ -385,7 +367,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
385 // If a timeout value of 0 was provided, just return the Timeout error code instead of 367 // If a timeout value of 0 was provided, just return the Timeout error code instead of
386 // suspending the thread. 368 // suspending the thread.
387 if (nano_seconds == 0) 369 if (nano_seconds == 0)
388 return ERR_SYNC_TIMEOUT; 370 return Kernel::RESULT_TIMEOUT;
389 371
390 // Put the thread to sleep 372 // Put the thread to sleep
391 thread->status = THREADSTATUS_WAIT_SYNCH_ANY; 373 thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
@@ -411,7 +393,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
411 // Otherwise we retain the default value of timeout, and -1 in the out parameter 393 // Otherwise we retain the default value of timeout, and -1 in the out parameter
412 thread->wait_set_output = true; 394 thread->wait_set_output = true;
413 *out = -1; 395 *out = -1;
414 return ERR_SYNC_TIMEOUT; 396 return Kernel::RESULT_TIMEOUT;
415 } 397 }
416} 398}
417 399
@@ -520,22 +502,20 @@ static ResultCode GetResourceLimitLimitValues(s64* values, Kernel::Handle resour
520} 502}
521 503
522/// Creates a new thread 504/// Creates a new thread
523static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 entry_point, u32 arg, 505static ResultCode CreateThread(Kernel::Handle* out_handle, u32 priority, u32 entry_point, u32 arg,
524 u32 stack_top, s32 processor_id) { 506 u32 stack_top, s32 processor_id) {
525 using Kernel::Thread; 507 using Kernel::Thread;
526 508
527 std::string name = Common::StringFromFormat("unknown-%08" PRIX32, entry_point); 509 std::string name = Common::StringFromFormat("unknown-%08" PRIX32, entry_point);
528 510
529 if (priority > THREADPRIO_LOWEST) { 511 if (priority > THREADPRIO_LOWEST) {
530 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, 512 return Kernel::ERR_OUT_OF_RANGE;
531 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
532 } 513 }
533 514
534 using Kernel::ResourceLimit; 515 using Kernel::ResourceLimit;
535 Kernel::SharedPtr<ResourceLimit>& resource_limit = Kernel::g_current_process->resource_limit; 516 Kernel::SharedPtr<ResourceLimit>& resource_limit = Kernel::g_current_process->resource_limit;
536 if (resource_limit->GetMaxResourceValue(Kernel::ResourceTypes::PRIORITY) > priority) { 517 if (resource_limit->GetMaxResourceValue(Kernel::ResourceTypes::PRIORITY) > priority) {
537 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::OS, 518 return Kernel::ERR_NOT_AUTHORIZED;
538 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
539 } 519 }
540 520
541 switch (processor_id) { 521 switch (processor_id) {
@@ -605,8 +585,7 @@ static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) {
605/// Sets the priority for the specified thread 585/// Sets the priority for the specified thread
606static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { 586static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) {
607 if (priority > THREADPRIO_LOWEST) { 587 if (priority > THREADPRIO_LOWEST) {
608 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, 588 return Kernel::ERR_OUT_OF_RANGE;
609 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
610 } 589 }
611 590
612 SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); 591 SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
@@ -618,8 +597,7 @@ static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) {
618 // the one from the thread owner's resource limit. 597 // the one from the thread owner's resource limit.
619 Kernel::SharedPtr<ResourceLimit>& resource_limit = Kernel::g_current_process->resource_limit; 598 Kernel::SharedPtr<ResourceLimit>& resource_limit = Kernel::g_current_process->resource_limit;
620 if (resource_limit->GetMaxResourceValue(Kernel::ResourceTypes::PRIORITY) > priority) { 599 if (resource_limit->GetMaxResourceValue(Kernel::ResourceTypes::PRIORITY) > priority) {
621 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::OS, 600 return Kernel::ERR_NOT_AUTHORIZED;
622 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
623 } 601 }
624 602
625 thread->SetPriority(priority); 603 thread->SetPriority(priority);
@@ -743,8 +721,7 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_inf
743 auto vma = process->vm_manager.FindVMA(addr); 721 auto vma = process->vm_manager.FindVMA(addr);
744 722
745 if (vma == Kernel::g_current_process->vm_manager.vma_map.end()) 723 if (vma == Kernel::g_current_process->vm_manager.vma_map.end())
746 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, 724 return Kernel::ERR_INVALID_ADDRESS;
747 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
748 725
749 memory_info->base_address = vma->second.base; 726 memory_info->base_address = vma->second.base;
750 memory_info->permission = static_cast<u32>(vma->second.permissions); 727 memory_info->permission = static_cast<u32>(vma->second.permissions);
@@ -842,8 +819,7 @@ static ResultCode SetTimer(Kernel::Handle handle, s64 initial, s64 interval) {
842 LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); 819 LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
843 820
844 if (initial < 0 || interval < 0) { 821 if (initial < 0 || interval < 0) {
845 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, 822 return Kernel::ERR_OUT_OF_RANGE_KERNEL;
846 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
847 } 823 }
848 824
849 SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); 825 SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
@@ -902,8 +878,7 @@ static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 si
902 using Kernel::SharedMemory; 878 using Kernel::SharedMemory;
903 879
904 if (size % Memory::PAGE_SIZE != 0) 880 if (size % Memory::PAGE_SIZE != 0)
905 return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, 881 return Kernel::ERR_MISALIGNED_SIZE;
906 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
907 882
908 SharedPtr<SharedMemory> shared_memory = nullptr; 883 SharedPtr<SharedMemory> shared_memory = nullptr;
909 884
@@ -924,16 +899,14 @@ static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 si
924 899
925 if (!VerifyPermissions(static_cast<MemoryPermission>(my_permission)) || 900 if (!VerifyPermissions(static_cast<MemoryPermission>(my_permission)) ||
926 !VerifyPermissions(static_cast<MemoryPermission>(other_permission))) 901 !VerifyPermissions(static_cast<MemoryPermission>(other_permission)))
927 return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, 902 return Kernel::ERR_INVALID_COMBINATION;
928 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
929 903
930 // TODO(Subv): Processes with memory type APPLICATION are not allowed 904 // TODO(Subv): Processes with memory type APPLICATION are not allowed
931 // to create memory blocks with addr = 0, any attempts to do so 905 // to create memory blocks with addr = 0, any attempts to do so
932 // should return error 0xD92007EA. 906 // should return error 0xD92007EA.
933 if ((addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) && 907 if ((addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) &&
934 addr != 0) { 908 addr != 0) {
935 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, 909 return Kernel::ERR_INVALID_ADDRESS;
936 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
937 } 910 }
938 911
939 // When trying to create a memory block with address = 0, 912 // When trying to create a memory block with address = 0,
@@ -1035,7 +1008,7 @@ static ResultCode GetProcessInfo(s64* out, Kernel::Handle process_handle, u32 ty
1035 *out = process->heap_used + process->linear_heap_used + process->misc_memory_used; 1008 *out = process->heap_used + process->linear_heap_used + process->misc_memory_used;
1036 if (*out % Memory::PAGE_SIZE != 0) { 1009 if (*out % Memory::PAGE_SIZE != 0) {
1037 LOG_ERROR(Kernel_SVC, "called, memory size not page-aligned"); 1010 LOG_ERROR(Kernel_SVC, "called, memory size not page-aligned");
1038 return ERR_MISALIGNED_SIZE; 1011 return Kernel::ERR_MISALIGNED_SIZE;
1039 } 1012 }
1040 break; 1013 break;
1041 case 1: 1014 case 1:
@@ -1051,19 +1024,15 @@ static ResultCode GetProcessInfo(s64* out, Kernel::Handle process_handle, u32 ty
1051 case 20: 1024 case 20:
1052 *out = Memory::FCRAM_PADDR - process->GetLinearHeapBase(); 1025 *out = Memory::FCRAM_PADDR - process->GetLinearHeapBase();
1053 break; 1026 break;
1027 case 21:
1028 case 22:
1029 case 23:
1030 // These return a different error value than higher invalid values
1031 LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type=%u", type);
1032 return Kernel::ERR_NOT_IMPLEMENTED;
1054 default: 1033 default:
1055 LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type=%u", type); 1034 LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type=%u", type);
1056 1035 return Kernel::ERR_INVALID_ENUM_VALUE;
1057 if (type >= 21 && type <= 23) {
1058 return ResultCode( // 0xE0E01BF4
1059 ErrorDescription::NotImplemented, ErrorModule::OS, ErrorSummary::InvalidArgument,
1060 ErrorLevel::Usage);
1061 } else {
1062 return ResultCode( // 0xD8E007ED
1063 ErrorDescription::InvalidEnumValue, ErrorModule::Kernel,
1064 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
1065 }
1066 break;
1067 } 1036 }
1068 1037
1069 return RESULT_SUCCESS; 1038 return RESULT_SUCCESS;
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 1d80766ae..48bbf687d 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -8,6 +8,7 @@
8#include <initializer_list> 8#include <initializer_list>
9#include <memory> 9#include <memory>
10#include <string> 10#include <string>
11#include <utility>
11#include <vector> 12#include <vector>
12#include <boost/optional.hpp> 13#include <boost/optional.hpp>
13#include "common/common_types.h" 14#include "common/common_types.h"
@@ -100,11 +101,11 @@ public:
100 * Loads the system mode that this application needs. 101 * Loads the system mode that this application needs.
101 * This function defaults to 2 (96MB allocated to the application) if it can't read the 102 * This function defaults to 2 (96MB allocated to the application) if it can't read the
102 * information. 103 * information.
103 * @returns Optional with the kernel system mode 104 * @returns A pair with the optional system mode, and and the status.
104 */ 105 */
105 virtual boost::optional<u32> LoadKernelSystemMode() { 106 virtual std::pair<boost::optional<u32>, ResultStatus> LoadKernelSystemMode() {
106 // 96MB allocated to the application. 107 // 96MB allocated to the application.
107 return 2; 108 return std::make_pair(2, ResultStatus::Success);
108 } 109 }
109 110
110 /** 111 /**
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index beeb13ffa..ffc019560 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -121,12 +121,16 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
121 return FileType::Error; 121 return FileType::Error;
122} 122}
123 123
124boost::optional<u32> AppLoader_NCCH::LoadKernelSystemMode() { 124std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() {
125 if (!is_loaded) { 125 if (!is_loaded) {
126 if (LoadExeFS() != ResultStatus::Success) 126 ResultStatus res = LoadExeFS();
127 return boost::none; 127 if (res != ResultStatus::Success) {
128 return std::make_pair(boost::none, res);
129 }
128 } 130 }
129 return exheader_header.arm11_system_local_caps.system_mode.Value(); 131 // Set the system mode as the one from the exheader.
132 return std::make_pair(exheader_header.arm11_system_local_caps.system_mode.Value(),
133 ResultStatus::Success);
130} 134}
131 135
132ResultStatus AppLoader_NCCH::LoadExec() { 136ResultStatus AppLoader_NCCH::LoadExec() {
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index 4ef95b5c6..0ebd47fd5 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -179,9 +179,9 @@ public:
179 179
180 /** 180 /**
181 * Loads the Exheader and returns the system mode for this application. 181 * Loads the Exheader and returns the system mode for this application.
182 * @return Optional with the kernel system mode 182 * @returns A pair with the optional system mode, and and the status.
183 */ 183 */
184 boost::optional<u32> LoadKernelSystemMode() override; 184 std::pair<boost::optional<u32>, ResultStatus> LoadKernelSystemMode() override;
185 185
186 ResultStatus ReadCode(std::vector<u8>& buffer) override; 186 ResultStatus ReadCode(std::vector<u8>& buffer) override;
187 187
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index d2e7c6b97..d4f0429d1 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -6,7 +6,7 @@
6#include "core/gdbstub/gdbstub.h" 6#include "core/gdbstub/gdbstub.h"
7#include "core/hle/service/hid/hid.h" 7#include "core/hle/service/hid/hid.h"
8#include "core/hle/service/ir/ir.h" 8#include "core/hle/service/ir/ir.h"
9#include "settings.h" 9#include "core/settings.h"
10#include "video_core/video_core.h" 10#include "video_core/video_core.h"
11 11
12#include "core/frontend/emu_window.h" 12#include "core/frontend/emu_window.h"
diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp
index 276a5b288..55b3b5efc 100644
--- a/src/core/tracer/recorder.cpp
+++ b/src/core/tracer/recorder.cpp
@@ -6,7 +6,7 @@
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/file_util.h" 7#include "common/file_util.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "recorder.h" 9#include "core/tracer/recorder.h"
10 10
11namespace CiTrace { 11namespace CiTrace {
12 12
diff --git a/src/core/tracer/recorder.h b/src/core/tracer/recorder.h
index aea363b95..39e6ec4fd 100644
--- a/src/core/tracer/recorder.h
+++ b/src/core/tracer/recorder.h
@@ -8,8 +8,8 @@
8#include <unordered_map> 8#include <unordered_map>
9#include <vector> 9#include <vector>
10#include <boost/crc.hpp> 10#include <boost/crc.hpp>
11#include "citrace.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/tracer/citrace.h"
13 13
14namespace CiTrace { 14namespace CiTrace {
15 15
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index cfe5caaa3..e3e36ada7 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -13,15 +13,14 @@ set(HEADERS
13if(SDL2_FOUND) 13if(SDL2_FOUND)
14 set(SRCS ${SRCS} sdl/sdl.cpp) 14 set(SRCS ${SRCS} sdl/sdl.cpp)
15 set(HEADERS ${HEADERS} sdl/sdl.h) 15 set(HEADERS ${HEADERS} sdl/sdl.h)
16 include_directories(${SDL2_INCLUDE_DIR})
17endif() 16endif()
18 17
19create_directory_groups(${SRCS} ${HEADERS}) 18create_directory_groups(${SRCS} ${HEADERS})
20 19
21add_library(input_common STATIC ${SRCS} ${HEADERS}) 20add_library(input_common STATIC ${SRCS} ${HEADERS})
22target_link_libraries(input_common common core) 21target_link_libraries(input_common PUBLIC core PRIVATE common)
23 22
24if(SDL2_FOUND) 23if(SDL2_FOUND)
25 target_link_libraries(input_common ${SDL2_LIBRARY}) 24 target_link_libraries(input_common PRIVATE SDL2)
26 set_property(TARGET input_common APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2) 25 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
27endif() 26endif()
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index d1144ba77..00d7c636a 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -10,10 +10,9 @@ set(HEADERS
10 10
11create_directory_groups(${SRCS} ${HEADERS}) 11create_directory_groups(${SRCS} ${HEADERS})
12 12
13include_directories(../../externals/catch/single_include/)
14
15add_executable(tests ${SRCS} ${HEADERS}) 13add_executable(tests ${SRCS} ${HEADERS})
16target_link_libraries(tests core video_core audio_core common) 14target_link_libraries(tests PRIVATE common core)
17target_link_libraries(tests ${PLATFORM_LIBRARIES} Threads::Threads) 15target_link_libraries(tests PRIVATE glad) # To support linker work-around
16target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads)
18 17
19add_test(NAME tests COMMAND $<TARGET_FILE:tests>) 18add_test(NAME tests COMMAND tests)
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index e00b88f71..0961a3251 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -79,13 +79,14 @@ endif()
79create_directory_groups(${SRCS} ${HEADERS}) 79create_directory_groups(${SRCS} ${HEADERS})
80 80
81add_library(video_core STATIC ${SRCS} ${HEADERS}) 81add_library(video_core STATIC ${SRCS} ${HEADERS})
82target_link_libraries(video_core glad) 82target_link_libraries(video_core PUBLIC common core)
83target_link_libraries(video_core PRIVATE glad nihstro-headers)
84
83if (ARCHITECTURE_x86_64) 85if (ARCHITECTURE_x86_64)
84 target_link_libraries(video_core xbyak) 86 target_link_libraries(video_core PRIVATE xbyak)
85endif() 87endif()
86 88
87if (PNG_FOUND) 89if (PNG_FOUND)
88 target_link_libraries(video_core ${PNG_LIBRARIES}) 90 target_link_libraries(video_core PRIVATE PNG::PNG)
89 include_directories(${PNG_INCLUDE_DIRS}) 91 target_compile_definitions(video_core PRIVATE HAVE_PNG)
90 add_definitions(${PNG_DEFINITIONS})
91endif() 92endif()
diff --git a/src/video_core/regs_texturing.h b/src/video_core/regs_texturing.h
index e4038b41b..3f5355fa9 100644
--- a/src/video_core/regs_texturing.h
+++ b/src/video_core/regs_texturing.h
@@ -133,7 +133,32 @@ struct TexturingRegs {
133 BitField<16, 1, u32> clear_texture_cache; // TODO: unimplemented 133 BitField<16, 1, u32> clear_texture_cache; // TODO: unimplemented
134 } main_config; 134 } main_config;
135 TextureConfig texture0; 135 TextureConfig texture0;
136 INSERT_PADDING_WORDS(0x8); 136
137 enum class CubeFace {
138 PositiveX = 0,
139 NegativeX = 1,
140 PositiveY = 2,
141 NegativeY = 3,
142 PositiveZ = 4,
143 NegativeZ = 5,
144 };
145
146 BitField<0, 22, u32> cube_address[5];
147
148 PAddr GetCubePhysicalAddress(CubeFace face) const {
149 PAddr address = texture0.address;
150 if (face != CubeFace::PositiveX) {
151 // Bits [22:27] from the main texture address is shared with all cubemap additional
152 // addresses.
153 auto& face_addr = cube_address[static_cast<size_t>(face) - 1];
154 address &= ~face_addr.mask;
155 address |= face_addr;
156 }
157 // A multiplier of 8 is also needed in the same way as the main address.
158 return address * 8;
159 }
160
161 INSERT_PADDING_WORDS(0x3);
137 BitField<0, 4, TextureFormat> texture0_format; 162 BitField<0, 4, TextureFormat> texture0_format;
138 BitField<0, 1, u32> fragment_lighting_enable; 163 BitField<0, 1, u32> fragment_lighting_enable;
139 INSERT_PADDING_WORDS(0x1); 164 INSERT_PADDING_WORDS(0x1);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 456443e86..8b717e43d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -561,20 +561,16 @@ RasterizerCacheOpenGL::GetFramebufferSurfaces(
561 color_params.is_tiled = depth_params.is_tiled = true; 561 color_params.is_tiled = depth_params.is_tiled = true;
562 562
563 // Set the internal resolution, assume the same scaling factor for top and bottom screens 563 // Set the internal resolution, assume the same scaling factor for top and bottom screens
564 const Layout::FramebufferLayout& layout = VideoCore::g_emu_window->GetFramebufferLayout(); 564 float resolution_scale_factor = Settings::values.resolution_factor;
565 if (Settings::values.resolution_factor == 0.0f) { 565 if (resolution_scale_factor == 0.0f) {
566 // Auto - scale resolution to the window size 566 // Auto - scale resolution to the window size
567 color_params.res_scale_width = depth_params.res_scale_width = 567 resolution_scale_factor = VideoCore::g_emu_window->GetFramebufferLayout().GetScalingRatio();
568 (float)layout.top_screen.GetWidth() / VideoCore::kScreenTopWidth;
569 color_params.res_scale_height = depth_params.res_scale_height =
570 (float)layout.top_screen.GetHeight() / VideoCore::kScreenTopHeight;
571 } else {
572 // Otherwise, scale the resolution by the specified factor
573 color_params.res_scale_width = Settings::values.resolution_factor;
574 depth_params.res_scale_width = Settings::values.resolution_factor;
575 color_params.res_scale_height = Settings::values.resolution_factor;
576 depth_params.res_scale_height = Settings::values.resolution_factor;
577 } 568 }
569 // Scale the resolution by the specified factor
570 color_params.res_scale_width = resolution_scale_factor;
571 depth_params.res_scale_width = resolution_scale_factor;
572 color_params.res_scale_height = resolution_scale_factor;
573 depth_params.res_scale_height = resolution_scale_factor;
578 574
579 color_params.addr = config.GetColorBufferPhysicalAddress(); 575 color_params.addr = config.GetColorBufferPhysicalAddress();
580 color_params.pixel_format = CachedSurface::PixelFormatFromColorFormat(config.color_format); 576 color_params.pixel_format = CachedSurface::PixelFormatFromColorFormat(config.color_format);
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 3eeb47d33..db53710aa 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -152,12 +152,40 @@ static bool IsPassThroughTevStage(const TevStageConfig& stage) {
152 stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1); 152 stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1);
153} 153}
154 154
155static std::string TexCoord(const PicaShaderConfig& config, int texture_unit) { 155static std::string SampleTexture(const PicaShaderConfig& config, unsigned texture_unit) {
156 if (texture_unit == 2 && config.state.texture2_use_coord1) { 156 const auto& state = config.state;
157 return "texcoord[1]"; 157 switch (texture_unit) {
158 case 0:
159 // Only unit 0 respects the texturing type
160 switch (state.texture0_type) {
161 case TexturingRegs::TextureConfig::Texture2D:
162 return "texture(tex[0], texcoord[0])";
163 case TexturingRegs::TextureConfig::Projection2D:
164 return "textureProj(tex[0], vec3(texcoord[0], texcoord0_w))";
165 default:
166 LOG_CRITICAL(HW_GPU, "Unhandled texture type %x",
167 static_cast<int>(state.texture0_type));
168 UNIMPLEMENTED();
169 return "texture(tex[0], texcoord[0])";
170 }
171 case 1:
172 return "texture(tex[1], texcoord[1])";
173 case 2:
174 if (state.texture2_use_coord1)
175 return "texture(tex[2], texcoord[1])";
176 else
177 return "texture(tex[2], texcoord[2])";
178 case 3:
179 if (state.proctex.enable) {
180 return "ProcTex()";
181 } else {
182 LOG_ERROR(Render_OpenGL, "Using Texture3 without enabling it");
183 return "vec4(0.0)";
184 }
185 default:
186 UNREACHABLE();
187 return "";
158 } 188 }
159
160 return "texcoord[" + std::to_string(texture_unit) + "]";
161} 189}
162 190
163/// Writes the specified TEV stage source component(s) 191/// Writes the specified TEV stage source component(s)
@@ -176,35 +204,16 @@ static void AppendSource(std::string& out, const PicaShaderConfig& config,
176 out += "secondary_fragment_color"; 204 out += "secondary_fragment_color";
177 break; 205 break;
178 case Source::Texture0: 206 case Source::Texture0:
179 // Only unit 0 respects the texturing type (according to 3DBrew) 207 out += SampleTexture(config, 0);
180 switch (state.texture0_type) {
181 case TexturingRegs::TextureConfig::Texture2D:
182 out += "texture(tex[0], texcoord[0])";
183 break;
184 case TexturingRegs::TextureConfig::Projection2D:
185 out += "textureProj(tex[0], vec3(texcoord[0], texcoord0_w))";
186 break;
187 default:
188 out += "texture(tex[0], texcoord[0])";
189 LOG_CRITICAL(HW_GPU, "Unhandled texture type %x",
190 static_cast<int>(state.texture0_type));
191 UNIMPLEMENTED();
192 break;
193 }
194 break; 208 break;
195 case Source::Texture1: 209 case Source::Texture1:
196 out += "texture(tex[1], texcoord[1])"; 210 out += SampleTexture(config, 1);
197 break; 211 break;
198 case Source::Texture2: 212 case Source::Texture2:
199 out += "texture(tex[2], " + TexCoord(config, 2) + ")"; 213 out += SampleTexture(config, 2);
200 break; 214 break;
201 case Source::Texture3: 215 case Source::Texture3:
202 if (config.state.proctex.enable) { 216 out += SampleTexture(config, 3);
203 out += "ProcTex()";
204 } else {
205 LOG_ERROR(Render_OpenGL, "Using Texture3 without enabling it");
206 out += "vec4(0.0)";
207 }
208 break; 217 break;
209 case Source::PreviousBuffer: 218 case Source::PreviousBuffer:
210 out += "combiner_buffer"; 219 out += "combiner_buffer";
@@ -515,18 +524,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
515 if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { 524 if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) {
516 // Bump mapping is enabled using a normal map, read perturbation vector from the selected 525 // Bump mapping is enabled using a normal map, read perturbation vector from the selected
517 // texture 526 // texture
518 if (lighting.bump_selector == 3) { 527 out += "vec3 surface_normal = 2.0 * (" + SampleTexture(config, lighting.bump_selector) +
519 if (config.state.proctex.enable) { 528 ").rgb - 1.0;\n";
520 out += "vec3 surface_normal = 2.0 * ProcTex().rgb - 1.0;\n";
521 } else {
522 LOG_ERROR(Render_OpenGL, "Using Texture3 without enabling it");
523 out += "vec3 surface_normal = vec3(-1.0);\n";
524 }
525 } else {
526 std::string bump_selector = std::to_string(lighting.bump_selector);
527 out += "vec3 surface_normal = 2.0 * texture(tex[" + bump_selector + "], " +
528 TexCoord(config, lighting.bump_selector) + ").rgb - 1.0;\n";
529 }
530 529
531 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher 530 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher
532 // precision result 531 // precision result
@@ -545,8 +544,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
545 } 544 }
546 545
547 // Rotate the surface-local normal by the interpolated normal quaternion to convert it to 546 // Rotate the surface-local normal by the interpolated normal quaternion to convert it to
548 // eyespace 547 // eyespace.
549 out += "vec3 normal = normalize(quaternion_rotate(normquat, surface_normal));\n"; 548 out += "vec3 normal = quaternion_rotate(normalize(normquat), surface_normal);\n";
550 549
551 // Gets the index into the specified lookup table for specular lighting 550 // Gets the index into the specified lookup table for specular lighting
552 auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input, 551 auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input,
@@ -1030,7 +1029,9 @@ uniform sampler1D proctex_diff_lut;
1030// Rotate the vector v by the quaternion q 1029// Rotate the vector v by the quaternion q
1031vec3 quaternion_rotate(vec4 q, vec3 v) { 1030vec3 quaternion_rotate(vec4 q, vec3 v) {
1032 return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); 1031 return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
1033})"; 1032}
1033
1034)";
1034 1035
1035 if (config.state.proctex.enable) 1036 if (config.state.proctex.enable)
1036 AppendProcTexSampler(out, config); 1037 AppendProcTexSampler(out, config);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index e19375466..d90c776f9 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -94,14 +94,8 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
94 return matrix; 94 return matrix;
95} 95}
96 96
97/// RendererOpenGL constructor 97RendererOpenGL::RendererOpenGL() = default;
98RendererOpenGL::RendererOpenGL() { 98RendererOpenGL::~RendererOpenGL() = default;
99 resolution_width = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
100 resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight;
101}
102
103/// RendererOpenGL destructor
104RendererOpenGL::~RendererOpenGL() {}
105 99
106/// Swap buffers (render frame) 100/// Swap buffers (render frame)
107void RendererOpenGL::SwapBuffers() { 101void RendererOpenGL::SwapBuffers() {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 87c556cff..0b4f69e8f 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -68,9 +68,6 @@ private:
68 68
69 EmuWindow* render_window; ///< Handle to render window 69 EmuWindow* render_window; ///< Handle to render window
70 70
71 int resolution_width; ///< Current resolution width
72 int resolution_height; ///< Current resolution height
73
74 OpenGLState state; 71 OpenGLState state;
75 72
76 // OpenGL object IDs 73 // OpenGL object IDs
diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp
index e9edf0360..8b7b1defb 100644
--- a/src/video_core/swrasterizer/rasterizer.cpp
+++ b/src/video_core/swrasterizer/rasterizer.cpp
@@ -5,6 +5,7 @@
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <cmath> 7#include <cmath>
8#include <tuple>
8#include "common/assert.h" 9#include "common/assert.h"
9#include "common/bit_field.h" 10#include "common/bit_field.h"
10#include "common/color.h" 11#include "common/color.h"
@@ -70,6 +71,49 @@ static int SignedArea(const Math::Vec2<Fix12P4>& vtx1, const Math::Vec2<Fix12P4>
70 return Math::Cross(vec1, vec2).z; 71 return Math::Cross(vec1, vec2).z;
71}; 72};
72 73
74/// Convert a 3D vector for cube map coordinates to 2D texture coordinates along with the face name
75static std::tuple<float24, float24, PAddr> ConvertCubeCoord(float24 u, float24 v, float24 w,
76 const TexturingRegs& regs) {
77 const float abs_u = std::abs(u.ToFloat32());
78 const float abs_v = std::abs(v.ToFloat32());
79 const float abs_w = std::abs(w.ToFloat32());
80 float24 x, y, z;
81 PAddr addr;
82 if (abs_u > abs_v && abs_u > abs_w) {
83 if (u > float24::FromFloat32(0)) {
84 addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::PositiveX);
85 y = -v;
86 } else {
87 addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::NegativeX);
88 y = v;
89 }
90 x = -w;
91 z = u;
92 } else if (abs_v > abs_w) {
93 if (v > float24::FromFloat32(0)) {
94 addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::PositiveY);
95 x = u;
96 } else {
97 addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::NegativeY);
98 x = -u;
99 }
100 y = w;
101 z = v;
102 } else {
103 if (w > float24::FromFloat32(0)) {
104 addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::PositiveZ);
105 y = -v;
106 } else {
107 addr = regs.GetCubePhysicalAddress(TexturingRegs::CubeFace::NegativeZ);
108 y = v;
109 }
110 x = u;
111 z = w;
112 }
113 const float24 half = float24::FromFloat32(0.5f);
114 return std::make_tuple(x / z * half + half, y / z * half + half, addr);
115}
116
73MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240)); 117MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240));
74 118
75/** 119/**
@@ -284,10 +328,16 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
284 328
285 // Only unit 0 respects the texturing type (according to 3DBrew) 329 // Only unit 0 respects the texturing type (according to 3DBrew)
286 // TODO: Refactor so cubemaps and shadowmaps can be handled 330 // TODO: Refactor so cubemaps and shadowmaps can be handled
331 PAddr texture_address = texture.config.GetPhysicalAddress();
287 if (i == 0) { 332 if (i == 0) {
288 switch (texture.config.type) { 333 switch (texture.config.type) {
289 case TexturingRegs::TextureConfig::Texture2D: 334 case TexturingRegs::TextureConfig::Texture2D:
290 break; 335 break;
336 case TexturingRegs::TextureConfig::TextureCube: {
337 auto w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w);
338 std::tie(u, v, texture_address) = ConvertCubeCoord(u, v, w, regs.texturing);
339 break;
340 }
291 case TexturingRegs::TextureConfig::Projection2D: { 341 case TexturingRegs::TextureConfig::Projection2D: {
292 auto tc0_w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w); 342 auto tc0_w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w);
293 u /= tc0_w; 343 u /= tc0_w;
@@ -322,8 +372,7 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
322 t = texture.config.height - 1 - 372 t = texture.config.height - 1 -
323 GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height); 373 GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height);
324 374
325 u8* texture_data = 375 const u8* texture_data = Memory::GetPhysicalPointer(texture_address);
326 Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress());
327 auto info = 376 auto info =
328 Texture::TextureInfo::FromPicaRegister(texture.config, texture.format); 377 Texture::TextureInfo::FromPicaRegister(texture.config, texture.format);
329 378
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 4aba19ca0..94e0867f0 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -15,21 +15,6 @@ class RendererBase;
15 15
16namespace VideoCore { 16namespace VideoCore {
17 17
18// 3DS Video Constants
19// -------------------
20
21// NOTE: The LCDs actually rotate the image 90 degrees when displaying. Because of that the
22// framebuffers in video memory are stored in column-major order and rendered sideways, causing
23// the widths and heights of the framebuffers read by the LCD to be switched compared to the
24// heights and widths of the screens listed here.
25static const int kScreenTopWidth = 400; ///< 3DS top screen width
26static const int kScreenTopHeight = 240; ///< 3DS top screen height
27static const int kScreenBottomWidth = 320; ///< 3DS bottom screen width
28static const int kScreenBottomHeight = 240; ///< 3DS bottom screen height
29
30// Video core renderer
31// ---------------------
32
33extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin 18extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
34extern EmuWindow* g_emu_window; ///< Emu window 19extern EmuWindow* g_emu_window; ///< Emu window
35 20