summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt11
-rw-r--r--README.md4
-rw-r--r--externals/CMakeLists.txt4
-rw-r--r--externals/cryptopp/CMakeLists.txt5
m---------externals/dynarmic0
-rw-r--r--src/citra/citra.cpp3
-rw-r--r--src/citra/config.cpp6
-rw-r--r--src/citra_qt/configuration/config.cpp6
-rw-r--r--src/citra_qt/configuration/configure_debug.ui7
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/function_wrappers.h15
-rw-r--r--src/core/hle/kernel/errors.h5
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp24
-rw-r--r--src/core/hle/kernel/hle_ipc.h7
-rw-r--r--src/core/hle/kernel/memory.cpp6
-rw-r--r--src/core/hle/kernel/process.cpp2
-rw-r--r--src/core/hle/kernel/server_port.cpp12
-rw-r--r--src/core/hle/kernel/server_port.h11
-rw-r--r--src/core/hle/kernel/server_session.cpp2
-rw-r--r--src/core/hle/kernel/thread.cpp4
-rw-r--r--src/core/hle/kernel/timer.cpp2
-rw-r--r--src/core/hle/result.h7
-rw-r--r--src/core/hle/service/apt/apt.cpp12
-rw-r--r--src/core/hle/service/cam/cam.cpp6
-rw-r--r--src/core/hle/service/cecd/cecd.cpp8
-rw-r--r--src/core/hle/service/cfg/cfg.cpp4
-rw-r--r--src/core/hle/service/csnd_snd.cpp4
-rw-r--r--src/core/hle/service/dsp_dsp.cpp2
-rw-r--r--src/core/hle/service/fs/archive.cpp4
-rw-r--r--src/core/hle/service/fs/fs_user.cpp6
-rw-r--r--src/core/hle/service/gsp_gpu.cpp4
-rw-r--r--src/core/hle/service/hid/hid.cpp12
-rw-r--r--src/core/hle/service/ir/ir_rst.cpp4
-rw-r--r--src/core/hle/service/ir/ir_user.cpp6
-rw-r--r--src/core/hle/service/mic_u.cpp2
-rw-r--r--src/core/hle/service/nfc/nfc.cpp4
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp117
-rw-r--r--src/core/hle/service/nwm/uds_data.cpp278
-rw-r--r--src/core/hle/service/nwm/uds_data.h78
-rw-r--r--src/core/hle/service/ptm/ptm.cpp2
-rw-r--r--src/core/hle/service/service.cpp5
-rw-r--r--src/core/hle/service/sm/srv.cpp4
-rw-r--r--src/core/hle/service/y2r_u.cpp2
-rw-r--r--src/core/hle/svc.cpp45
-rw-r--r--src/core/hw/aes/key.h2
-rw-r--r--src/core/hw/gpu.cpp41
-rw-r--r--src/core/hw/gpu.h2
-rw-r--r--src/core/memory.h4
-rw-r--r--src/tests/CMakeLists.txt5
-rw-r--r--src/tests/core/hle/kernel/hle_ipc.cpp216
-rw-r--r--src/video_core/pica_state.h20
-rw-r--r--src/video_core/regs_lighting.h6
-rw-r--r--src/video_core/regs_texturing.h8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp151
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp189
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h2
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp46
-rw-r--r--src/video_core/renderer_opengl/gl_state.h29
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h13
-rw-r--r--src/video_core/shader/shader_jit_x64_compiler.cpp26
-rw-r--r--src/video_core/swrasterizer/rasterizer.cpp23
-rw-r--r--src/video_core/swrasterizer/texturing.cpp19
63 files changed, 1235 insertions, 331 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1f0af2d41..4668d4bea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -79,6 +79,8 @@ else()
79 add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS) 79 add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS)
80 # Avoid windows.h junk 80 # Avoid windows.h junk
81 add_definitions(/DNOMINMAX) 81 add_definitions(/DNOMINMAX)
82 # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
83 add_definitions(/DWIN32_LEAN_AND_MEAN)
82 84
83 # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms) 85 # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)
84 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 86 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
@@ -92,10 +94,13 @@ else()
92 # /W3 - Level 3 warnings 94 # /W3 - Level 3 warnings
93 # /MP - Multi-threaded compilation 95 # /MP - Multi-threaded compilation
94 # /Zi - Output debugging information 96 # /Zi - Output debugging information
95 # /Zo - enahnced debug info for optimized builds 97 # /Zo - enhanced debug info for optimized builds
96 set(CMAKE_C_FLAGS "/W3 /MP /Zi /Zo" CACHE STRING "" FORCE) 98 # /permissive- - enables stricter C++ standards conformance checks
99 set(CMAKE_C_FLAGS "/W3 /MP /Zi /Zo /permissive-" CACHE STRING "" FORCE)
97 # /EHsc - C++-only exception handling semantics 100 # /EHsc - C++-only exception handling semantics
98 set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHsc" CACHE STRING "" FORCE) 101 # /Zc:throwingNew - let codegen assume `operator new` will never return null
102 # /Zc:inline - let codegen omit inline functions in object files
103 set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHsc /Zc:throwingNew,inline" CACHE STRING "" FORCE)
99 104
100 # /MDd - Multi-threaded Debug Runtime DLL 105 # /MDd - Multi-threaded Debug Runtime DLL
101 set(CMAKE_C_FLAGS_DEBUG "/Od /MDd" CACHE STRING "" FORCE) 106 set(CMAKE_C_FLAGS_DEBUG "/Od /MDd" CACHE STRING "" FORCE)
diff --git a/README.md b/README.md
index 8de62d0ef..e766918f7 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ Citra Emulator
5[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg?branch=master)](https://travis-ci.org/citra-emu/citra) 5[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg?branch=master)](https://travis-ci.org/citra-emu/citra)
6[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/sdf1o4kh3g1e68m9?svg=true)](https://ci.appveyor.com/project/bunnei/citra) 6[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/sdf1o4kh3g1e68m9?svg=true)](https://ci.appveyor.com/project/bunnei/citra)
7 7
8Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward. 8Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS. Citra only emulates a subset of 3DS hardware and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward.
9 9
10Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://citra-emu.org/wiki/faq/) before getting started with the project. 10Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://citra-emu.org/wiki/faq/) before getting started with the project.
11 11
@@ -27,7 +27,7 @@ If you want to contribute please take a look at the [Contributor's Guide](CONTRI
27 27
28 28
29### Support 29### Support
30We happily accept monetary donations, or donated games and hardware. Please see our [donations page](https://citra-emu.org/donate/) for more information on how you can contribute to Citra. Any donations received will go towards things like: 30We happily accept monetary donations or donated games and hardware. Please see our [donations page](https://citra-emu.org/donate/) for more information on how you can contribute to Citra. Any donations received will go towards things like:
31* 3DS consoles for developers to explore the hardware 31* 3DS consoles for developers to explore the hardware
32* 3DS games for testing 32* 3DS games for testing
33* Any equipment required for homebrew 33* Any equipment required for homebrew
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 1e04931ee..02e02350c 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -46,7 +46,5 @@ if (ARCHITECTURE_x86_64)
46 # Defined before "dynarmic" above 46 # Defined before "dynarmic" above
47 # add_library(xbyak INTERFACE) 47 # add_library(xbyak INTERFACE)
48 target_include_directories(xbyak INTERFACE ./xbyak/xbyak) 48 target_include_directories(xbyak INTERFACE ./xbyak/xbyak)
49 if (NOT MSVC) 49 target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
50 target_compile_options(xbyak INTERFACE -fno-operator-names)
51 endif()
52endif() 50endif()
diff --git a/externals/cryptopp/CMakeLists.txt b/externals/cryptopp/CMakeLists.txt
index 864de18bb..8a626e44a 100644
--- a/externals/cryptopp/CMakeLists.txt
+++ b/externals/cryptopp/CMakeLists.txt
@@ -44,6 +44,11 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
44 add_definitions(-wd68 -wd186 -wd279 -wd327 -wd161 -wd3180) 44 add_definitions(-wd68 -wd186 -wd279 -wd327 -wd161 -wd3180)
45endif() 45endif()
46 46
47if(MSVC)
48 # Disable C4390: empty controlled statement found: is this the intent?
49 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4390")
50endif()
51
47# Endianness 52# Endianness
48TEST_BIG_ENDIAN(IS_BIG_ENDIAN) 53TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
49if(IS_BIG_ENDIAN) 54if(IS_BIG_ENDIAN)
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 7707ff13e981b0aecf87f3156ee0b641469f7bb Subproject 8f15e3f70cb96e56705e5de6ba97b5d09423a56
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index dd357ff72..14574e56c 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -18,7 +18,10 @@
18#endif 18#endif
19 19
20#ifdef _WIN32 20#ifdef _WIN32
21// windows.h needs to be included before shellapi.h
21#include <windows.h> 22#include <windows.h>
23
24#include <shellapi.h>
22#endif 25#endif
23 26
24#include "citra/config.h" 27#include "citra/config.h"
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index f08b4069c..957d8dc86 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -88,9 +88,9 @@ void Config::ReadValues() {
88 Settings::values.toggle_framelimit = 88 Settings::values.toggle_framelimit =
89 sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); 89 sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true);
90 90
91 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); 91 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
92 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); 92 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
93 Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0); 93 Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
94 94
95 // Layout 95 // Layout
96 Settings::values.layout_option = 96 Settings::values.layout_option =
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 2b99447ec..64ffc9152 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -70,9 +70,9 @@ void Config::ReadValues() {
70 Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); 70 Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool();
71 Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); 71 Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool();
72 72
73 Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); 73 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
74 Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); 74 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
75 Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); 75 Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat();
76 qt_config->endGroup(); 76 qt_config->endGroup();
77 77
78 qt_config->beginGroup("Layout"); 78 qt_config->beginGroup("Layout");
diff --git a/src/citra_qt/configuration/configure_debug.ui b/src/citra_qt/configuration/configure_debug.ui
index bbbb0e3f4..96638ebdb 100644
--- a/src/citra_qt/configuration/configure_debug.ui
+++ b/src/citra_qt/configuration/configure_debug.ui
@@ -23,6 +23,13 @@
23 </property> 23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_2"> 24 <layout class="QVBoxLayout" name="verticalLayout_2">
25 <item> 25 <item>
26 <widget class="QLabel">
27 <property name="text">
28 <string>The GDB Stub only works correctly when the CPU JIT is off.</string>
29 </property>
30 </widget>
31 </item>
32 <item>
26 <layout class="QHBoxLayout" name="horizontalLayout_3"> 33 <layout class="QHBoxLayout" name="horizontalLayout_3">
27 <item> 34 <item>
28 <widget class="QCheckBox" name="toggle_gdbstub"> 35 <widget class="QCheckBox" name="toggle_gdbstub">
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b16a89990..ea09819e5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -144,6 +144,7 @@ set(SRCS
144 hle/service/nwm/nwm_tst.cpp 144 hle/service/nwm/nwm_tst.cpp
145 hle/service/nwm/nwm_uds.cpp 145 hle/service/nwm/nwm_uds.cpp
146 hle/service/nwm/uds_beacon.cpp 146 hle/service/nwm/uds_beacon.cpp
147 hle/service/nwm/uds_data.cpp
147 hle/service/pm_app.cpp 148 hle/service/pm_app.cpp
148 hle/service/ptm/ptm.cpp 149 hle/service/ptm/ptm.cpp
149 hle/service/ptm/ptm_gets.cpp 150 hle/service/ptm/ptm_gets.cpp
@@ -341,6 +342,7 @@ set(HEADERS
341 hle/service/nwm/nwm_tst.h 342 hle/service/nwm/nwm_tst.h
342 hle/service/nwm/nwm_uds.h 343 hle/service/nwm/nwm_uds.h
343 hle/service/nwm/uds_beacon.h 344 hle/service/nwm/uds_beacon.h
345 hle/service/nwm/uds_data.h
344 hle/service/pm_app.h 346 hle/service/pm_app.h
345 hle/service/ptm/ptm.h 347 hle/service/ptm/ptm.h
346 hle/service/ptm/ptm_gets.h 348 hle/service/ptm/ptm_gets.h
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 2abdfe1b3..5e6002f4e 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -229,9 +229,18 @@ void Wrap() {
229 u32 retval = func(&param_1, &param_2, 229 u32 retval = func(&param_1, &param_2,
230 reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3)) 230 reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3))
231 .raw; 231 .raw;
232 // The first out parameter is moved into R2 and the second is moved into R1. 232 Core::CPU().SetReg(1, param_1);
233 Core::CPU().SetReg(1, param_2); 233 Core::CPU().SetReg(2, param_2);
234 Core::CPU().SetReg(2, param_1); 234 FuncReturn(retval);
235}
236
237template <ResultCode func(Kernel::Handle*, Kernel::Handle*)>
238void Wrap() {
239 Kernel::Handle param_1 = 0;
240 Kernel::Handle param_2 = 0;
241 u32 retval = func(&param_1, &param_2).raw;
242 Core::CPU().SetReg(1, param_1);
243 Core::CPU().SetReg(2, param_2);
235 FuncReturn(retval); 244 FuncReturn(retval);
236} 245}
237 246
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index b3b60e7df..64aa61460 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -13,6 +13,7 @@ enum {
13 OutOfHandles = 19, 13 OutOfHandles = 19,
14 SessionClosedByRemote = 26, 14 SessionClosedByRemote = 26,
15 PortNameTooLong = 30, 15 PortNameTooLong = 30,
16 NoPendingSessions = 35,
16 WrongPermission = 46, 17 WrongPermission = 46,
17 InvalidBufferDescriptor = 48, 18 InvalidBufferDescriptor = 48,
18 MaxConnectionsReached = 52, 19 MaxConnectionsReached = 52,
@@ -94,5 +95,9 @@ constexpr ResultCode ERR_OUT_OF_RANGE_KERNEL(ErrorDescription::OutOfRange, Error
94 ErrorLevel::Permanent); // 0xD8E007FD 95 ErrorLevel::Permanent); // 0xD8E007FD
95constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS, 96constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS,
96 ErrorSummary::StatusChanged, ErrorLevel::Info); 97 ErrorSummary::StatusChanged, ErrorLevel::Info);
98/// Returned when Accept() is called on a port with no sessions to be accepted.
99constexpr ResultCode ERR_NO_PENDING_SESSIONS(ErrCodes::NoPendingSessions, ErrorModule::OS,
100 ErrorSummary::WouldBlock,
101 ErrorLevel::Permanent); // 0xD8401823
97 102
98} // namespace Kernel 103} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 6cf1886cf..5ebe2eca4 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -23,6 +23,11 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
23 boost::range::remove_erase(connected_sessions, server_session); 23 boost::range::remove_erase(connected_sessions, server_session);
24} 24}
25 25
26HLERequestContext::HLERequestContext(SharedPtr<ServerSession> session)
27 : session(std::move(session)) {
28 cmd_buf[0] = 0;
29}
30
26HLERequestContext::~HLERequestContext() = default; 31HLERequestContext::~HLERequestContext() = default;
27 32
28SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { 33SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const {
@@ -62,10 +67,13 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr
62 ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error 67 ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error
63 for (u32 j = 0; j < num_handles; ++j) { 68 for (u32 j = 0; j < num_handles; ++j) {
64 Handle handle = src_cmdbuf[i]; 69 Handle handle = src_cmdbuf[i];
65 SharedPtr<Object> object = src_table.GetGeneric(handle); 70 SharedPtr<Object> object = nullptr;
66 ASSERT(object != nullptr); // TODO(yuriks): Return error 71 if (handle != 0) {
67 if (descriptor == IPC::DescriptorType::MoveHandle) { 72 object = src_table.GetGeneric(handle);
68 src_table.Close(handle); 73 ASSERT(object != nullptr); // TODO(yuriks): Return error
74 if (descriptor == IPC::DescriptorType::MoveHandle) {
75 src_table.Close(handle);
76 }
69 } 77 }
70 78
71 cmd_buf[i++] = AddOutgoingHandle(std::move(object)); 79 cmd_buf[i++] = AddOutgoingHandle(std::move(object));
@@ -107,9 +115,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
107 ASSERT(i + num_handles <= command_size); 115 ASSERT(i + num_handles <= command_size);
108 for (u32 j = 0; j < num_handles; ++j) { 116 for (u32 j = 0; j < num_handles; ++j) {
109 SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]); 117 SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]);
110 118 Handle handle = 0;
111 // TODO(yuriks): Figure out the proper error handling for if this fails 119 if (object != nullptr) {
112 Handle handle = dst_table.Create(object).Unwrap(); 120 // TODO(yuriks): Figure out the proper error handling for if this fails
121 handle = dst_table.Create(object).Unwrap();
122 }
113 dst_cmdbuf[i++] = handle; 123 dst_cmdbuf[i++] = handle;
114 } 124 }
115 break; 125 break;
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index cbb109d8f..35795fc1d 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -84,6 +84,7 @@ protected:
84 */ 84 */
85class HLERequestContext { 85class HLERequestContext {
86public: 86public:
87 HLERequestContext(SharedPtr<ServerSession> session);
87 ~HLERequestContext(); 88 ~HLERequestContext();
88 89
89 /// Returns a pointer to the IPC command buffer for this request. 90 /// Returns a pointer to the IPC command buffer for this request.
@@ -118,14 +119,14 @@ public:
118 */ 119 */
119 void ClearIncomingObjects(); 120 void ClearIncomingObjects();
120 121
121private: 122 /// Populates this context with data from the requesting process/thread.
122 friend class Service::ServiceFrameworkBase;
123
124 ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, 123 ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process,
125 HandleTable& src_table); 124 HandleTable& src_table);
125 /// Writes data from this context back to the requesting process/thread.
126 ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, 126 ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
127 HandleTable& dst_table) const; 127 HandleTable& dst_table) const;
128 128
129private:
129 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; 130 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
130 SharedPtr<ServerSession> session; 131 SharedPtr<ServerSession> session;
131 // TODO(yuriks): Check common usage of this and optimize size accordingly 132 // TODO(yuriks): Check common usage of this and optimize size accordingly
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 804f23b1c..496d07cb5 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -166,7 +166,7 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin
166 auto vma = address_space 166 auto vma = address_space
167 .MapBackingMemory(mapping.address, target_pointer + offset_into_region, 167 .MapBackingMemory(mapping.address, target_pointer + offset_into_region,
168 mapping.size, memory_state) 168 mapping.size, memory_state)
169 .MoveFrom(); 169 .Unwrap();
170 address_space.Reprotect(vma, 170 address_space.Reprotect(vma,
171 mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); 171 mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite);
172} 172}
@@ -176,14 +176,14 @@ void MapSharedPages(VMManager& address_space) {
176 .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, 176 .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR,
177 reinterpret_cast<u8*>(&ConfigMem::config_mem), 177 reinterpret_cast<u8*>(&ConfigMem::config_mem),
178 Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared) 178 Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared)
179 .MoveFrom(); 179 .Unwrap();
180 address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); 180 address_space.Reprotect(cfg_mem_vma, VMAPermission::Read);
181 181
182 auto shared_page_vma = address_space 182 auto shared_page_vma = address_space
183 .MapBackingMemory(Memory::SHARED_PAGE_VADDR, 183 .MapBackingMemory(Memory::SHARED_PAGE_VADDR,
184 reinterpret_cast<u8*>(&SharedPage::shared_page), 184 reinterpret_cast<u8*>(&SharedPage::shared_page),
185 Memory::SHARED_PAGE_SIZE, MemoryState::Shared) 185 Memory::SHARED_PAGE_SIZE, MemoryState::Shared)
186 .MoveFrom(); 186 .Unwrap();
187 address_space.Reprotect(shared_page_vma, VMAPermission::Read); 187 address_space.Reprotect(shared_page_vma, VMAPermission::Read);
188} 188}
189 189
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 1c31ec950..522ad2333 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -151,6 +151,8 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
151} 151}
152 152
153VAddr Process::GetLinearHeapAreaAddress() const { 153VAddr Process::GetLinearHeapAreaAddress() const {
154 // Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of
155 // the extra RAM in the n3DS.
154 return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; 156 return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR;
155} 157}
156 158
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 4d20c39a1..49a9cdfa3 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -5,8 +5,10 @@
5#include <tuple> 5#include <tuple>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/hle/kernel/client_port.h" 7#include "core/hle/kernel/client_port.h"
8#include "core/hle/kernel/errors.h"
8#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/server_port.h" 10#include "core/hle/kernel/server_port.h"
11#include "core/hle/kernel/server_session.h"
10#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
11 13
12namespace Kernel { 14namespace Kernel {
@@ -14,6 +16,16 @@ namespace Kernel {
14ServerPort::ServerPort() {} 16ServerPort::ServerPort() {}
15ServerPort::~ServerPort() {} 17ServerPort::~ServerPort() {}
16 18
19ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
20 if (pending_sessions.empty()) {
21 return ERR_NO_PENDING_SESSIONS;
22 }
23
24 auto session = std::move(pending_sessions.back());
25 pending_sessions.pop_back();
26 return MakeResult(std::move(session));
27}
28
17bool ServerPort::ShouldWait(Thread* thread) const { 29bool ServerPort::ShouldWait(Thread* thread) const {
18 // If there are no pending sessions, we wait until a new one is added. 30 // If there are no pending sessions, we wait until a new one is added.
19 return pending_sessions.size() == 0; 31 return pending_sessions.size() == 0;
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index f1419cd46..6fe7c7f2f 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -14,6 +14,7 @@
14namespace Kernel { 14namespace Kernel {
15 15
16class ClientPort; 16class ClientPort;
17class ServerSession;
17class SessionRequestHandler; 18class SessionRequestHandler;
18 19
19class ServerPort final : public WaitObject { 20class ServerPort final : public WaitObject {
@@ -41,6 +42,12 @@ public:
41 } 42 }
42 43
43 /** 44 /**
45 * Accepts a pending incoming connection on this port. If there are no pending sessions, will
46 * return ERR_NO_PENDING_SESSIONS.
47 */
48 ResultVal<SharedPtr<ServerSession>> Accept();
49
50 /**
44 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port 51 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
45 * will inherit a reference to this handler. 52 * will inherit a reference to this handler.
46 */ 53 */
@@ -50,8 +57,8 @@ public:
50 57
51 std::string name; ///< Name of port (optional) 58 std::string name; ///< Name of port (optional)
52 59
53 std::vector<SharedPtr<WaitObject>> 60 /// ServerSessions waiting to be accepted by the port
54 pending_sessions; ///< ServerSessions waiting to be accepted by the port 61 std::vector<SharedPtr<ServerSession>> pending_sessions;
55 62
56 /// This session's HLE request handler template (optional) 63 /// This session's HLE request handler template (optional)
57 /// ServerSessions created from this port inherit a reference to this handler. 64 /// ServerSessions created from this port inherit a reference to this handler.
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 970eac5fe..337896abf 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -81,7 +81,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
81 81
82ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name, 82ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name,
83 SharedPtr<ClientPort> port) { 83 SharedPtr<ClientPort> port) {
84 auto server_session = ServerSession::Create(name + "_Server").MoveFrom(); 84 auto server_session = ServerSession::Create(name + "_Server").Unwrap();
85 SharedPtr<ClientSession> client_session(new ClientSession); 85 SharedPtr<ClientSession> client_session(new ClientSession);
86 client_session->name = name + "_Client"; 86 client_session->name = name + "_Client";
87 87
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 75ce626f8..f5f2eb2f7 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -389,7 +389,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
389 thread->wait_objects.clear(); 389 thread->wait_objects.clear();
390 thread->wait_address = 0; 390 thread->wait_address = 0;
391 thread->name = std::move(name); 391 thread->name = std::move(name);
392 thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); 392 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
393 thread->owner_process = g_current_process; 393 thread->owner_process = g_current_process;
394 394
395 // Find the next available TLS index, and mark it as used 395 // Find the next available TLS index, and mark it as used
@@ -484,7 +484,7 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) {
484 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, 484 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
485 Memory::HEAP_VADDR_END); 485 Memory::HEAP_VADDR_END);
486 486
487 SharedPtr<Thread> thread = thread_res.MoveFrom(); 487 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
488 488
489 thread->context.fpscr = 489 thread->context.fpscr =
490 FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 490 FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index 6f2cf3b02..d7ec93672 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -30,7 +30,7 @@ SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) {
30 timer->name = std::move(name); 30 timer->name = std::move(name);
31 timer->initial_delay = 0; 31 timer->initial_delay = 0;
32 timer->interval_delay = 0; 32 timer->interval_delay = 0;
33 timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); 33 timer->callback_handle = timer_callback_handle_table.Create(timer).Unwrap();
34 34
35 return timer; 35 return timer;
36} 36}
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 5f2cdbb96..47b6e2b23 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -388,13 +388,14 @@ public:
388 } 388 }
389 389
390 /// Asserts that the result succeeded and returns a reference to it. 390 /// Asserts that the result succeeded and returns a reference to it.
391 T& Unwrap() { 391 T& Unwrap() & {
392 ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); 392 ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal");
393 return **this; 393 return **this;
394 } 394 }
395 395
396 T&& MoveFrom() { 396 T&& Unwrap() && {
397 return std::move(Unwrap()); 397 ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal");
398 return std::move(**this);
398 } 399 }
399 400
400private: 401private:
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 4c587e3c8..25e7b777d 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -55,8 +55,8 @@ void Initialize(Service::Interface* self) {
55 u32 flags = rp.Pop<u32>(); 55 u32 flags = rp.Pop<u32>();
56 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); 56 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
57 rb.Push(RESULT_SUCCESS); 57 rb.Push(RESULT_SUCCESS);
58 rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).MoveFrom(), 58 rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(),
59 Kernel::g_handle_table.Create(parameter_event).MoveFrom()); 59 Kernel::g_handle_table.Create(parameter_event).Unwrap());
60 60
61 // TODO(bunnei): Check if these events are cleared every time Initialize is called. 61 // TODO(bunnei): Check if these events are cleared every time Initialize is called.
62 notification_event->Clear(); 62 notification_event->Clear();
@@ -93,7 +93,7 @@ void GetSharedFont(Service::Interface* self) {
93 // allocated, the real APT service calculates this address by scanning the entire address space 93 // allocated, the real APT service calculates this address by scanning the entire address space
94 // (using svcQueryMemory) and searches for an allocation of the same size as the Shared Font. 94 // (using svcQueryMemory) and searches for an allocation of the same size as the Shared Font.
95 rb.Push(target_address); 95 rb.Push(target_address);
96 rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).MoveFrom()); 96 rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).Unwrap());
97} 97}
98 98
99void NotifyToWait(Service::Interface* self) { 99void NotifyToWait(Service::Interface* self) {
@@ -115,7 +115,7 @@ void GetLockHandle(Service::Interface* self) {
115 rb.Push(RESULT_SUCCESS); // No error 115 rb.Push(RESULT_SUCCESS); // No error
116 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. 116 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.
117 rb.Push<u32>(0); // Least significant bit = power button state 117 rb.Push<u32>(0); // Least significant bit = power button state
118 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).MoveFrom(); 118 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap();
119 rb.PushCopyHandles(handle_copy); 119 rb.PushCopyHandles(handle_copy);
120 120
121 LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", handle_copy, 121 LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", handle_copy,
@@ -231,7 +231,7 @@ void ReceiveParameter(Service::Interface* self) {
231 rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size 231 rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size
232 232
233 rb.PushMoveHandles((next_parameter.object != nullptr) 233 rb.PushMoveHandles((next_parameter.object != nullptr)
234 ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom() 234 ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap()
235 : 0); 235 : 0);
236 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); 236 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0);
237 237
@@ -261,7 +261,7 @@ void GlanceParameter(Service::Interface* self) {
261 rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size 261 rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size
262 262
263 rb.PushCopyHandles((next_parameter.object != nullptr) 263 rb.PushCopyHandles((next_parameter.object != nullptr)
264 ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom() 264 ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap()
265 : 0); 265 : 0);
266 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); 266 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0);
267 267
diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp
index 7394c844f..c9f9e9d95 100644
--- a/src/core/hle/service/cam/cam.cpp
+++ b/src/core/hle/service/cam/cam.cpp
@@ -347,7 +347,7 @@ void GetVsyncInterruptEvent(Service::Interface* self) {
347 int port = *port_select.begin(); 347 int port = *port_select.begin();
348 rb.Push(RESULT_SUCCESS); 348 rb.Push(RESULT_SUCCESS);
349 rb.PushCopyHandles( 349 rb.PushCopyHandles(
350 Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom()); 350 Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).Unwrap());
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 rb.Push(ERROR_INVALID_ENUM_VALUE); 353 rb.Push(ERROR_INVALID_ENUM_VALUE);
@@ -366,7 +366,7 @@ void GetBufferErrorInterruptEvent(Service::Interface* self) {
366 int port = *port_select.begin(); 366 int port = *port_select.begin();
367 rb.Push(RESULT_SUCCESS); 367 rb.Push(RESULT_SUCCESS);
368 rb.PushCopyHandles( 368 rb.PushCopyHandles(
369 Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom()); 369 Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).Unwrap());
370 } else { 370 } else {
371 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);
372 rb.Push(ERROR_INVALID_ENUM_VALUE); 372 rb.Push(ERROR_INVALID_ENUM_VALUE);
@@ -400,7 +400,7 @@ void SetReceiving(Service::Interface* self) {
400 } 400 }
401 401
402 rb.Push(RESULT_SUCCESS); 402 rb.Push(RESULT_SUCCESS);
403 rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).MoveFrom()); 403 rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).Unwrap());
404 } else { 404 } else {
405 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);
406 rb.Push(ERROR_INVALID_ENUM_VALUE); 406 rb.Push(ERROR_INVALID_ENUM_VALUE);
diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp
index bd9814244..421006a9e 100644
--- a/src/core/hle/service/cecd/cecd.cpp
+++ b/src/core/hle/service/cecd/cecd.cpp
@@ -31,8 +31,8 @@ void GetCecStateAbbreviated(Service::Interface* self) {
31void GetCecInfoEventHandle(Service::Interface* self) { 31void GetCecInfoEventHandle(Service::Interface* self) {
32 u32* cmd_buff = Kernel::GetCommandBuffer(); 32 u32* cmd_buff = Kernel::GetCommandBuffer();
33 33
34 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 34 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
35 cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).MoveFrom(); // Event handle 35 cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).Unwrap(); // Event handle
36 36
37 LOG_WARNING(Service_CECD, "(STUBBED) called"); 37 LOG_WARNING(Service_CECD, "(STUBBED) called");
38} 38}
@@ -40,8 +40,8 @@ void GetCecInfoEventHandle(Service::Interface* self) {
40void GetChangeStateEventHandle(Service::Interface* self) { 40void GetChangeStateEventHandle(Service::Interface* self) {
41 u32* cmd_buff = Kernel::GetCommandBuffer(); 41 u32* cmd_buff = Kernel::GetCommandBuffer();
42 42
43 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 43 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
44 cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).MoveFrom(); // Event handle 44 cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).Unwrap(); // Event handle
45 45
46 LOG_WARNING(Service_CECD, "(STUBBED) called"); 46 LOG_WARNING(Service_CECD, "(STUBBED) called");
47} 47}
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 5a7878b31..6624f1711 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -406,7 +406,7 @@ ResultCode UpdateConfigNANDSavegame() {
406 auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode); 406 auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode);
407 ASSERT_MSG(config_result.Succeeded(), "could not open file"); 407 ASSERT_MSG(config_result.Succeeded(), "could not open file");
408 408
409 auto config = config_result.MoveFrom(); 409 auto config = std::move(config_result).Unwrap();
410 config->backend->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data()); 410 config->backend->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data());
411 411
412 return RESULT_SUCCESS; 412 return RESULT_SUCCESS;
@@ -560,7 +560,7 @@ ResultCode LoadConfigNANDSaveFile() {
560 560
561 // Read the file if it already exists 561 // Read the file if it already exists
562 if (config_result.Succeeded()) { 562 if (config_result.Succeeded()) {
563 auto config = config_result.MoveFrom(); 563 auto config = std::move(config_result).Unwrap();
564 config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); 564 config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
565 return RESULT_SUCCESS; 565 return RESULT_SUCCESS;
566 } 566 }
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 1455f20ca..9471ec1ef 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -51,8 +51,8 @@ static void Initialize(Interface* self) {
51 51
52 cmd_buff[1] = RESULT_SUCCESS.raw; 52 cmd_buff[1] = RESULT_SUCCESS.raw;
53 cmd_buff[2] = IPC::CopyHandleDesc(2); 53 cmd_buff[2] = IPC::CopyHandleDesc(2);
54 cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom(); 54 cmd_buff[3] = Kernel::g_handle_table.Create(mutex).Unwrap();
55 cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom(); 55 cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).Unwrap();
56 56
57 LOG_WARNING(Service_CSND, "(STUBBED) called"); 57 LOG_WARNING(Service_CSND, "(STUBBED) called");
58} 58}
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 363066d14..7d746054f 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -168,7 +168,7 @@ static void GetSemaphoreEventHandle(Service::Interface* self) {
168 cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2); 168 cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2);
169 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 169 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
170 // cmd_buff[2] not set 170 // cmd_buff[2] not set
171 cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle 171 cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).Unwrap(); // Event handle
172 172
173 LOG_WARNING(Service_DSP, "(STUBBED) called"); 173 LOG_WARNING(Service_DSP, "(STUBBED) called");
174} 174}
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 3605ef175..033fbc9aa 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -311,7 +311,7 @@ ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handl
311 if (backend.Failed()) 311 if (backend.Failed())
312 return backend.Code(); 312 return backend.Code();
313 313
314 auto file = std::shared_ptr<File>(new File(backend.MoveFrom(), path)); 314 auto file = std::shared_ptr<File>(new File(std::move(backend).Unwrap(), path));
315 return MakeResult<std::shared_ptr<File>>(std::move(file)); 315 return MakeResult<std::shared_ptr<File>>(std::move(file));
316} 316}
317 317
@@ -401,7 +401,7 @@ ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle arc
401 if (backend.Failed()) 401 if (backend.Failed())
402 return backend.Code(); 402 return backend.Code();
403 403
404 auto directory = std::shared_ptr<Directory>(new Directory(backend.MoveFrom(), path)); 404 auto directory = std::shared_ptr<Directory>(new Directory(std::move(backend).Unwrap(), path));
405 return MakeResult<std::shared_ptr<Directory>>(std::move(directory)); 405 return MakeResult<std::shared_ptr<Directory>>(std::move(directory));
406} 406}
407 407
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 34e1783ec..b9eab7838 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -87,7 +87,7 @@ static void OpenFile(Service::Interface* self) {
87 file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); 87 file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
88 88
89 rb.PushMoveHandles( 89 rb.PushMoveHandles(
90 Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom()); 90 Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap());
91 } else { 91 } else {
92 rb.PushMoveHandles(0); 92 rb.PushMoveHandles(0);
93 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());
@@ -153,7 +153,7 @@ static void OpenFileDirectly(Service::Interface* self) {
153 file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); 153 file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
154 154
155 cmd_buff[3] = 155 cmd_buff[3] =
156 Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); 156 Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap();
157 } else { 157 } else {
158 cmd_buff[3] = 0; 158 cmd_buff[3] = 0;
159 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",
@@ -420,7 +420,7 @@ static void OpenDirectory(Service::Interface* self) {
420 directory->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); 420 directory->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
421 421
422 cmd_buff[3] = 422 cmd_buff[3] =
423 Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); 423 Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap();
424 } else { 424 } else {
425 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",
426 dirname_type, dirname_size, dir_path.DebugStr().c_str()); 426 dirname_type, dirname_size, dir_path.DebugStr().c_str());
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 6ff0f4812..bc964ec60 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -389,8 +389,8 @@ static void RegisterInterruptRelayQueue(Interface* self) {
389 } else { 389 } else {
390 cmd_buff[1] = RESULT_SUCCESS.raw; 390 cmd_buff[1] = RESULT_SUCCESS.raw;
391 } 391 }
392 cmd_buff[2] = g_thread_id++; // Thread ID 392 cmd_buff[2] = g_thread_id++; // Thread ID
393 cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); // GSP shared memory 393 cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).Unwrap(); // GSP shared memory
394 394
395 g_interrupt_event->Signal(); // TODO(bunnei): Is this correct? 395 g_interrupt_event->Signal(); // TODO(bunnei): Is this correct?
396 396
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 5255f6dc8..2014b8461 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -253,12 +253,12 @@ void GetIPCHandles(Service::Interface* self) {
253 cmd_buff[1] = 0; // No error 253 cmd_buff[1] = 0; // No error
254 cmd_buff[2] = 0x14000000; // IPC Command Structure translate-header 254 cmd_buff[2] = 0x14000000; // IPC Command Structure translate-header
255 // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) 255 // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling)
256 cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).MoveFrom(); 256 cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).Unwrap();
257 cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).MoveFrom(); 257 cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).Unwrap();
258 cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).MoveFrom(); 258 cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).Unwrap();
259 cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).MoveFrom(); 259 cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).Unwrap();
260 cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).MoveFrom(); 260 cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).Unwrap();
261 cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).MoveFrom(); 261 cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).Unwrap();
262} 262}
263 263
264void EnableAccelerometer(Service::Interface* self) { 264void EnableAccelerometer(Service::Interface* self) {
diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp
index 0de698003..837413f93 100644
--- a/src/core/hle/service/ir/ir_rst.cpp
+++ b/src/core/hle/service/ir/ir_rst.cpp
@@ -145,8 +145,8 @@ static void GetHandles(Interface* self) {
145 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 0, 0); 145 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 0, 0);
146 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); 146 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
147 rb.Push(RESULT_SUCCESS); 147 rb.Push(RESULT_SUCCESS);
148 rb.PushMoveHandles(Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(), 148 rb.PushMoveHandles(Kernel::g_handle_table.Create(Service::IR::shared_memory).Unwrap(),
149 Kernel::g_handle_table.Create(Service::IR::update_event).MoveFrom()); 149 Kernel::g_handle_table.Create(Service::IR::update_event).Unwrap());
150} 150}
151 151
152/** 152/**
diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp
index fdecdce64..fbdf7a465 100644
--- a/src/core/hle/service/ir/ir_user.cpp
+++ b/src/core/hle/service/ir/ir_user.cpp
@@ -337,7 +337,7 @@ void GetReceiveEvent(Interface* self) {
337 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0A, 1, 2); 337 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0A, 1, 2);
338 338
339 rb.Push(RESULT_SUCCESS); 339 rb.Push(RESULT_SUCCESS);
340 rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).MoveFrom()); 340 rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).Unwrap());
341 341
342 LOG_INFO(Service_IR, "called"); 342 LOG_INFO(Service_IR, "called");
343} 343}
@@ -354,7 +354,7 @@ void GetSendEvent(Interface* self) {
354 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0B, 1, 2); 354 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0B, 1, 2);
355 355
356 rb.Push(RESULT_SUCCESS); 356 rb.Push(RESULT_SUCCESS);
357 rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).MoveFrom()); 357 rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).Unwrap());
358 358
359 LOG_INFO(Service_IR, "called"); 359 LOG_INFO(Service_IR, "called");
360} 360}
@@ -394,7 +394,7 @@ static void GetConnectionStatusEvent(Interface* self) {
394 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0C, 1, 2); 394 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0C, 1, 2);
395 395
396 rb.Push(RESULT_SUCCESS); 396 rb.Push(RESULT_SUCCESS);
397 rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom()); 397 rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).Unwrap());
398 398
399 LOG_INFO(Service_IR, "called"); 399 LOG_INFO(Service_IR, "called");
400} 400}
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index 35212b59b..23e1ff094 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -160,7 +160,7 @@ static void IsSampling(Interface* self) {
160static void GetBufferFullEvent(Interface* self) { 160static void GetBufferFullEvent(Interface* self) {
161 u32* cmd_buff = Kernel::GetCommandBuffer(); 161 u32* cmd_buff = Kernel::GetCommandBuffer();
162 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 162 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
163 cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom(); 163 cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).Unwrap();
164 LOG_WARNING(Service_MIC, "(STUBBED) called"); 164 LOG_WARNING(Service_MIC, "(STUBBED) called");
165} 165}
166 166
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index b44a9f668..cb09ed0b7 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -95,7 +95,7 @@ void GetTagInRangeEvent(Interface* self) {
95 cmd_buff[0] = IPC::MakeHeader(0xB, 1, 2); 95 cmd_buff[0] = IPC::MakeHeader(0xB, 1, 2);
96 cmd_buff[1] = RESULT_SUCCESS.raw; 96 cmd_buff[1] = RESULT_SUCCESS.raw;
97 cmd_buff[2] = IPC::CopyHandleDesc(); 97 cmd_buff[2] = IPC::CopyHandleDesc();
98 cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).MoveFrom(); 98 cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).Unwrap();
99 LOG_WARNING(Service_NFC, "(STUBBED) called"); 99 LOG_WARNING(Service_NFC, "(STUBBED) called");
100} 100}
101 101
@@ -105,7 +105,7 @@ void GetTagOutOfRangeEvent(Interface* self) {
105 cmd_buff[0] = IPC::MakeHeader(0xC, 1, 2); 105 cmd_buff[0] = IPC::MakeHeader(0xC, 1, 2);
106 cmd_buff[1] = RESULT_SUCCESS.raw; 106 cmd_buff[1] = RESULT_SUCCESS.raw;
107 cmd_buff[2] = IPC::CopyHandleDesc(); 107 cmd_buff[2] = IPC::CopyHandleDesc();
108 cmd_buff[3] = Kernel::g_handle_table.Create(tag_out_of_range_event).MoveFrom(); 108 cmd_buff[3] = Kernel::g_handle_table.Create(tag_out_of_range_event).Unwrap();
109 LOG_WARNING(Service_NFC, "(STUBBED) called"); 109 LOG_WARNING(Service_NFC, "(STUBBED) called");
110} 110}
111 111
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index e92900d48..6dbdff044 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -15,6 +15,7 @@
15#include "core/hle/result.h" 15#include "core/hle/result.h"
16#include "core/hle/service/nwm/nwm_uds.h" 16#include "core/hle/service/nwm/nwm_uds.h"
17#include "core/hle/service/nwm/uds_beacon.h" 17#include "core/hle/service/nwm/uds_beacon.h"
18#include "core/hle/service/nwm/uds_data.h"
18#include "core/memory.h" 19#include "core/memory.h"
19 20
20namespace Service { 21namespace Service {
@@ -190,7 +191,7 @@ static void InitializeWithVersion(Interface* self) {
190 191
191 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); 192 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
192 rb.Push(RESULT_SUCCESS); 193 rb.Push(RESULT_SUCCESS);
193 rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).MoveFrom()); 194 rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap());
194 195
195 LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", 196 LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X",
196 sharedmem_size, version, sharedmem_handle); 197 sharedmem_size, version, sharedmem_handle);
@@ -265,7 +266,7 @@ static void Bind(Interface* self) {
265 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); 266 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
266 267
267 rb.Push(RESULT_SUCCESS); 268 rb.Push(RESULT_SUCCESS);
268 rb.PushCopyHandles(Kernel::g_handle_table.Create(event).MoveFrom()); 269 rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap());
269} 270}
270 271
271/** 272/**
@@ -373,6 +374,80 @@ static void DestroyNetwork(Interface* self) {
373} 374}
374 375
375/** 376/**
377 * NWM_UDS::SendTo service function.
378 * Sends a data frame to the UDS network we're connected to.
379 * Inputs:
380 * 0 : Command header.
381 * 1 : Unknown.
382 * 2 : u16 Destination network node id.
383 * 3 : u8 Data channel.
384 * 4 : Buffer size >> 2
385 * 5 : Data size
386 * 6 : Flags
387 * 7 : Input buffer descriptor
388 * 8 : Input buffer address
389 * Outputs:
390 * 0 : Return header
391 * 1 : Result of function, 0 on success, otherwise error code
392 */
393static void SendTo(Interface* self) {
394 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 6, 2);
395
396 rp.Skip(1, false);
397 u16 dest_node_id = rp.Pop<u16>();
398 u8 data_channel = rp.Pop<u8>();
399 rp.Skip(1, false);
400 u32 data_size = rp.Pop<u32>();
401 u32 flags = rp.Pop<u32>();
402
403 size_t desc_size;
404 const VAddr input_address = rp.PopStaticBuffer(&desc_size, false);
405 ASSERT(desc_size == data_size);
406
407 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
408
409 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) &&
410 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
411 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
412 ErrorSummary::InvalidState, ErrorLevel::Status));
413 return;
414 }
415
416 if (dest_node_id == connection_status.network_node_id) {
417 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
418 ErrorSummary::WrongArgument, ErrorLevel::Status));
419 return;
420 }
421
422 // TODO(Subv): Do something with the flags.
423
424 constexpr size_t MaxSize = 0x5C6;
425 if (data_size > MaxSize) {
426 rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS,
427 ErrorSummary::WrongArgument, ErrorLevel::Usage));
428 return;
429 }
430
431 std::vector<u8> data(data_size);
432 Memory::ReadBlock(input_address, data.data(), data.size());
433
434 // TODO(Subv): Increment the sequence number after each sent packet.
435 u16 sequence_number = 0;
436 std::vector<u8> data_payload = GenerateDataPayload(
437 data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number);
438
439 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt
440 // and encapsulate the payload.
441
442 // TODO(Subv): Send the frame.
443
444 rb.Push(RESULT_SUCCESS);
445
446 LOG_WARNING(Service_NWM, "(STUB) called dest_node_id=%u size=%u flags=%u channel=%u",
447 static_cast<u32>(dest_node_id), data_size, flags, static_cast<u32>(data_channel));
448}
449
450/**
376 * NWM_UDS::GetChannel service function. 451 * NWM_UDS::GetChannel service function.
377 * Returns the WiFi channel in which the network we're connected to is transmitting. 452 * Returns the WiFi channel in which the network we're connected to is transmitting.
378 * Inputs: 453 * Inputs:
@@ -543,6 +618,42 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
543 beacon_broadcast_event, 0); 618 beacon_broadcast_event, 0);
544} 619}
545 620
621/*
622 * Returns an available index in the nodes array for the
623 * currently-hosted UDS network.
624 */
625static u32 GetNextAvailableNodeId() {
626 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
627 "Can not accept clients if we're not hosting a network");
628
629 for (unsigned index = 0; index < connection_status.max_nodes; ++index) {
630 if ((connection_status.node_bitmask & (1 << index)) == 0)
631 return index;
632 }
633
634 // Any connection attempts to an already full network should have been refused.
635 ASSERT_MSG(false, "No available connection slots in the network");
636}
637
638/*
639 * Called when a client connects to an UDS network we're hosting,
640 * updates the connection status and signals the update event.
641 * @param network_node_id Network Node Id of the connecting client.
642 */
643void OnClientConnected(u16 network_node_id) {
644 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
645 "Can not accept clients if we're not hosting a network");
646 ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes,
647 "Can not accept connections on a full network");
648
649 u32 node_id = GetNextAvailableNodeId();
650 connection_status.node_bitmask |= 1 << node_id;
651 connection_status.changed_nodes |= 1 << node_id;
652 connection_status.nodes[node_id] = network_node_id;
653 connection_status.total_nodes++;
654 connection_status_event->Signal();
655}
656
546const Interface::FunctionInfo FunctionTable[] = { 657const Interface::FunctionInfo FunctionTable[] = {
547 {0x00010442, nullptr, "Initialize (deprecated)"}, 658 {0x00010442, nullptr, "Initialize (deprecated)"},
548 {0x00020000, nullptr, "Scrap"}, 659 {0x00020000, nullptr, "Scrap"},
@@ -564,7 +675,7 @@ const Interface::FunctionInfo FunctionTable[] = {
564 {0x00130040, nullptr, "Unbind"}, 675 {0x00130040, nullptr, "Unbind"},
565 {0x001400C0, nullptr, "PullPacket"}, 676 {0x001400C0, nullptr, "PullPacket"},
566 {0x00150080, nullptr, "SetMaxSendDelay"}, 677 {0x00150080, nullptr, "SetMaxSendDelay"},
567 {0x00170182, nullptr, "SendTo"}, 678 {0x00170182, SendTo, "SendTo"},
568 {0x001A0000, GetChannel, "GetChannel"}, 679 {0x001A0000, GetChannel, "GetChannel"},
569 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, 680 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"},
570 {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"}, 681 {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"},
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp
new file mode 100644
index 000000000..8c6742dba
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_data.cpp
@@ -0,0 +1,278 @@
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 <cstring>
6#include <cryptopp/aes.h>
7#include <cryptopp/ccm.h>
8#include <cryptopp/filters.h>
9#include <cryptopp/md5.h>
10#include <cryptopp/modes.h>
11#include "core/hle/service/nwm/nwm_uds.h"
12#include "core/hle/service/nwm/uds_data.h"
13#include "core/hw/aes/key.h"
14
15namespace Service {
16namespace NWM {
17
18using MacAddress = std::array<u8, 6>;
19
20/*
21 * Generates a SNAP-enabled 802.2 LLC header for the specified protocol.
22 * @returns a buffer with the bytes of the generated header.
23 */
24static std::vector<u8> GenerateLLCHeader(EtherType protocol) {
25 LLCHeader header{};
26 header.protocol = static_cast<u16>(protocol);
27
28 std::vector<u8> buffer(sizeof(header));
29 memcpy(buffer.data(), &header, sizeof(header));
30
31 return buffer;
32}
33
34/*
35 * Generates a Nintendo UDS SecureData header with the specified parameters.
36 * @returns a buffer with the bytes of the generated header.
37 */
38static std::vector<u8> GenerateSecureDataHeader(u16 data_size, u8 channel, u16 dest_node_id,
39 u16 src_node_id, u16 sequence_number) {
40 SecureDataHeader header{};
41 header.protocol_size = data_size + sizeof(SecureDataHeader);
42 // Note: This size includes everything except the first 4 bytes of the structure,
43 // reinforcing the hypotheses that the first 4 bytes are actually the header of
44 // another container protocol.
45 header.securedata_size = data_size + sizeof(SecureDataHeader) - 4;
46 // Frames sent by the emulated application are never UDS management frames
47 header.is_management = 0;
48 header.data_channel = channel;
49 header.sequence_number = sequence_number;
50 header.dest_node_id = dest_node_id;
51 header.src_node_id = src_node_id;
52
53 std::vector<u8> buffer(sizeof(header));
54 memcpy(buffer.data(), &header, sizeof(header));
55
56 return buffer;
57}
58
59/*
60 * Calculates the CTR used for the AES-CTR process that calculates
61 * the CCMP crypto key for data frames.
62 * @returns The CTR used for data frames crypto key generation.
63 */
64static std::array<u8, CryptoPP::MD5::DIGESTSIZE> GetDataCryptoCTR(const NetworkInfo& network_info) {
65 DataFrameCryptoCTR data{};
66
67 data.host_mac = network_info.host_mac_address;
68 data.wlan_comm_id = network_info.wlan_comm_id;
69 data.id = network_info.id;
70 data.network_id = network_info.network_id;
71
72 std::array<u8, CryptoPP::MD5::DIGESTSIZE> hash;
73 CryptoPP::MD5().CalculateDigest(hash.data(), reinterpret_cast<u8*>(&data), sizeof(data));
74
75 return hash;
76}
77
78/*
79 * Generates the key used for encrypting the 802.11 data frames generated by UDS.
80 * @returns The key used for data frames crypto.
81 */
82static std::array<u8, CryptoPP::AES::BLOCKSIZE> GenerateDataCCMPKey(
83 const std::vector<u8>& passphrase, const NetworkInfo& network_info) {
84 // Calculate the MD5 hash of the input passphrase.
85 std::array<u8, CryptoPP::MD5::DIGESTSIZE> passphrase_hash;
86 CryptoPP::MD5().CalculateDigest(passphrase_hash.data(), passphrase.data(), passphrase.size());
87
88 std::array<u8, CryptoPP::AES::BLOCKSIZE> ccmp_key;
89
90 // The CCMP key is the result of encrypting the MD5 hash of the passphrase with AES-CTR using
91 // keyslot 0x2D.
92 using CryptoPP::AES;
93 std::array<u8, CryptoPP::MD5::DIGESTSIZE> counter = GetDataCryptoCTR(network_info);
94 std::array<u8, AES::BLOCKSIZE> key = HW::AES::GetNormalKey(HW::AES::KeySlotID::UDSDataKey);
95 CryptoPP::CTR_Mode<AES>::Encryption aes;
96 aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, counter.data());
97 aes.ProcessData(ccmp_key.data(), passphrase_hash.data(), passphrase_hash.size());
98
99 return ccmp_key;
100}
101
102/*
103 * Generates the Additional Authenticated Data (AAD) for an UDS 802.11 encrypted data frame.
104 * @returns a buffer with the bytes of the AAD.
105 */
106static std::vector<u8> GenerateCCMPAAD(const MacAddress& sender, const MacAddress& receiver,
107 const MacAddress& bssid, u16 frame_control) {
108 // Reference: IEEE 802.11-2007
109
110 // 8.3.3.3.2 Construct AAD (22-30 bytes)
111 // The AAD is constructed from the MPDU header. The AAD does not include the header Duration
112 // field, because the Duration field value can change due to normal IEEE 802.11 operation (e.g.,
113 // a rate change during retransmission). For similar reasons, several subfields in the Frame
114 // Control field are masked to 0.
115 struct {
116 u16_be FC; // MPDU Frame Control field
117 MacAddress A1;
118 MacAddress A2;
119 MacAddress A3;
120 u16_be SC; // MPDU Sequence Control field
121 } aad_struct{};
122
123 constexpr u16 AADFrameControlMask = 0x8FC7;
124 aad_struct.FC = frame_control & AADFrameControlMask;
125 aad_struct.SC = 0;
126
127 bool to_ds = (frame_control & (1 << 0)) != 0;
128 bool from_ds = (frame_control & (1 << 1)) != 0;
129 // In the 802.11 standard, ToDS = 1 and FromDS = 1 is a valid configuration,
130 // however, the 3DS doesn't seem to transmit frames with such combination.
131 ASSERT_MSG(to_ds != from_ds, "Invalid combination");
132
133 // The meaning of the address fields depends on the ToDS and FromDS fields.
134 if (from_ds) {
135 aad_struct.A1 = receiver;
136 aad_struct.A2 = bssid;
137 aad_struct.A3 = sender;
138 }
139
140 if (to_ds) {
141 aad_struct.A1 = bssid;
142 aad_struct.A2 = sender;
143 aad_struct.A3 = receiver;
144 }
145
146 std::vector<u8> aad(sizeof(aad_struct));
147 std::memcpy(aad.data(), &aad_struct, sizeof(aad_struct));
148
149 return aad;
150}
151
152/*
153 * Decrypts the payload of an encrypted 802.11 data frame using the specified key.
154 * @returns The decrypted payload.
155 */
156static std::vector<u8> DecryptDataFrame(const std::vector<u8>& encrypted_payload,
157 const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key,
158 const MacAddress& sender, const MacAddress& receiver,
159 const MacAddress& bssid, u16 sequence_number,
160 u16 frame_control) {
161
162 // Reference: IEEE 802.11-2007
163
164 std::vector<u8> aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control);
165
166 std::vector<u8> packet_number{0,
167 0,
168 0,
169 0,
170 static_cast<u8>((sequence_number >> 8) & 0xFF),
171 static_cast<u8>(sequence_number & 0xFF)};
172
173 // 8.3.3.3.3 Construct CCM nonce (13 bytes)
174 std::vector<u8> nonce;
175 nonce.push_back(0); // priority
176 nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2
177 nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN
178
179 try {
180 CryptoPP::CCM<CryptoPP::AES, 8>::Decryption d;
181 d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size());
182 d.SpecifyDataLengths(aad.size(), encrypted_payload.size() - 8, 0);
183
184 CryptoPP::AuthenticatedDecryptionFilter df(
185 d, nullptr, CryptoPP::AuthenticatedDecryptionFilter::MAC_AT_END |
186 CryptoPP::AuthenticatedDecryptionFilter::THROW_EXCEPTION);
187 // put aad
188 df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size());
189
190 // put cipher with mac
191 df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, encrypted_payload.data(),
192 encrypted_payload.size() - 8);
193 df.ChannelPut(CryptoPP::DEFAULT_CHANNEL,
194 encrypted_payload.data() + encrypted_payload.size() - 8, 8);
195
196 df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL);
197 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL);
198 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
199
200 int size = df.MaxRetrievable();
201
202 std::vector<u8> pdata(size);
203 df.Get(pdata.data(), size);
204 return pdata;
205 } catch (CryptoPP::Exception&) {
206 LOG_ERROR(Service_NWM, "failed to decrypt");
207 }
208
209 return {};
210}
211
212/*
213 * Encrypts the payload of an 802.11 data frame using the specified key.
214 * @returns The encrypted payload.
215 */
216static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload,
217 const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key,
218 const MacAddress& sender, const MacAddress& receiver,
219 const MacAddress& bssid, u16 sequence_number,
220 u16 frame_control) {
221 // Reference: IEEE 802.11-2007
222
223 std::vector<u8> aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control);
224
225 std::vector<u8> packet_number{0,
226 0,
227 0,
228 0,
229 static_cast<u8>((sequence_number >> 8) & 0xFF),
230 static_cast<u8>(sequence_number & 0xFF)};
231
232 // 8.3.3.3.3 Construct CCM nonce (13 bytes)
233 std::vector<u8> nonce;
234 nonce.push_back(0); // priority
235 nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2
236 nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN
237
238 try {
239 CryptoPP::CCM<CryptoPP::AES, 8>::Encryption d;
240 d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size());
241 d.SpecifyDataLengths(aad.size(), payload.size(), 0);
242
243 CryptoPP::AuthenticatedEncryptionFilter df(d);
244 // put aad
245 df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size());
246 df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL);
247
248 // put plaintext
249 df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, payload.data(), payload.size());
250 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL);
251
252 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
253
254 int size = df.MaxRetrievable();
255
256 std::vector<u8> cipher(size);
257 df.Get(cipher.data(), size);
258 return cipher;
259 } catch (CryptoPP::Exception&) {
260 LOG_ERROR(Service_NWM, "failed to encrypt");
261 }
262
263 return {};
264}
265
266std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
267 u16 src_node, u16 sequence_number) {
268 std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData);
269 std::vector<u8> securedata_header =
270 GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number);
271
272 buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end());
273 buffer.insert(buffer.end(), data.begin(), data.end());
274 return buffer;
275}
276
277} // namespace NWM
278} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h
new file mode 100644
index 000000000..a23520a41
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_data.h
@@ -0,0 +1,78 @@
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 <array>
8#include <vector>
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/service.h"
12
13namespace Service {
14namespace NWM {
15
16enum class SAP : u8 { SNAPExtensionUsed = 0xAA };
17
18enum class PDUControl : u8 { UnnumberedInformation = 3 };
19
20enum class EtherType : u16 { SecureData = 0x876D, EAPoL = 0x888E };
21
22/*
23 * 802.2 header, UDS packets always use SNAP for these headers,
24 * which means the dsap and ssap are always SNAPExtensionUsed (0xAA)
25 * and the OUI is always 0.
26 */
27struct LLCHeader {
28 u8 dsap = static_cast<u8>(SAP::SNAPExtensionUsed);
29 u8 ssap = static_cast<u8>(SAP::SNAPExtensionUsed);
30 u8 control = static_cast<u8>(PDUControl::UnnumberedInformation);
31 std::array<u8, 3> OUI = {};
32 u16_be protocol;
33};
34
35static_assert(sizeof(LLCHeader) == 8, "LLCHeader has the wrong size");
36
37/*
38 * Nintendo SecureData header, every UDS packet contains one,
39 * it is used to store metadata about the transmission such as
40 * the source and destination network node ids.
41 */
42struct SecureDataHeader {
43 // TODO(Subv): It is likely that the first 4 bytes of this header are
44 // actually part of another container protocol.
45 u16_be protocol_size;
46 INSERT_PADDING_BYTES(2);
47 u16_be securedata_size;
48 u8 is_management;
49 u8 data_channel;
50 u16_be sequence_number;
51 u16_be dest_node_id;
52 u16_be src_node_id;
53};
54
55static_assert(sizeof(SecureDataHeader) == 14, "SecureDataHeader has the wrong size");
56
57/*
58 * The raw bytes of this structure are the CTR used in the encryption (AES-CTR)
59 * process used to generate the CCMP key for data frame encryption.
60 */
61struct DataFrameCryptoCTR {
62 u32_le wlan_comm_id;
63 u32_le network_id;
64 std::array<u8, 6> host_mac;
65 u16_le id;
66};
67
68static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size");
69
70/**
71 * Generates an unencrypted 802.11 data payload.
72 * @returns The generated frame payload.
73 */
74std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
75 u16 src_node, u16 sequence_number);
76
77} // namespace NWM
78} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 39382ef09..a0b959797 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -152,7 +152,7 @@ void Init() {
152 auto gamecoin_result = 152 auto gamecoin_result =
153 Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode); 153 Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode);
154 if (gamecoin_result.Succeeded()) { 154 if (gamecoin_result.Succeeded()) {
155 auto gamecoin = gamecoin_result.MoveFrom(); 155 auto gamecoin = std::move(gamecoin_result).Unwrap();
156 gamecoin->backend->Write(0, sizeof(GameCoin), true, 156 gamecoin->backend->Write(0, sizeof(GameCoin), true,
157 reinterpret_cast<const u8*>(&default_game_coin)); 157 reinterpret_cast<const u8*>(&default_game_coin));
158 gamecoin->backend->Close(); 158 gamecoin->backend->Close();
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 791a65c19..aad950e50 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -173,8 +173,7 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses
173 173
174 // TODO(yuriks): The kernel should be the one handling this as part of translation after 174 // TODO(yuriks): The kernel should be the one handling this as part of translation after
175 // everything else is migrated 175 // everything else is migrated
176 Kernel::HLERequestContext context; 176 Kernel::HLERequestContext context(std::move(server_session));
177 context.session = std::move(server_session);
178 context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, 177 context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
179 Kernel::g_handle_table); 178 Kernel::g_handle_table);
180 179
@@ -207,7 +206,7 @@ void AddService(Interface* interface_) {
207 auto server_port = 206 auto server_port =
208 SM::g_service_manager 207 SM::g_service_manager
209 ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions()) 208 ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions())
210 .MoveFrom(); 209 .Unwrap();
211 server_port->SetHleHandler(std::shared_ptr<Interface>(interface_)); 210 server_port->SetHleHandler(std::shared_ptr<Interface>(interface_));
212} 211}
213 212
diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp
index 74a1256e0..352941e69 100644
--- a/src/core/hle/service/sm/srv.cpp
+++ b/src/core/hle/service/sm/srv.cpp
@@ -113,13 +113,13 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
113 (*session)->GetObjectId()); 113 (*session)->GetObjectId());
114 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); 114 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
115 rb.Push(session.Code()); 115 rb.Push(session.Code());
116 rb.PushObjects(session.MoveFrom()); 116 rb.PushObjects(std::move(session).Unwrap());
117 } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) { 117 } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) {
118 LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u", 118 LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u",
119 name.c_str(), (*client_port)->GetObjectId()); 119 name.c_str(), (*client_port)->GetObjectId());
120 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); 120 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
121 rb.Push(ERR_MAX_CONNECTIONS_REACHED); 121 rb.Push(ERR_MAX_CONNECTIONS_REACHED);
122 rb.PushObjects(client_port.MoveFrom()); 122 rb.PushObjects(std::move(client_port).Unwrap());
123 } else { 123 } else {
124 LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); 124 LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code());
125 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 125 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index bb7bf2d67..e73971d5f 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -275,7 +275,7 @@ static void GetTransferEndEvent(Interface* self) {
275 275
276 cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); 276 cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0);
277 cmd_buff[1] = RESULT_SUCCESS.raw; 277 cmd_buff[1] = RESULT_SUCCESS.raw;
278 cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); 278 cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).Unwrap();
279 279
280 LOG_DEBUG(Service_Y2R, "called"); 280 LOG_DEBUG(Service_Y2R, "called");
281} 281}
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 0a7f011f3..e4b803046 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -37,8 +37,9 @@
37//////////////////////////////////////////////////////////////////////////////////////////////////// 37////////////////////////////////////////////////////////////////////////////////////////////////////
38// Namespace SVC 38// Namespace SVC
39 39
40using Kernel::SharedPtr;
41using Kernel::ERR_INVALID_HANDLE; 40using Kernel::ERR_INVALID_HANDLE;
41using Kernel::Handle;
42using Kernel::SharedPtr;
42 43
43namespace SVC { 44namespace SVC {
44 45
@@ -1040,7 +1041,6 @@ static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client
1040 1041
1041 using Kernel::ServerPort; 1042 using Kernel::ServerPort;
1042 using Kernel::ClientPort; 1043 using Kernel::ClientPort;
1043 using Kernel::SharedPtr;
1044 1044
1045 auto ports = ServerPort::CreatePortPair(max_sessions); 1045 auto ports = ServerPort::CreatePortPair(max_sessions);
1046 CASCADE_RESULT(*client_port, Kernel::g_handle_table.Create( 1046 CASCADE_RESULT(*client_port, Kernel::g_handle_table.Create(
@@ -1054,6 +1054,41 @@ static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client
1054 return RESULT_SUCCESS; 1054 return RESULT_SUCCESS;
1055} 1055}
1056 1056
1057static ResultCode CreateSessionToPort(Handle* out_client_session, Handle client_port_handle) {
1058 using Kernel::ClientPort;
1059 SharedPtr<ClientPort> client_port = Kernel::g_handle_table.Get<ClientPort>(client_port_handle);
1060 if (client_port == nullptr)
1061 return ERR_INVALID_HANDLE;
1062
1063 CASCADE_RESULT(auto session, client_port->Connect());
1064 CASCADE_RESULT(*out_client_session, Kernel::g_handle_table.Create(std::move(session)));
1065 return RESULT_SUCCESS;
1066}
1067
1068static ResultCode CreateSession(Handle* server_session, Handle* client_session) {
1069 auto sessions = Kernel::ServerSession::CreateSessionPair();
1070
1071 auto& server = std::get<SharedPtr<Kernel::ServerSession>>(sessions);
1072 CASCADE_RESULT(*server_session, Kernel::g_handle_table.Create(std::move(server)));
1073
1074 auto& client = std::get<SharedPtr<Kernel::ClientSession>>(sessions);
1075 CASCADE_RESULT(*client_session, Kernel::g_handle_table.Create(std::move(client)));
1076
1077 LOG_TRACE(Kernel_SVC, "called");
1078 return RESULT_SUCCESS;
1079}
1080
1081static ResultCode AcceptSession(Handle* out_server_session, Handle server_port_handle) {
1082 using Kernel::ServerPort;
1083 SharedPtr<ServerPort> server_port = Kernel::g_handle_table.Get<ServerPort>(server_port_handle);
1084 if (server_port == nullptr)
1085 return ERR_INVALID_HANDLE;
1086
1087 CASCADE_RESULT(auto session, server_port->Accept());
1088 CASCADE_RESULT(*out_server_session, Kernel::g_handle_table.Create(std::move(session)));
1089 return RESULT_SUCCESS;
1090}
1091
1057static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { 1092static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) {
1058 using Kernel::MemoryRegion; 1093 using Kernel::MemoryRegion;
1059 1094
@@ -1228,9 +1263,9 @@ static const FunctionDef SVC_Table[] = {
1228 {0x45, nullptr, "Unknown"}, 1263 {0x45, nullptr, "Unknown"},
1229 {0x46, nullptr, "Unknown"}, 1264 {0x46, nullptr, "Unknown"},
1230 {0x47, HLE::Wrap<CreatePort>, "CreatePort"}, 1265 {0x47, HLE::Wrap<CreatePort>, "CreatePort"},
1231 {0x48, nullptr, "CreateSessionToPort"}, 1266 {0x48, HLE::Wrap<CreateSessionToPort>, "CreateSessionToPort"},
1232 {0x49, nullptr, "CreateSession"}, 1267 {0x49, HLE::Wrap<CreateSession>, "CreateSession"},
1233 {0x4A, nullptr, "AcceptSession"}, 1268 {0x4A, HLE::Wrap<AcceptSession>, "AcceptSession"},
1234 {0x4B, nullptr, "ReplyAndReceive1"}, 1269 {0x4B, nullptr, "ReplyAndReceive1"},
1235 {0x4C, nullptr, "ReplyAndReceive2"}, 1270 {0x4C, nullptr, "ReplyAndReceive2"},
1236 {0x4D, nullptr, "ReplyAndReceive3"}, 1271 {0x4D, nullptr, "ReplyAndReceive3"},
diff --git a/src/core/hw/aes/key.h b/src/core/hw/aes/key.h
index b01d04f13..c9f1342f4 100644
--- a/src/core/hw/aes/key.h
+++ b/src/core/hw/aes/key.h
@@ -12,6 +12,8 @@ namespace HW {
12namespace AES { 12namespace AES {
13 13
14enum KeySlotID : size_t { 14enum KeySlotID : size_t {
15 // AES Keyslot used to generate the UDS data frame CCMP key.
16 UDSDataKey = 0x2D,
15 APTWrap = 0x31, 17 APTWrap = 0x31,
16 18
17 MaxKeySlotID = 0x40, 19 MaxKeySlotID = 0x40,
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 42809c731..6838e449c 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -5,6 +5,7 @@
5#include <cstring> 5#include <cstring>
6#include <numeric> 6#include <numeric>
7#include <type_traits> 7#include <type_traits>
8#include "common/alignment.h"
8#include "common/color.h" 9#include "common/color.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
@@ -313,7 +314,7 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) {
313 const PAddr src_addr = config.GetPhysicalInputAddress(); 314 const PAddr src_addr = config.GetPhysicalInputAddress();
314 const PAddr dst_addr = config.GetPhysicalOutputAddress(); 315 const PAddr dst_addr = config.GetPhysicalOutputAddress();
315 316
316 // TODO: do hwtest with these cases 317 // TODO: do hwtest with invalid addresses
317 if (!Memory::IsValidPhysicalAddress(src_addr)) { 318 if (!Memory::IsValidPhysicalAddress(src_addr)) {
318 LOG_CRITICAL(HW_GPU, "invalid input address 0x%08X", src_addr); 319 LOG_CRITICAL(HW_GPU, "invalid input address 0x%08X", src_addr);
319 return; 320 return;
@@ -324,31 +325,36 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) {
324 return; 325 return;
325 } 326 }
326 327
327 if (config.texture_copy.input_width == 0) { 328 if (VideoCore::g_renderer->Rasterizer()->AccelerateTextureCopy(config))
328 LOG_CRITICAL(HW_GPU, "zero input width");
329 return; 329 return;
330 }
331 330
332 if (config.texture_copy.output_width == 0) { 331 u8* src_pointer = Memory::GetPhysicalPointer(src_addr);
333 LOG_CRITICAL(HW_GPU, "zero output width"); 332 u8* dst_pointer = Memory::GetPhysicalPointer(dst_addr);
333
334 u32 remaining_size = Common::AlignDown(config.texture_copy.size, 16);
335
336 if (remaining_size == 0) {
337 LOG_CRITICAL(HW_GPU, "zero size. Real hardware freezes on this.");
334 return; 338 return;
335 } 339 }
336 340
337 if (config.texture_copy.size == 0) { 341 u32 input_gap = config.texture_copy.input_gap * 16;
338 LOG_CRITICAL(HW_GPU, "zero size"); 342 u32 output_gap = config.texture_copy.output_gap * 16;
343
344 // Zero gap means contiguous input/output even if width = 0. To avoid infinite loop below, width
345 // is assigned with the total size if gap = 0.
346 u32 input_width = input_gap == 0 ? remaining_size : config.texture_copy.input_width * 16;
347 u32 output_width = output_gap == 0 ? remaining_size : config.texture_copy.output_width * 16;
348
349 if (input_width == 0) {
350 LOG_CRITICAL(HW_GPU, "zero input width. Real hardware freezes on this.");
339 return; 351 return;
340 } 352 }
341 353
342 if (VideoCore::g_renderer->Rasterizer()->AccelerateTextureCopy(config)) 354 if (output_width == 0) {
355 LOG_CRITICAL(HW_GPU, "zero output width. Real hardware freezes on this.");
343 return; 356 return;
344 357 }
345 u8* src_pointer = Memory::GetPhysicalPointer(src_addr);
346 u8* dst_pointer = Memory::GetPhysicalPointer(dst_addr);
347
348 u32 input_width = config.texture_copy.input_width * 16;
349 u32 input_gap = config.texture_copy.input_gap * 16;
350 u32 output_width = config.texture_copy.output_width * 16;
351 u32 output_gap = config.texture_copy.output_gap * 16;
352 358
353 size_t contiguous_input_size = 359 size_t contiguous_input_size =
354 config.texture_copy.size / input_width * (input_width + input_gap); 360 config.texture_copy.size / input_width * (input_width + input_gap);
@@ -360,7 +366,6 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) {
360 Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), 366 Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(),
361 static_cast<u32>(contiguous_output_size)); 367 static_cast<u32>(contiguous_output_size));
362 368
363 u32 remaining_size = config.texture_copy.size;
364 u32 remaining_input = input_width; 369 u32 remaining_input = input_width;
365 u32 remaining_output = output_width; 370 u32 remaining_output = output_width;
366 while (remaining_size > 0) { 371 while (remaining_size > 0) {
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index bdd997b2a..21b127fee 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -225,7 +225,7 @@ struct Regs {
225 INSERT_PADDING_WORDS(0x1); 225 INSERT_PADDING_WORDS(0x1);
226 226
227 struct { 227 struct {
228 u32 size; 228 u32 size; // The lower 4 bits are ignored
229 229
230 union { 230 union {
231 u32 input_size; 231 u32 input_size;
diff --git a/src/core/memory.h b/src/core/memory.h
index 802aa465e..71fb278ad 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -55,8 +55,10 @@ enum : PAddr {
55 55
56 /// Main FCRAM 56 /// Main FCRAM
57 FCRAM_PADDR = 0x20000000, 57 FCRAM_PADDR = 0x20000000,
58 FCRAM_SIZE = 0x08000000, ///< FCRAM size (128MB) 58 FCRAM_SIZE = 0x08000000, ///< FCRAM size on the Old 3DS (128MB)
59 FCRAM_N3DS_SIZE = 0x10000000, ///< FCRAM size on the New 3DS (256MB)
59 FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE, 60 FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE,
61 FCRAM_N3DS_PADDR_END = FCRAM_PADDR + FCRAM_N3DS_SIZE,
60}; 62};
61 63
62/// Virtual user-space memory regions 64/// Virtual user-space memory regions
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 00d7c636a..a14df325a 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,8 +1,9 @@
1set(SRCS 1set(SRCS
2 glad.cpp
3 tests.cpp
4 common/param_package.cpp 2 common/param_package.cpp
5 core/file_sys/path_parser.cpp 3 core/file_sys/path_parser.cpp
4 core/hle/kernel/hle_ipc.cpp
5 glad.cpp
6 tests.cpp
6 ) 7 )
7 8
8set(HEADERS 9set(HEADERS
diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp
new file mode 100644
index 000000000..52336d027
--- /dev/null
+++ b/src/tests/core/hle/kernel/hle_ipc.cpp
@@ -0,0 +1,216 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <catch.hpp>
6#include "core/hle/ipc.h"
7#include "core/hle/kernel/client_port.h"
8#include "core/hle/kernel/client_session.h"
9#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/handle_table.h"
11#include "core/hle/kernel/hle_ipc.h"
12#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/server_session.h"
14
15namespace Kernel {
16
17static SharedPtr<Object> MakeObject() {
18 return Event::Create(ResetType::OneShot);
19}
20
21TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") {
22 auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair());
23 HLERequestContext context(std::move(session));
24
25 auto process = Process::Create(CodeSet::Create("", 0));
26 HandleTable handle_table;
27
28 SECTION("works with empty cmdbuf") {
29 const u32_le input[]{
30 IPC::MakeHeader(0x1234, 0, 0),
31 };
32
33 context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
34
35 REQUIRE(context.CommandBuffer()[0] == 0x12340000);
36 }
37
38 SECTION("translates regular params") {
39 const u32_le input[]{
40 IPC::MakeHeader(0, 3, 0), 0x12345678, 0x21122112, 0xAABBCCDD,
41 };
42
43 context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
44
45 auto* output = context.CommandBuffer();
46 REQUIRE(output[1] == 0x12345678);
47 REQUIRE(output[2] == 0x21122112);
48 REQUIRE(output[3] == 0xAABBCCDD);
49 }
50
51 SECTION("translates move handles") {
52 auto a = MakeObject();
53 Handle a_handle = handle_table.Create(a).Unwrap();
54 const u32_le input[]{
55 IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), a_handle,
56 };
57
58 context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
59
60 auto* output = context.CommandBuffer();
61 REQUIRE(context.GetIncomingHandle(output[2]) == a);
62 REQUIRE(handle_table.GetGeneric(a_handle) == nullptr);
63 }
64
65 SECTION("translates copy handles") {
66 auto a = MakeObject();
67 Handle a_handle = handle_table.Create(a).Unwrap();
68 const u32_le input[]{
69 IPC::MakeHeader(0, 0, 2), IPC::CopyHandleDesc(1), a_handle,
70 };
71
72 context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
73
74 auto* output = context.CommandBuffer();
75 REQUIRE(context.GetIncomingHandle(output[2]) == a);
76 REQUIRE(handle_table.GetGeneric(a_handle) == a);
77 }
78
79 SECTION("translates multi-handle descriptors") {
80 auto a = MakeObject();
81 auto b = MakeObject();
82 auto c = MakeObject();
83 const u32_le input[]{
84 IPC::MakeHeader(0, 0, 5), IPC::MoveHandleDesc(2),
85 handle_table.Create(a).Unwrap(), handle_table.Create(b).Unwrap(),
86 IPC::MoveHandleDesc(1), handle_table.Create(c).Unwrap(),
87 };
88
89 context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
90
91 auto* output = context.CommandBuffer();
92 REQUIRE(context.GetIncomingHandle(output[2]) == a);
93 REQUIRE(context.GetIncomingHandle(output[3]) == b);
94 REQUIRE(context.GetIncomingHandle(output[5]) == c);
95 }
96
97 SECTION("translates null handles") {
98 const u32_le input[]{
99 IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), 0,
100 };
101
102 auto result = context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
103
104 REQUIRE(result == RESULT_SUCCESS);
105 auto* output = context.CommandBuffer();
106 REQUIRE(context.GetIncomingHandle(output[2]) == nullptr);
107 }
108
109 SECTION("translates CallingPid descriptors") {
110 const u32_le input[]{
111 IPC::MakeHeader(0, 0, 2), IPC::CallingPidDesc(), 0x98989898,
112 };
113
114 context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
115
116 REQUIRE(context.CommandBuffer()[2] == process->process_id);
117 }
118
119 SECTION("translates mixed params") {
120 auto a = MakeObject();
121 const u32_le input[]{
122 IPC::MakeHeader(0, 2, 4),
123 0x12345678,
124 0xABCDEF00,
125 IPC::MoveHandleDesc(1),
126 handle_table.Create(a).Unwrap(),
127 IPC::CallingPidDesc(),
128 0,
129 };
130
131 context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
132
133 auto* output = context.CommandBuffer();
134 REQUIRE(output[1] == 0x12345678);
135 REQUIRE(output[2] == 0xABCDEF00);
136 REQUIRE(context.GetIncomingHandle(output[4]) == a);
137 REQUIRE(output[6] == process->process_id);
138 }
139}
140
141TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
142 auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair());
143 HLERequestContext context(std::move(session));
144
145 auto process = Process::Create(CodeSet::Create("", 0));
146 HandleTable handle_table;
147 auto* input = context.CommandBuffer();
148 u32_le output[IPC::COMMAND_BUFFER_LENGTH];
149
150 SECTION("works with empty cmdbuf") {
151 input[0] = IPC::MakeHeader(0x1234, 0, 0);
152
153 context.WriteToOutgoingCommandBuffer(output, *process, handle_table);
154
155 REQUIRE(output[0] == 0x12340000);
156 }
157
158 SECTION("translates regular params") {
159 input[0] = IPC::MakeHeader(0, 3, 0);
160 input[1] = 0x12345678;
161 input[2] = 0x21122112;
162 input[3] = 0xAABBCCDD;
163
164 context.WriteToOutgoingCommandBuffer(output, *process, handle_table);
165
166 REQUIRE(output[1] == 0x12345678);
167 REQUIRE(output[2] == 0x21122112);
168 REQUIRE(output[3] == 0xAABBCCDD);
169 }
170
171 SECTION("translates move/copy handles") {
172 auto a = MakeObject();
173 auto b = MakeObject();
174 input[0] = IPC::MakeHeader(0, 0, 4);
175 input[1] = IPC::MoveHandleDesc(1);
176 input[2] = context.AddOutgoingHandle(a);
177 input[3] = IPC::CopyHandleDesc(1);
178 input[4] = context.AddOutgoingHandle(b);
179
180 context.WriteToOutgoingCommandBuffer(output, *process, handle_table);
181
182 REQUIRE(handle_table.GetGeneric(output[2]) == a);
183 REQUIRE(handle_table.GetGeneric(output[4]) == b);
184 }
185
186 SECTION("translates null handles") {
187 input[0] = IPC::MakeHeader(0, 0, 2);
188 input[1] = IPC::MoveHandleDesc(1);
189 input[2] = context.AddOutgoingHandle(nullptr);
190
191 auto result = context.WriteToOutgoingCommandBuffer(output, *process, handle_table);
192
193 REQUIRE(result == RESULT_SUCCESS);
194 REQUIRE(output[2] == 0);
195 }
196
197 SECTION("translates multi-handle descriptors") {
198 auto a = MakeObject();
199 auto b = MakeObject();
200 auto c = MakeObject();
201 input[0] = IPC::MakeHeader(0, 0, 5);
202 input[1] = IPC::MoveHandleDesc(2);
203 input[2] = context.AddOutgoingHandle(a);
204 input[3] = context.AddOutgoingHandle(b);
205 input[4] = IPC::CopyHandleDesc(1);
206 input[5] = context.AddOutgoingHandle(c);
207
208 context.WriteToOutgoingCommandBuffer(output, *process, handle_table);
209
210 REQUIRE(handle_table.GetGeneric(output[2]) == a);
211 REQUIRE(handle_table.GetGeneric(output[3]) == b);
212 REQUIRE(handle_table.GetGeneric(output[5]) == c);
213 }
214}
215
216} // namespace Kernel
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
index f46db09fb..2d23d34e6 100644
--- a/src/video_core/pica_state.h
+++ b/src/video_core/pica_state.h
@@ -87,12 +87,18 @@ struct State {
87 // LUT value, encoded as 12-bit fixed point, with 12 fraction bits 87 // LUT value, encoded as 12-bit fixed point, with 12 fraction bits
88 BitField<0, 12, u32> value; // 0.0.12 fixed point 88 BitField<0, 12, u32> value; // 0.0.12 fixed point
89 89
90 // Used by HW for efficient interpolation, Citra does not use these 90 // Used for efficient interpolation.
91 BitField<12, 12, s32> difference; // 1.0.11 fixed point 91 BitField<12, 11, u32> difference; // 0.0.11 fixed point
92 BitField<23, 1, u32> neg_difference;
92 93
93 float ToFloat() { 94 float ToFloat() const {
94 return static_cast<float>(value) / 4095.f; 95 return static_cast<float>(value) / 4095.f;
95 } 96 }
97
98 float DiffToFloat() const {
99 float diff = static_cast<float>(difference) / 2047.f;
100 return neg_difference ? -diff : diff;
101 }
96 }; 102 };
97 103
98 std::array<std::array<LutEntry, 256>, 24> luts; 104 std::array<std::array<LutEntry, 256>, 24> luts;
@@ -105,6 +111,14 @@ struct State {
105 111
106 BitField<0, 13, s32> difference; // 1.1.11 fixed point 112 BitField<0, 13, s32> difference; // 1.1.11 fixed point
107 BitField<13, 11, u32> value; // 0.0.11 fixed point 113 BitField<13, 11, u32> value; // 0.0.11 fixed point
114
115 float ToFloat() const {
116 return static_cast<float>(value) / 2047.0f;
117 }
118
119 float DiffToFloat() const {
120 return static_cast<float>(difference) / 2047.0f;
121 }
108 }; 122 };
109 123
110 std::array<LutEntry, 128> lut; 124 std::array<LutEntry, 128> lut;
diff --git a/src/video_core/regs_lighting.h b/src/video_core/regs_lighting.h
index fbfebc0a7..b89709cfe 100644
--- a/src/video_core/regs_lighting.h
+++ b/src/video_core/regs_lighting.h
@@ -26,6 +26,8 @@ struct LightingRegs {
26 DistanceAttenuation = 16, 26 DistanceAttenuation = 16,
27 }; 27 };
28 28
29 static constexpr unsigned NumLightingSampler = 24;
30
29 static LightingSampler SpotlightAttenuationSampler(unsigned index) { 31 static LightingSampler SpotlightAttenuationSampler(unsigned index) {
30 return static_cast<LightingSampler>( 32 return static_cast<LightingSampler>(
31 static_cast<unsigned>(LightingSampler::SpotlightAttenuation) + index); 33 static_cast<unsigned>(LightingSampler::SpotlightAttenuation) + index);
@@ -84,7 +86,7 @@ struct LightingRegs {
84 NV = 2, // Cosine of the angle between the normal and the view vector 86 NV = 2, // Cosine of the angle between the normal and the view vector
85 LN = 3, // Cosine of the angle between the light and the normal vectors 87 LN = 3, // Cosine of the angle between the light and the normal vectors
86 SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors 88 SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors
87 CP = 5, // TODO: document and implement 89 CP = 5, // Cosine of the angle between the tangent and projection of half-angle vectors
88 }; 90 };
89 91
90 enum class LightingBumpMode : u32 { 92 enum class LightingBumpMode : u32 {
@@ -168,6 +170,8 @@ struct LightingRegs {
168 union { 170 union {
169 BitField<0, 1, u32> directional; 171 BitField<0, 1, u32> directional;
170 BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0 172 BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0
173 BitField<2, 1, u32> geometric_factor_0;
174 BitField<3, 1, u32> geometric_factor_1;
171 } config; 175 } config;
172 176
173 BitField<0, 20, u32> dist_atten_bias; 177 BitField<0, 20, u32> dist_atten_bias;
diff --git a/src/video_core/regs_texturing.h b/src/video_core/regs_texturing.h
index 3f5355fa9..0b09f2299 100644
--- a/src/video_core/regs_texturing.h
+++ b/src/video_core/regs_texturing.h
@@ -30,10 +30,10 @@ struct TexturingRegs {
30 Repeat = 2, 30 Repeat = 2,
31 MirroredRepeat = 3, 31 MirroredRepeat = 3,
32 // Mode 4-7 produces some weird result and may be just invalid: 32 // Mode 4-7 produces some weird result and may be just invalid:
33 // 4: Positive coord: clamp to edge; negative coord: repeat 33 ClampToEdge2 = 4, // Positive coord: clamp to edge; negative coord: repeat
34 // 5: Positive coord: clamp to border; negative coord: repeat 34 ClampToBorder2 = 5, // Positive coord: clamp to border; negative coord: repeat
35 // 6: Repeat 35 Repeat2 = 6, // Same as Repeat
36 // 7: Repeat 36 Repeat3 = 7, // Same as Repeat
37 }; 37 };
38 38
39 enum TextureFilter : u32 { 39 enum TextureFilter : u32 {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e6cccebf6..ff3f69ba3 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -49,9 +49,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
49 49
50 uniform_block_data.dirty = true; 50 uniform_block_data.dirty = true;
51 51
52 for (unsigned index = 0; index < lighting_luts.size(); index++) { 52 uniform_block_data.lut_dirty.fill(true);
53 uniform_block_data.lut_dirty[index] = true;
54 }
55 53
56 uniform_block_data.fog_lut_dirty = true; 54 uniform_block_data.fog_lut_dirty = true;
57 55
@@ -96,36 +94,32 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
96 framebuffer.Create(); 94 framebuffer.Create();
97 95
98 // Allocate and bind lighting lut textures 96 // Allocate and bind lighting lut textures
99 for (size_t i = 0; i < lighting_luts.size(); ++i) { 97 lighting_lut.Create();
100 lighting_luts[i].Create(); 98 state.lighting_lut.texture_buffer = lighting_lut.handle;
101 state.lighting_luts[i].texture_1d = lighting_luts[i].handle;
102 }
103 state.Apply(); 99 state.Apply();
104 100 lighting_lut_buffer.Create();
105 for (size_t i = 0; i < lighting_luts.size(); ++i) { 101 glBindBuffer(GL_TEXTURE_BUFFER, lighting_lut_buffer.handle);
106 glActiveTexture(static_cast<GLenum>(GL_TEXTURE3 + i)); 102 glBufferData(GL_TEXTURE_BUFFER,
107 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); 103 sizeof(GLfloat) * 2 * 256 * Pica::LightingRegs::NumLightingSampler, nullptr,
108 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 104 GL_DYNAMIC_DRAW);
109 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 105 glActiveTexture(TextureUnits::LightingLUT.Enum());
110 } 106 glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, lighting_lut_buffer.handle);
111 107
112 // Setup the LUT for the fog 108 // Setup the LUT for the fog
113 { 109 fog_lut.Create();
114 fog_lut.Create(); 110 state.fog_lut.texture_buffer = fog_lut.handle;
115 state.fog_lut.texture_1d = fog_lut.handle;
116 }
117 state.Apply(); 111 state.Apply();
118 112 fog_lut_buffer.Create();
119 glActiveTexture(GL_TEXTURE9); 113 glBindBuffer(GL_TEXTURE_BUFFER, fog_lut_buffer.handle);
120 glTexImage1D(GL_TEXTURE_1D, 0, GL_R32UI, 128, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr); 114 glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat) * 2 * 128, nullptr, GL_DYNAMIC_DRAW);
121 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 115 glActiveTexture(TextureUnits::FogLUT.Enum());
122 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 116 glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, fog_lut_buffer.handle);
123 117
124 // Setup the noise LUT for proctex 118 // Setup the noise LUT for proctex
125 proctex_noise_lut.Create(); 119 proctex_noise_lut.Create();
126 state.proctex_noise_lut.texture_1d = proctex_noise_lut.handle; 120 state.proctex_noise_lut.texture_1d = proctex_noise_lut.handle;
127 state.Apply(); 121 state.Apply();
128 glActiveTexture(GL_TEXTURE10); 122 glActiveTexture(TextureUnits::ProcTexNoiseLUT.Enum());
129 glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr); 123 glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr);
130 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 124 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
131 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 125 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -134,7 +128,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
134 proctex_color_map.Create(); 128 proctex_color_map.Create();
135 state.proctex_color_map.texture_1d = proctex_color_map.handle; 129 state.proctex_color_map.texture_1d = proctex_color_map.handle;
136 state.Apply(); 130 state.Apply();
137 glActiveTexture(GL_TEXTURE11); 131 glActiveTexture(TextureUnits::ProcTexColorMap.Enum());
138 glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr); 132 glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr);
139 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 133 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
140 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 134 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -143,7 +137,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
143 proctex_alpha_map.Create(); 137 proctex_alpha_map.Create();
144 state.proctex_alpha_map.texture_1d = proctex_alpha_map.handle; 138 state.proctex_alpha_map.texture_1d = proctex_alpha_map.handle;
145 state.Apply(); 139 state.Apply();
146 glActiveTexture(GL_TEXTURE12); 140 glActiveTexture(TextureUnits::ProcTexAlphaMap.Enum());
147 glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr); 141 glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr);
148 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 142 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
149 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 143 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -152,7 +146,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
152 proctex_lut.Create(); 146 proctex_lut.Create();
153 state.proctex_lut.texture_1d = proctex_lut.handle; 147 state.proctex_lut.texture_1d = proctex_lut.handle;
154 state.Apply(); 148 state.Apply();
155 glActiveTexture(GL_TEXTURE13); 149 glActiveTexture(TextureUnits::ProcTexLUT.Enum());
156 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); 150 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);
157 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 151 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
158 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 152 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -161,7 +155,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
161 proctex_diff_lut.Create(); 155 proctex_diff_lut.Create();
162 state.proctex_diff_lut.texture_1d = proctex_diff_lut.handle; 156 state.proctex_diff_lut.texture_1d = proctex_diff_lut.handle;
163 state.Apply(); 157 state.Apply();
164 glActiveTexture(GL_TEXTURE14); 158 glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum());
165 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); 159 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);
166 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 160 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
167 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 161 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -313,7 +307,7 @@ void RasterizerOpenGL::DrawTriangles() {
313 } 307 }
314 308
315 // Sync the lighting luts 309 // Sync the lighting luts
316 for (unsigned index = 0; index < lighting_luts.size(); index++) { 310 for (unsigned index = 0; index < uniform_block_data.lut_dirty.size(); index++) {
317 if (uniform_block_data.lut_dirty[index]) { 311 if (uniform_block_data.lut_dirty[index]) {
318 SyncLightingLUT(index); 312 SyncLightingLUT(index);
319 uniform_block_data.lut_dirty[index] = false; 313 uniform_block_data.lut_dirty[index] = false;
@@ -851,7 +845,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
851 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce): 845 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce):
852 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf): { 846 case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf): {
853 auto& lut_config = regs.lighting.lut_config; 847 auto& lut_config = regs.lighting.lut_config;
854 uniform_block_data.lut_dirty[lut_config.type / 4] = true; 848 uniform_block_data.lut_dirty[lut_config.type] = true;
855 break; 849 break;
856 } 850 }
857 } 851 }
@@ -1187,77 +1181,57 @@ void RasterizerOpenGL::SetShader() {
1187 state.Apply(); 1181 state.Apply();
1188 1182
1189 // Set the texture samplers to correspond to different texture units 1183 // Set the texture samplers to correspond to different texture units
1190 GLuint uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[0]"); 1184 GLint uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[0]");
1191 if (uniform_tex != -1) { 1185 if (uniform_tex != -1) {
1192 glUniform1i(uniform_tex, 0); 1186 glUniform1i(uniform_tex, TextureUnits::PicaTexture(0).id);
1193 } 1187 }
1194 uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[1]"); 1188 uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[1]");
1195 if (uniform_tex != -1) { 1189 if (uniform_tex != -1) {
1196 glUniform1i(uniform_tex, 1); 1190 glUniform1i(uniform_tex, TextureUnits::PicaTexture(1).id);
1197 } 1191 }
1198 uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]"); 1192 uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]");
1199 if (uniform_tex != -1) { 1193 if (uniform_tex != -1) {
1200 glUniform1i(uniform_tex, 2); 1194 glUniform1i(uniform_tex, TextureUnits::PicaTexture(2).id);
1201 } 1195 }
1202 1196
1203 // Set the texture samplers to correspond to different lookup table texture units 1197 // Set the texture samplers to correspond to different lookup table texture units
1204 GLuint uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[0]"); 1198 GLint uniform_lut = glGetUniformLocation(shader->shader.handle, "lighting_lut");
1205 if (uniform_lut != -1) {
1206 glUniform1i(uniform_lut, 3);
1207 }
1208 uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[1]");
1209 if (uniform_lut != -1) {
1210 glUniform1i(uniform_lut, 4);
1211 }
1212 uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[2]");
1213 if (uniform_lut != -1) {
1214 glUniform1i(uniform_lut, 5);
1215 }
1216 uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[3]");
1217 if (uniform_lut != -1) {
1218 glUniform1i(uniform_lut, 6);
1219 }
1220 uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[4]");
1221 if (uniform_lut != -1) { 1199 if (uniform_lut != -1) {
1222 glUniform1i(uniform_lut, 7); 1200 glUniform1i(uniform_lut, TextureUnits::LightingLUT.id);
1223 }
1224 uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[5]");
1225 if (uniform_lut != -1) {
1226 glUniform1i(uniform_lut, 8);
1227 } 1201 }
1228 1202
1229 GLuint uniform_fog_lut = glGetUniformLocation(shader->shader.handle, "fog_lut"); 1203 GLint uniform_fog_lut = glGetUniformLocation(shader->shader.handle, "fog_lut");
1230 if (uniform_fog_lut != -1) { 1204 if (uniform_fog_lut != -1) {
1231 glUniform1i(uniform_fog_lut, 9); 1205 glUniform1i(uniform_fog_lut, TextureUnits::FogLUT.id);
1232 } 1206 }
1233 1207
1234 GLuint uniform_proctex_noise_lut = 1208 GLint uniform_proctex_noise_lut =
1235 glGetUniformLocation(shader->shader.handle, "proctex_noise_lut"); 1209 glGetUniformLocation(shader->shader.handle, "proctex_noise_lut");
1236 if (uniform_proctex_noise_lut != -1) { 1210 if (uniform_proctex_noise_lut != -1) {
1237 glUniform1i(uniform_proctex_noise_lut, 10); 1211 glUniform1i(uniform_proctex_noise_lut, TextureUnits::ProcTexNoiseLUT.id);
1238 } 1212 }
1239 1213
1240 GLuint uniform_proctex_color_map = 1214 GLint uniform_proctex_color_map =
1241 glGetUniformLocation(shader->shader.handle, "proctex_color_map"); 1215 glGetUniformLocation(shader->shader.handle, "proctex_color_map");
1242 if (uniform_proctex_color_map != -1) { 1216 if (uniform_proctex_color_map != -1) {
1243 glUniform1i(uniform_proctex_color_map, 11); 1217 glUniform1i(uniform_proctex_color_map, TextureUnits::ProcTexColorMap.id);
1244 } 1218 }
1245 1219
1246 GLuint uniform_proctex_alpha_map = 1220 GLint uniform_proctex_alpha_map =
1247 glGetUniformLocation(shader->shader.handle, "proctex_alpha_map"); 1221 glGetUniformLocation(shader->shader.handle, "proctex_alpha_map");
1248 if (uniform_proctex_alpha_map != -1) { 1222 if (uniform_proctex_alpha_map != -1) {
1249 glUniform1i(uniform_proctex_alpha_map, 12); 1223 glUniform1i(uniform_proctex_alpha_map, TextureUnits::ProcTexAlphaMap.id);
1250 } 1224 }
1251 1225
1252 GLuint uniform_proctex_lut = glGetUniformLocation(shader->shader.handle, "proctex_lut"); 1226 GLint uniform_proctex_lut = glGetUniformLocation(shader->shader.handle, "proctex_lut");
1253 if (uniform_proctex_lut != -1) { 1227 if (uniform_proctex_lut != -1) {
1254 glUniform1i(uniform_proctex_lut, 13); 1228 glUniform1i(uniform_proctex_lut, TextureUnits::ProcTexLUT.id);
1255 } 1229 }
1256 1230
1257 GLuint uniform_proctex_diff_lut = 1231 GLint uniform_proctex_diff_lut =
1258 glGetUniformLocation(shader->shader.handle, "proctex_diff_lut"); 1232 glGetUniformLocation(shader->shader.handle, "proctex_diff_lut");
1259 if (uniform_proctex_diff_lut != -1) { 1233 if (uniform_proctex_diff_lut != -1) {
1260 glUniform1i(uniform_proctex_diff_lut, 14); 1234 glUniform1i(uniform_proctex_diff_lut, TextureUnits::ProcTexDiffLUT.id);
1261 } 1235 }
1262 1236
1263 current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); 1237 current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
@@ -1380,16 +1354,17 @@ void RasterizerOpenGL::SyncFogColor() {
1380} 1354}
1381 1355
1382void RasterizerOpenGL::SyncFogLUT() { 1356void RasterizerOpenGL::SyncFogLUT() {
1383 std::array<GLuint, 128> new_data; 1357 std::array<GLvec2, 128> new_data;
1384 1358
1385 std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(), 1359 std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(),
1386 [](const auto& entry) { return entry.raw; }); 1360 [](const auto& entry) {
1361 return GLvec2{entry.ToFloat(), entry.DiffToFloat()};
1362 });
1387 1363
1388 if (new_data != fog_lut_data) { 1364 if (new_data != fog_lut_data) {
1389 fog_lut_data = new_data; 1365 fog_lut_data = new_data;
1390 glActiveTexture(GL_TEXTURE9); 1366 glBindBuffer(GL_TEXTURE_BUFFER, fog_lut_buffer.handle);
1391 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 128, GL_RED_INTEGER, GL_UNSIGNED_INT, 1367 glBufferSubData(GL_TEXTURE_BUFFER, 0, new_data.size() * sizeof(GLvec2), new_data.data());
1392 fog_lut_data.data());
1393 } 1368 }
1394} 1369}
1395 1370
@@ -1426,17 +1401,18 @@ static void SyncProcTexValueLUT(const std::array<Pica::State::ProcTex::ValueEntr
1426} 1401}
1427 1402
1428void RasterizerOpenGL::SyncProcTexNoiseLUT() { 1403void RasterizerOpenGL::SyncProcTexNoiseLUT() {
1429 SyncProcTexValueLUT(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, GL_TEXTURE10); 1404 SyncProcTexValueLUT(Pica::g_state.proctex.noise_table, proctex_noise_lut_data,
1405 TextureUnits::ProcTexNoiseLUT.Enum());
1430} 1406}
1431 1407
1432void RasterizerOpenGL::SyncProcTexColorMap() { 1408void RasterizerOpenGL::SyncProcTexColorMap() {
1433 SyncProcTexValueLUT(Pica::g_state.proctex.color_map_table, proctex_color_map_data, 1409 SyncProcTexValueLUT(Pica::g_state.proctex.color_map_table, proctex_color_map_data,
1434 GL_TEXTURE11); 1410 TextureUnits::ProcTexColorMap.Enum());
1435} 1411}
1436 1412
1437void RasterizerOpenGL::SyncProcTexAlphaMap() { 1413void RasterizerOpenGL::SyncProcTexAlphaMap() {
1438 SyncProcTexValueLUT(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data, 1414 SyncProcTexValueLUT(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data,
1439 GL_TEXTURE12); 1415 TextureUnits::ProcTexAlphaMap.Enum());
1440} 1416}
1441 1417
1442void RasterizerOpenGL::SyncProcTexLUT() { 1418void RasterizerOpenGL::SyncProcTexLUT() {
@@ -1451,7 +1427,7 @@ void RasterizerOpenGL::SyncProcTexLUT() {
1451 1427
1452 if (new_data != proctex_lut_data) { 1428 if (new_data != proctex_lut_data) {
1453 proctex_lut_data = new_data; 1429 proctex_lut_data = new_data;
1454 glActiveTexture(GL_TEXTURE13); 1430 glActiveTexture(TextureUnits::ProcTexLUT.Enum());
1455 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_lut_data.data()); 1431 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_lut_data.data());
1456 } 1432 }
1457} 1433}
@@ -1468,7 +1444,7 @@ void RasterizerOpenGL::SyncProcTexDiffLUT() {
1468 1444
1469 if (new_data != proctex_diff_lut_data) { 1445 if (new_data != proctex_diff_lut_data) {
1470 proctex_diff_lut_data = new_data; 1446 proctex_diff_lut_data = new_data;
1471 glActiveTexture(GL_TEXTURE14); 1447 glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum());
1472 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_diff_lut_data.data()); 1448 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_diff_lut_data.data());
1473 } 1449 }
1474} 1450}
@@ -1571,20 +1547,17 @@ void RasterizerOpenGL::SyncGlobalAmbient() {
1571} 1547}
1572 1548
1573void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) { 1549void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) {
1574 std::array<GLvec4, 256> new_data; 1550 std::array<GLvec2, 256> new_data;
1575 1551 const auto& source_lut = Pica::g_state.lighting.luts[lut_index];
1576 for (unsigned offset = 0; offset < new_data.size(); ++offset) { 1552 std::transform(source_lut.begin(), source_lut.end(), new_data.begin(), [](const auto& entry) {
1577 new_data[offset][0] = Pica::g_state.lighting.luts[(lut_index * 4) + 0][offset].ToFloat(); 1553 return GLvec2{entry.ToFloat(), entry.DiffToFloat()};
1578 new_data[offset][1] = Pica::g_state.lighting.luts[(lut_index * 4) + 1][offset].ToFloat(); 1554 });
1579 new_data[offset][2] = Pica::g_state.lighting.luts[(lut_index * 4) + 2][offset].ToFloat();
1580 new_data[offset][3] = Pica::g_state.lighting.luts[(lut_index * 4) + 3][offset].ToFloat();
1581 }
1582 1555
1583 if (new_data != lighting_lut_data[lut_index]) { 1556 if (new_data != lighting_lut_data[lut_index]) {
1584 lighting_lut_data[lut_index] = new_data; 1557 lighting_lut_data[lut_index] = new_data;
1585 glActiveTexture(GL_TEXTURE3 + lut_index); 1558 glBindBuffer(GL_TEXTURE_BUFFER, lighting_lut_buffer.handle);
1586 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, 1559 glBufferSubData(GL_TEXTURE_BUFFER, lut_index * new_data.size() * sizeof(GLvec2),
1587 lighting_lut_data[lut_index].data()); 1560 new_data.size() * sizeof(GLvec2), new_data.data());
1588 } 1561 }
1589} 1562}
1590 1563
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index d9a3e9d1c..a433c1d4a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -263,7 +263,7 @@ private:
263 263
264 struct { 264 struct {
265 UniformData data; 265 UniformData data;
266 bool lut_dirty[6]; 266 std::array<bool, Pica::LightingRegs::NumLightingSampler> lut_dirty;
267 bool fog_lut_dirty; 267 bool fog_lut_dirty;
268 bool proctex_noise_lut_dirty; 268 bool proctex_noise_lut_dirty;
269 bool proctex_color_map_dirty; 269 bool proctex_color_map_dirty;
@@ -279,11 +279,13 @@ private:
279 OGLBuffer uniform_buffer; 279 OGLBuffer uniform_buffer;
280 OGLFramebuffer framebuffer; 280 OGLFramebuffer framebuffer;
281 281
282 std::array<OGLTexture, 6> lighting_luts; 282 OGLBuffer lighting_lut_buffer;
283 std::array<std::array<GLvec4, 256>, 6> lighting_lut_data{}; 283 OGLTexture lighting_lut;
284 std::array<std::array<GLvec2, 256>, Pica::LightingRegs::NumLightingSampler> lighting_lut_data{};
284 285
286 OGLBuffer fog_lut_buffer;
285 OGLTexture fog_lut; 287 OGLTexture fog_lut;
286 std::array<GLuint, 128> fog_lut_data{}; 288 std::array<GLvec2, 128> fog_lut_data{};
287 289
288 OGLTexture proctex_noise_lut; 290 OGLTexture proctex_noise_lut;
289 std::array<GLvec2, 128> proctex_noise_lut_data{}; 291 std::array<GLvec2, 128> proctex_noise_lut_data{};
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index db53710aa..c93b108fb 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -73,6 +73,8 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) {
73 state.lighting.light[light_index].num = num; 73 state.lighting.light[light_index].num = num;
74 state.lighting.light[light_index].directional = light.config.directional != 0; 74 state.lighting.light[light_index].directional = light.config.directional != 0;
75 state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; 75 state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0;
76 state.lighting.light[light_index].geometric_factor_0 = light.config.geometric_factor_0 != 0;
77 state.lighting.light[light_index].geometric_factor_1 = light.config.geometric_factor_1 != 0;
76 state.lighting.light[light_index].dist_atten_enable = 78 state.lighting.light[light_index].dist_atten_enable =
77 !regs.lighting.IsDistAttenDisabled(num); 79 !regs.lighting.IsDistAttenDisabled(num);
78 state.lighting.light[light_index].spot_atten_enable = 80 state.lighting.light[light_index].spot_atten_enable =
@@ -518,14 +520,16 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
518 "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" 520 "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"
519 "vec3 light_vector = vec3(0.0);\n" 521 "vec3 light_vector = vec3(0.0);\n"
520 "vec3 refl_value = vec3(0.0);\n" 522 "vec3 refl_value = vec3(0.0);\n"
521 "vec3 spot_dir = vec3(0.0);\n;"; 523 "vec3 spot_dir = vec3(0.0);\n"
524 "vec3 half_vector = vec3(0.0);\n"
525 "float geo_factor = 1.0;\n";
522 526
523 // Compute fragment normals 527 // Compute fragment normals and tangents
528 const std::string pertubation =
529 "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0";
524 if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { 530 if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) {
525 // Bump mapping is enabled using a normal map, read perturbation vector from the selected 531 // Bump mapping is enabled using a normal map
526 // texture 532 out += "vec3 surface_normal = " + pertubation + ";\n";
527 out += "vec3 surface_normal = 2.0 * (" + SampleTexture(config, lighting.bump_selector) +
528 ").rgb - 1.0;\n";
529 533
530 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher 534 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher
531 // precision result 535 // precision result
@@ -534,31 +538,41 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
534 "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))"; 538 "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))";
535 out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n"; 539 out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n";
536 } 540 }
541
542 // The tangent vector is not perturbed by the normal map and is just a unit vector.
543 out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n";
537 } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { 544 } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) {
538 // Bump mapping is enabled using a tangent map 545 // Bump mapping is enabled using a tangent map
539 LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)"); 546 out += "vec3 surface_tangent = " + pertubation + ";\n";
540 UNIMPLEMENTED(); 547 // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant
548 // computation below, which is also confirmed on 3DS. So we don't bother recomputing here
549 // even if 'renorm' is enabled.
550
551 // The normal vector is not perturbed by the tangent map and is just a unit vector.
552 out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n";
541 } else { 553 } else {
542 // No bump mapping - surface local normal is just a unit normal 554 // No bump mapping - surface local normal and tangent are just unit vectors
543 out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; 555 out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n";
556 out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n";
544 } 557 }
545 558
546 // Rotate the surface-local normal by the interpolated normal quaternion to convert it to 559 // Rotate the surface-local normal by the interpolated normal quaternion to convert it to
547 // eyespace. 560 // eyespace.
548 out += "vec3 normal = quaternion_rotate(normalize(normquat), surface_normal);\n"; 561 out += "vec4 normalized_normquat = normalize(normquat);\n";
562 out += "vec3 normal = quaternion_rotate(normalized_normquat, surface_normal);\n";
563 out += "vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n";
549 564
550 // Gets the index into the specified lookup table for specular lighting 565 // Samples the specified lookup table for specular lighting
551 auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input, 566 auto GetLutValue = [&lighting](LightingRegs::LightingSampler sampler, unsigned light_num,
552 bool abs) { 567 LightingRegs::LightingLutInput input, bool abs) {
553 const std::string half_angle = "normalize(normalize(view) + light_vector)";
554 std::string index; 568 std::string index;
555 switch (input) { 569 switch (input) {
556 case LightingRegs::LightingLutInput::NH: 570 case LightingRegs::LightingLutInput::NH:
557 index = "dot(normal, " + half_angle + ")"; 571 index = "dot(normal, normalize(half_vector))";
558 break; 572 break;
559 573
560 case LightingRegs::LightingLutInput::VH: 574 case LightingRegs::LightingLutInput::VH:
561 index = std::string("dot(normalize(view), " + half_angle + ")"); 575 index = std::string("dot(normalize(view), normalize(half_vector))");
562 break; 576 break;
563 577
564 case LightingRegs::LightingLutInput::NV: 578 case LightingRegs::LightingLutInput::NV:
@@ -573,6 +587,22 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
573 index = std::string("dot(light_vector, spot_dir)"); 587 index = std::string("dot(light_vector, spot_dir)");
574 break; 588 break;
575 589
590 case LightingRegs::LightingLutInput::CP:
591 // CP input is only available with configuration 7
592 if (lighting.config == LightingRegs::LightingConfig::Config7) {
593 // Note: even if the normal vector is modified by normal map, which is not the
594 // normal of the tangent plane anymore, the half angle vector is still projected
595 // using the modified normal vector.
596 std::string half_angle_proj = "normalize(half_vector) - normal / dot(normal, "
597 "normal) * dot(normal, normalize(half_vector))";
598 // Note: the half angle vector projection is confirmed not normalized before the dot
599 // product. The result is in fact not cos(phi) as the name suggested.
600 index = "dot(" + half_angle_proj + ", tangent)";
601 } else {
602 index = "0.0";
603 }
604 break;
605
576 default: 606 default:
577 LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); 607 LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input);
578 UNIMPLEMENTED(); 608 UNIMPLEMENTED();
@@ -580,22 +610,18 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
580 break; 610 break;
581 } 611 }
582 612
613 std::string sampler_string = std::to_string(static_cast<unsigned>(sampler));
614
583 if (abs) { 615 if (abs) {
584 // LUT index is in the range of (0.0, 1.0) 616 // LUT index is in the range of (0.0, 1.0)
585 index = lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" 617 index = lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")"
586 : "max(" + index + ", 0.0)"; 618 : "max(" + index + ", 0.0)";
619 return "LookupLightingLUTUnsigned(" + sampler_string + ", " + index + ")";
587 } else { 620 } else {
588 // LUT index is in the range of (-1.0, 1.0) 621 // LUT index is in the range of (-1.0, 1.0)
589 index = "((" + index + " < 0) ? " + index + " + 2.0 : " + index + ") / 2.0"; 622 return "LookupLightingLUTSigned(" + sampler_string + ", " + index + ")";
590 } 623 }
591 624
592 return "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))";
593 };
594
595 // Gets the lighting lookup table value given the specified sampler and index
596 auto GetLutValue = [](LightingRegs::LightingSampler sampler, std::string lut_index) {
597 return std::string("texture(lut[" + std::to_string((unsigned)sampler / 4) + "], " +
598 lut_index + ")[" + std::to_string((unsigned)sampler & 3) + "]");
599 }; 625 };
600 626
601 // Write the code to emulate each enabled light 627 // Write the code to emulate each enabled light
@@ -610,6 +636,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
610 out += "light_vector = normalize(" + light_src + ".position + view);\n"; 636 out += "light_vector = normalize(" + light_src + ".position + view);\n";
611 637
612 out += "spot_dir = " + light_src + ".spot_direction;\n"; 638 out += "spot_dir = " + light_src + ".spot_direction;\n";
639 out += "half_vector = normalize(view) + light_vector;\n";
613 640
614 // Compute dot product of light_vector and normal, adjust if lighting is one-sided or 641 // Compute dot product of light_vector and normal, adjust if lighting is one-sided or
615 // two-sided 642 // two-sided
@@ -622,48 +649,57 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
622 if (light_config.spot_atten_enable && 649 if (light_config.spot_atten_enable &&
623 LightingRegs::IsLightingSamplerSupported( 650 LightingRegs::IsLightingSamplerSupported(
624 lighting.config, LightingRegs::LightingSampler::SpotlightAttenuation)) { 651 lighting.config, LightingRegs::LightingSampler::SpotlightAttenuation)) {
625 std::string index = 652 std::string value =
626 GetLutIndex(light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input); 653 GetLutValue(LightingRegs::SpotlightAttenuationSampler(light_config.num),
627 auto sampler = LightingRegs::SpotlightAttenuationSampler(light_config.num); 654 light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input);
628 spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + 655 spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + value + ")";
629 GetLutValue(sampler, index) + ")";
630 } 656 }
631 657
632 // If enabled, compute distance attenuation value 658 // If enabled, compute distance attenuation value
633 std::string dist_atten = "1.0"; 659 std::string dist_atten = "1.0";
634 if (light_config.dist_atten_enable) { 660 if (light_config.dist_atten_enable) {
635 std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " + 661 std::string index = "clamp(" + light_src + ".dist_atten_scale * length(-view - " +
636 light_src + ".position) + " + light_src + ".dist_atten_bias)"; 662 light_src + ".position) + " + light_src +
637 index = "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; 663 ".dist_atten_bias, 0.0, 1.0)";
638 auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num); 664 auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num);
639 dist_atten = GetLutValue(sampler, index); 665 dist_atten = "LookupLightingLUTUnsigned(" +
666 std::to_string(static_cast<unsigned>(sampler)) + "," + index + ")";
640 } 667 }
641 668
642 // If enabled, clamp specular component if lighting result is negative 669 // If enabled, clamp specular component if lighting result is negative
643 std::string clamp_highlights = 670 std::string clamp_highlights =
644 lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0"; 671 lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0";
645 672
673 if (light_config.geometric_factor_0 || light_config.geometric_factor_1) {
674 out += "geo_factor = dot(half_vector, half_vector);\n"
675 "geo_factor = geo_factor == 0.0 ? 0.0 : min(" +
676 dot_product + " / geo_factor, 1.0);\n";
677 }
678
646 // Specular 0 component 679 // Specular 0 component
647 std::string d0_lut_value = "1.0"; 680 std::string d0_lut_value = "1.0";
648 if (lighting.lut_d0.enable && 681 if (lighting.lut_d0.enable &&
649 LightingRegs::IsLightingSamplerSupported( 682 LightingRegs::IsLightingSamplerSupported(
650 lighting.config, LightingRegs::LightingSampler::Distribution0)) { 683 lighting.config, LightingRegs::LightingSampler::Distribution0)) {
651 // Lookup specular "distribution 0" LUT value 684 // Lookup specular "distribution 0" LUT value
652 std::string index = 685 std::string value =
653 GetLutIndex(light_config.num, lighting.lut_d0.type, lighting.lut_d0.abs_input); 686 GetLutValue(LightingRegs::LightingSampler::Distribution0, light_config.num,
654 d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " + 687 lighting.lut_d0.type, lighting.lut_d0.abs_input);
655 GetLutValue(LightingRegs::LightingSampler::Distribution0, index) + ")"; 688 d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " + value + ")";
656 } 689 }
657 std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; 690 std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)";
691 if (light_config.geometric_factor_0) {
692 specular_0 = "(" + specular_0 + " * geo_factor)";
693 }
658 694
659 // If enabled, lookup ReflectRed value, otherwise, 1.0 is used 695 // If enabled, lookup ReflectRed value, otherwise, 1.0 is used
660 if (lighting.lut_rr.enable && 696 if (lighting.lut_rr.enable &&
661 LightingRegs::IsLightingSamplerSupported(lighting.config, 697 LightingRegs::IsLightingSamplerSupported(lighting.config,
662 LightingRegs::LightingSampler::ReflectRed)) { 698 LightingRegs::LightingSampler::ReflectRed)) {
663 std::string index = 699 std::string value =
664 GetLutIndex(light_config.num, lighting.lut_rr.type, lighting.lut_rr.abs_input); 700 GetLutValue(LightingRegs::LightingSampler::ReflectRed, light_config.num,
665 std::string value = "(" + std::to_string(lighting.lut_rr.scale) + " * " + 701 lighting.lut_rr.type, lighting.lut_rr.abs_input);
666 GetLutValue(LightingRegs::LightingSampler::ReflectRed, index) + ")"; 702 value = "(" + std::to_string(lighting.lut_rr.scale) + " * " + value + ")";
667 out += "refl_value.r = " + value + ";\n"; 703 out += "refl_value.r = " + value + ";\n";
668 } else { 704 } else {
669 out += "refl_value.r = 1.0;\n"; 705 out += "refl_value.r = 1.0;\n";
@@ -673,11 +709,10 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
673 if (lighting.lut_rg.enable && 709 if (lighting.lut_rg.enable &&
674 LightingRegs::IsLightingSamplerSupported(lighting.config, 710 LightingRegs::IsLightingSamplerSupported(lighting.config,
675 LightingRegs::LightingSampler::ReflectGreen)) { 711 LightingRegs::LightingSampler::ReflectGreen)) {
676 std::string index = 712 std::string value =
677 GetLutIndex(light_config.num, lighting.lut_rg.type, lighting.lut_rg.abs_input); 713 GetLutValue(LightingRegs::LightingSampler::ReflectGreen, light_config.num,
678 std::string value = "(" + std::to_string(lighting.lut_rg.scale) + " * " + 714 lighting.lut_rg.type, lighting.lut_rg.abs_input);
679 GetLutValue(LightingRegs::LightingSampler::ReflectGreen, index) + 715 value = "(" + std::to_string(lighting.lut_rg.scale) + " * " + value + ")";
680 ")";
681 out += "refl_value.g = " + value + ";\n"; 716 out += "refl_value.g = " + value + ";\n";
682 } else { 717 } else {
683 out += "refl_value.g = refl_value.r;\n"; 718 out += "refl_value.g = refl_value.r;\n";
@@ -687,11 +722,10 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
687 if (lighting.lut_rb.enable && 722 if (lighting.lut_rb.enable &&
688 LightingRegs::IsLightingSamplerSupported(lighting.config, 723 LightingRegs::IsLightingSamplerSupported(lighting.config,
689 LightingRegs::LightingSampler::ReflectBlue)) { 724 LightingRegs::LightingSampler::ReflectBlue)) {
690 std::string index = 725 std::string value =
691 GetLutIndex(light_config.num, lighting.lut_rb.type, lighting.lut_rb.abs_input); 726 GetLutValue(LightingRegs::LightingSampler::ReflectBlue, light_config.num,
692 std::string value = "(" + std::to_string(lighting.lut_rb.scale) + " * " + 727 lighting.lut_rb.type, lighting.lut_rb.abs_input);
693 GetLutValue(LightingRegs::LightingSampler::ReflectBlue, index) + 728 value = "(" + std::to_string(lighting.lut_rb.scale) + " * " + value + ")";
694 ")";
695 out += "refl_value.b = " + value + ";\n"; 729 out += "refl_value.b = " + value + ";\n";
696 } else { 730 } else {
697 out += "refl_value.b = refl_value.r;\n"; 731 out += "refl_value.b = refl_value.r;\n";
@@ -703,23 +737,26 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
703 LightingRegs::IsLightingSamplerSupported( 737 LightingRegs::IsLightingSamplerSupported(
704 lighting.config, LightingRegs::LightingSampler::Distribution1)) { 738 lighting.config, LightingRegs::LightingSampler::Distribution1)) {
705 // Lookup specular "distribution 1" LUT value 739 // Lookup specular "distribution 1" LUT value
706 std::string index = 740 std::string value =
707 GetLutIndex(light_config.num, lighting.lut_d1.type, lighting.lut_d1.abs_input); 741 GetLutValue(LightingRegs::LightingSampler::Distribution1, light_config.num,
708 d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " + 742 lighting.lut_d1.type, lighting.lut_d1.abs_input);
709 GetLutValue(LightingRegs::LightingSampler::Distribution1, index) + ")"; 743 d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " + value + ")";
710 } 744 }
711 std::string specular_1 = 745 std::string specular_1 =
712 "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; 746 "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)";
747 if (light_config.geometric_factor_1) {
748 specular_1 = "(" + specular_1 + " * geo_factor)";
749 }
713 750
714 // Fresnel 751 // Fresnel
715 if (lighting.lut_fr.enable && 752 if (lighting.lut_fr.enable &&
716 LightingRegs::IsLightingSamplerSupported(lighting.config, 753 LightingRegs::IsLightingSamplerSupported(lighting.config,
717 LightingRegs::LightingSampler::Fresnel)) { 754 LightingRegs::LightingSampler::Fresnel)) {
718 // Lookup fresnel LUT value 755 // Lookup fresnel LUT value
719 std::string index = 756 std::string value =
720 GetLutIndex(light_config.num, lighting.lut_fr.type, lighting.lut_fr.abs_input); 757 GetLutValue(LightingRegs::LightingSampler::Fresnel, light_config.num,
721 std::string value = "(" + std::to_string(lighting.lut_fr.scale) + " * " + 758 lighting.lut_fr.type, lighting.lut_fr.abs_input);
722 GetLutValue(LightingRegs::LightingSampler::Fresnel, index) + ")"; 759 value = "(" + std::to_string(lighting.lut_fr.scale) + " * " + value + ")";
723 760
724 // Enabled for difffuse lighting alpha component 761 // Enabled for difffuse lighting alpha component
725 if (lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::PrimaryAlpha || 762 if (lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::PrimaryAlpha ||
@@ -973,10 +1010,6 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) {
973#define NUM_TEV_STAGES 6 1010#define NUM_TEV_STAGES 6
974#define NUM_LIGHTS 8 1011#define NUM_LIGHTS 8
975 1012
976// Texture coordinate offsets and scales
977#define OFFSET_256 (0.5 / 256.0)
978#define SCALE_256 (255.0 / 256.0)
979
980in vec4 primary_color; 1013in vec4 primary_color;
981in vec2 texcoord[3]; 1014in vec2 texcoord[3];
982in float texcoord0_w; 1015in float texcoord0_w;
@@ -1018,8 +1051,8 @@ layout (std140) uniform shader_data {
1018}; 1051};
1019 1052
1020uniform sampler2D tex[3]; 1053uniform sampler2D tex[3];
1021uniform sampler1D lut[6]; 1054uniform samplerBuffer lighting_lut;
1022uniform usampler1D fog_lut; 1055uniform samplerBuffer fog_lut;
1023uniform sampler1D proctex_noise_lut; 1056uniform sampler1D proctex_noise_lut;
1024uniform sampler1D proctex_color_map; 1057uniform sampler1D proctex_color_map;
1025uniform sampler1D proctex_alpha_map; 1058uniform sampler1D proctex_alpha_map;
@@ -1031,6 +1064,24 @@ vec3 quaternion_rotate(vec4 q, vec3 v) {
1031 return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); 1064 return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
1032} 1065}
1033 1066
1067float LookupLightingLUT(int lut_index, int index, float delta) {
1068 vec2 entry = texelFetch(lighting_lut, lut_index * 256 + index).rg;
1069 return entry.r + entry.g * delta;
1070}
1071
1072float LookupLightingLUTUnsigned(int lut_index, float pos) {
1073 int index = clamp(int(pos * 256.0), 0, 255);
1074 float delta = pos * 256.0 - index;
1075 return LookupLightingLUT(lut_index, index, delta);
1076}
1077
1078float LookupLightingLUTSigned(int lut_index, float pos) {
1079 int index = clamp(int(pos * 128.0), -128, 127);
1080 float delta = pos * 128.0 - index;
1081 if (index < 0) index += 256;
1082 return LookupLightingLUT(lut_index, index, delta);
1083}
1084
1034)"; 1085)";
1035 1086
1036 if (config.state.proctex.enable) 1087 if (config.state.proctex.enable)
@@ -1094,12 +1145,8 @@ vec4 secondary_fragment_color = vec4(0.0);
1094 // Generate clamped fog factor from LUT for given fog index 1145 // Generate clamped fog factor from LUT for given fog index
1095 out += "float fog_i = clamp(floor(fog_index), 0.0, 127.0);\n"; 1146 out += "float fog_i = clamp(floor(fog_index), 0.0, 127.0);\n";
1096 out += "float fog_f = fog_index - fog_i;\n"; 1147 out += "float fog_f = fog_index - fog_i;\n";
1097 out += "uint fog_lut_entry = texelFetch(fog_lut, int(fog_i), 0).r;\n"; 1148 out += "vec2 fog_lut_entry = texelFetch(fog_lut, int(fog_i)).rg;\n";
1098 out += "float fog_lut_entry_difference = float(int((fog_lut_entry & 0x1FFFU) << 19U) >> " 1149 out += "float fog_factor = fog_lut_entry.r + fog_lut_entry.g * fog_f;\n";
1099 "19);\n"; // Extract signed difference
1100 out += "float fog_lut_entry_value = float((fog_lut_entry >> 13U) & 0x7FFU);\n";
1101 out += "float fog_factor = (fog_lut_entry_value + fog_lut_entry_difference * fog_f) / "
1102 "2047.0;\n";
1103 out += "fog_factor = clamp(fog_factor, 0.0, 1.0);\n"; 1150 out += "fog_factor = clamp(fog_factor, 0.0, 1.0);\n";
1104 1151
1105 // Blend the fog 1152 // Blend the fog
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index 9c90eadf9..2302ae453 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -94,6 +94,8 @@ union PicaShaderConfig {
94 bool two_sided_diffuse; 94 bool two_sided_diffuse;
95 bool dist_atten_enable; 95 bool dist_atten_enable;
96 bool spot_atten_enable; 96 bool spot_atten_enable;
97 bool geometric_factor_0;
98 bool geometric_factor_1;
97 } light[8]; 99 } light[8];
98 100
99 bool enable; 101 bool enable;
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index bf837a7fb..eface2dea 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -52,11 +52,9 @@ OpenGLState::OpenGLState() {
52 texture_unit.sampler = 0; 52 texture_unit.sampler = 0;
53 } 53 }
54 54
55 for (auto& lut : lighting_luts) { 55 lighting_lut.texture_buffer = 0;
56 lut.texture_1d = 0;
57 }
58 56
59 fog_lut.texture_1d = 0; 57 fog_lut.texture_buffer = 0;
60 58
61 proctex_lut.texture_1d = 0; 59 proctex_lut.texture_1d = 0;
62 proctex_diff_lut.texture_1d = 0; 60 proctex_diff_lut.texture_1d = 0;
@@ -185,7 +183,7 @@ void OpenGLState::Apply() const {
185 // Textures 183 // Textures
186 for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) { 184 for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
187 if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) { 185 if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) {
188 glActiveTexture(GL_TEXTURE0 + i); 186 glActiveTexture(TextureUnits::PicaTexture(i).Enum());
189 glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d); 187 glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);
190 } 188 }
191 if (texture_units[i].sampler != cur_state.texture_units[i].sampler) { 189 if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {
@@ -194,46 +192,44 @@ void OpenGLState::Apply() const {
194 } 192 }
195 193
196 // Lighting LUTs 194 // Lighting LUTs
197 for (unsigned i = 0; i < ARRAY_SIZE(lighting_luts); ++i) { 195 if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) {
198 if (lighting_luts[i].texture_1d != cur_state.lighting_luts[i].texture_1d) { 196 glActiveTexture(TextureUnits::LightingLUT.Enum());
199 glActiveTexture(GL_TEXTURE3 + i); 197 glBindTexture(GL_TEXTURE_BUFFER, cur_state.lighting_lut.texture_buffer);
200 glBindTexture(GL_TEXTURE_1D, lighting_luts[i].texture_1d);
201 }
202 } 198 }
203 199
204 // Fog LUT 200 // Fog LUT
205 if (fog_lut.texture_1d != cur_state.fog_lut.texture_1d) { 201 if (fog_lut.texture_buffer != cur_state.fog_lut.texture_buffer) {
206 glActiveTexture(GL_TEXTURE9); 202 glActiveTexture(TextureUnits::FogLUT.Enum());
207 glBindTexture(GL_TEXTURE_1D, fog_lut.texture_1d); 203 glBindTexture(GL_TEXTURE_BUFFER, fog_lut.texture_buffer);
208 } 204 }
209 205
210 // ProcTex Noise LUT 206 // ProcTex Noise LUT
211 if (proctex_noise_lut.texture_1d != cur_state.proctex_noise_lut.texture_1d) { 207 if (proctex_noise_lut.texture_1d != cur_state.proctex_noise_lut.texture_1d) {
212 glActiveTexture(GL_TEXTURE10); 208 glActiveTexture(TextureUnits::ProcTexNoiseLUT.Enum());
213 glBindTexture(GL_TEXTURE_1D, proctex_noise_lut.texture_1d); 209 glBindTexture(GL_TEXTURE_1D, proctex_noise_lut.texture_1d);
214 } 210 }
215 211
216 // ProcTex Color Map 212 // ProcTex Color Map
217 if (proctex_color_map.texture_1d != cur_state.proctex_color_map.texture_1d) { 213 if (proctex_color_map.texture_1d != cur_state.proctex_color_map.texture_1d) {
218 glActiveTexture(GL_TEXTURE11); 214 glActiveTexture(TextureUnits::ProcTexColorMap.Enum());
219 glBindTexture(GL_TEXTURE_1D, proctex_color_map.texture_1d); 215 glBindTexture(GL_TEXTURE_1D, proctex_color_map.texture_1d);
220 } 216 }
221 217
222 // ProcTex Alpha Map 218 // ProcTex Alpha Map
223 if (proctex_alpha_map.texture_1d != cur_state.proctex_alpha_map.texture_1d) { 219 if (proctex_alpha_map.texture_1d != cur_state.proctex_alpha_map.texture_1d) {
224 glActiveTexture(GL_TEXTURE12); 220 glActiveTexture(TextureUnits::ProcTexAlphaMap.Enum());
225 glBindTexture(GL_TEXTURE_1D, proctex_alpha_map.texture_1d); 221 glBindTexture(GL_TEXTURE_1D, proctex_alpha_map.texture_1d);
226 } 222 }
227 223
228 // ProcTex LUT 224 // ProcTex LUT
229 if (proctex_lut.texture_1d != cur_state.proctex_lut.texture_1d) { 225 if (proctex_lut.texture_1d != cur_state.proctex_lut.texture_1d) {
230 glActiveTexture(GL_TEXTURE13); 226 glActiveTexture(TextureUnits::ProcTexLUT.Enum());
231 glBindTexture(GL_TEXTURE_1D, proctex_lut.texture_1d); 227 glBindTexture(GL_TEXTURE_1D, proctex_lut.texture_1d);
232 } 228 }
233 229
234 // ProcTex Diff LUT 230 // ProcTex Diff LUT
235 if (proctex_diff_lut.texture_1d != cur_state.proctex_diff_lut.texture_1d) { 231 if (proctex_diff_lut.texture_1d != cur_state.proctex_diff_lut.texture_1d) {
236 glActiveTexture(GL_TEXTURE14); 232 glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum());
237 glBindTexture(GL_TEXTURE_1D, proctex_diff_lut.texture_1d); 233 glBindTexture(GL_TEXTURE_1D, proctex_diff_lut.texture_1d);
238 } 234 }
239 235
@@ -274,6 +270,20 @@ void OpenGLState::ResetTexture(GLuint handle) {
274 unit.texture_2d = 0; 270 unit.texture_2d = 0;
275 } 271 }
276 } 272 }
273 if (cur_state.lighting_lut.texture_buffer == handle)
274 cur_state.lighting_lut.texture_buffer = 0;
275 if (cur_state.fog_lut.texture_buffer == handle)
276 cur_state.fog_lut.texture_buffer = 0;
277 if (cur_state.proctex_noise_lut.texture_1d == handle)
278 cur_state.proctex_noise_lut.texture_1d = 0;
279 if (cur_state.proctex_color_map.texture_1d == handle)
280 cur_state.proctex_color_map.texture_1d = 0;
281 if (cur_state.proctex_alpha_map.texture_1d == handle)
282 cur_state.proctex_alpha_map.texture_1d = 0;
283 if (cur_state.proctex_lut.texture_1d == handle)
284 cur_state.proctex_lut.texture_1d = 0;
285 if (cur_state.proctex_diff_lut.texture_1d == handle)
286 cur_state.proctex_diff_lut.texture_1d = 0;
277} 287}
278 288
279void OpenGLState::ResetSampler(GLuint handle) { 289void OpenGLState::ResetSampler(GLuint handle) {
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 7dcc03bd5..1efcf0811 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -6,6 +6,29 @@
6 6
7#include <glad/glad.h> 7#include <glad/glad.h>
8 8
9namespace TextureUnits {
10
11struct TextureUnit {
12 GLint id;
13 constexpr GLenum Enum() const {
14 return static_cast<GLenum>(GL_TEXTURE0 + id);
15 }
16};
17
18constexpr TextureUnit PicaTexture(int unit) {
19 return TextureUnit{unit};
20}
21
22constexpr TextureUnit LightingLUT{3};
23constexpr TextureUnit FogLUT{4};
24constexpr TextureUnit ProcTexNoiseLUT{5};
25constexpr TextureUnit ProcTexColorMap{6};
26constexpr TextureUnit ProcTexAlphaMap{7};
27constexpr TextureUnit ProcTexLUT{8};
28constexpr TextureUnit ProcTexDiffLUT{9};
29
30} // namespace TextureUnits
31
9class OpenGLState { 32class OpenGLState {
10public: 33public:
11 struct { 34 struct {
@@ -64,11 +87,11 @@ public:
64 } texture_units[3]; 87 } texture_units[3];
65 88
66 struct { 89 struct {
67 GLuint texture_1d; // GL_TEXTURE_BINDING_1D 90 GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
68 } lighting_luts[6]; 91 } lighting_lut;
69 92
70 struct { 93 struct {
71 GLuint texture_1d; // GL_TEXTURE_BINDING_1D 94 GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER
72 } fog_lut; 95 } fog_lut;
73 96
74 struct { 97 struct {
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h
index 93d7b0b71..70298e211 100644
--- a/src/video_core/renderer_opengl/pica_to_gl.h
+++ b/src/video_core/renderer_opengl/pica_to_gl.h
@@ -55,6 +55,12 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) {
55 GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder 55 GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder
56 GL_REPEAT, // WrapMode::Repeat 56 GL_REPEAT, // WrapMode::Repeat
57 GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat 57 GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat
58 // TODO(wwylele): ClampToEdge2 and ClampToBorder2 are not properly implemented here. See the
59 // comments in enum WrapMode.
60 GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge2
61 GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder2
62 GL_REPEAT, // WrapMode::Repeat2
63 GL_REPEAT, // WrapMode::Repeat3
58 }; 64 };
59 65
60 // Range check table for input 66 // Range check table for input
@@ -65,6 +71,13 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) {
65 return GL_CLAMP_TO_EDGE; 71 return GL_CLAMP_TO_EDGE;
66 } 72 }
67 73
74 if (static_cast<u32>(mode) > 3) {
75 // It is still unclear whether mode 4-7 are valid, so log it if a game uses them.
76 // TODO(wwylele): telemetry should be added here so we can collect more info about which
77 // game uses this.
78 LOG_WARNING(Render_OpenGL, "Using texture wrap mode %u", static_cast<u32>(mode));
79 }
80
68 GLenum gl_mode = wrap_mode_table[mode]; 81 GLenum gl_mode = wrap_mode_table[mode];
69 82
70 // Check for dummy values indicating an unknown mode 83 // Check for dummy values indicating an unknown mode
diff --git a/src/video_core/shader/shader_jit_x64_compiler.cpp b/src/video_core/shader/shader_jit_x64_compiler.cpp
index 5d9b6448c..42a57aab1 100644
--- a/src/video_core/shader/shader_jit_x64_compiler.cpp
+++ b/src/video_core/shader/shader_jit_x64_compiler.cpp
@@ -321,27 +321,27 @@ void JitShader::Compile_EvaluateCondition(Instruction instr) {
321 case Instruction::FlowControlType::Or: 321 case Instruction::FlowControlType::Or:
322 mov(eax, COND0); 322 mov(eax, COND0);
323 mov(ebx, COND1); 323 mov(ebx, COND1);
324 xor(eax, (instr.flow_control.refx.Value() ^ 1)); 324 xor_(eax, (instr.flow_control.refx.Value() ^ 1));
325 xor(ebx, (instr.flow_control.refy.Value() ^ 1)); 325 xor_(ebx, (instr.flow_control.refy.Value() ^ 1));
326 or (eax, ebx); 326 or_(eax, ebx);
327 break; 327 break;
328 328
329 case Instruction::FlowControlType::And: 329 case Instruction::FlowControlType::And:
330 mov(eax, COND0); 330 mov(eax, COND0);
331 mov(ebx, COND1); 331 mov(ebx, COND1);
332 xor(eax, (instr.flow_control.refx.Value() ^ 1)); 332 xor_(eax, (instr.flow_control.refx.Value() ^ 1));
333 xor(ebx, (instr.flow_control.refy.Value() ^ 1)); 333 xor_(ebx, (instr.flow_control.refy.Value() ^ 1));
334 and(eax, ebx); 334 and_(eax, ebx);
335 break; 335 break;
336 336
337 case Instruction::FlowControlType::JustX: 337 case Instruction::FlowControlType::JustX:
338 mov(eax, COND0); 338 mov(eax, COND0);
339 xor(eax, (instr.flow_control.refx.Value() ^ 1)); 339 xor_(eax, (instr.flow_control.refx.Value() ^ 1));
340 break; 340 break;
341 341
342 case Instruction::FlowControlType::JustY: 342 case Instruction::FlowControlType::JustY:
343 mov(eax, COND1); 343 mov(eax, COND1);
344 xor(eax, (instr.flow_control.refy.Value() ^ 1)); 344 xor_(eax, (instr.flow_control.refy.Value() ^ 1));
345 break; 345 break;
346 } 346 }
347} 347}
@@ -734,10 +734,10 @@ void JitShader::Compile_LOOP(Instruction instr) {
734 mov(LOOPCOUNT, dword[SETUP + offset]); 734 mov(LOOPCOUNT, dword[SETUP + offset]);
735 mov(LOOPCOUNT_REG, LOOPCOUNT); 735 mov(LOOPCOUNT_REG, LOOPCOUNT);
736 shr(LOOPCOUNT_REG, 4); 736 shr(LOOPCOUNT_REG, 4);
737 and(LOOPCOUNT_REG, 0xFF0); // Y-component is the start 737 and_(LOOPCOUNT_REG, 0xFF0); // Y-component is the start
738 mov(LOOPINC, LOOPCOUNT); 738 mov(LOOPINC, LOOPCOUNT);
739 shr(LOOPINC, 12); 739 shr(LOOPINC, 12);
740 and(LOOPINC, 0xFF0); // Z-component is the incrementer 740 and_(LOOPINC, 0xFF0); // Z-component is the incrementer
741 movzx(LOOPCOUNT, LOOPCOUNT.cvt8()); // X-component is iteration count 741 movzx(LOOPCOUNT, LOOPCOUNT.cvt8()); // X-component is iteration count
742 add(LOOPCOUNT, 1); // Iteration count is X-component + 1 742 add(LOOPCOUNT, 1); // Iteration count is X-component + 1
743 743
@@ -858,9 +858,9 @@ void JitShader::Compile(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>* program_
858 mov(STATE, ABI_PARAM2); 858 mov(STATE, ABI_PARAM2);
859 859
860 // Zero address/loop registers 860 // Zero address/loop registers
861 xor(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); 861 xor_(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32());
862 xor(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); 862 xor_(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32());
863 xor(LOOPCOUNT_REG, LOOPCOUNT_REG); 863 xor_(LOOPCOUNT_REG, LOOPCOUNT_REG);
864 864
865 // Used to set a register to one 865 // Used to set a register to one
866 static const __m128 one = {1.f, 1.f, 1.f, 1.f}; 866 static const __m128 one = {1.f, 1.f, 1.f, 1.f};
diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp
index 8b7b1defb..512e81c08 100644
--- a/src/video_core/swrasterizer/rasterizer.cpp
+++ b/src/video_core/swrasterizer/rasterizer.cpp
@@ -357,10 +357,22 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
357 int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height))) 357 int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height)))
358 .ToFloat32(); 358 .ToFloat32();
359 359
360 if ((texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder && 360 bool use_border_s = false;
361 (s < 0 || static_cast<u32>(s) >= texture.config.width)) || 361 bool use_border_t = false;
362 (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder && 362
363 (t < 0 || static_cast<u32>(t) >= texture.config.height))) { 363 if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder) {
364 use_border_s = s < 0 || s >= static_cast<int>(texture.config.width);
365 } else if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder2) {
366 use_border_s = s >= static_cast<int>(texture.config.width);
367 }
368
369 if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder) {
370 use_border_t = t < 0 || t >= static_cast<int>(texture.config.height);
371 } else if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder2) {
372 use_border_t = t >= static_cast<int>(texture.config.height);
373 }
374
375 if (use_border_s || use_border_t) {
364 auto border_color = texture.config.border_color; 376 auto border_color = texture.config.border_color;
365 texture_color[i] = {border_color.r, border_color.g, border_color.b, 377 texture_color[i] = {border_color.r, border_color.g, border_color.b,
366 border_color.a}; 378 border_color.a};
@@ -572,8 +584,7 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
572 float fog_i = MathUtil::Clamp(floorf(fog_index), 0.0f, 127.0f); 584 float fog_i = MathUtil::Clamp(floorf(fog_index), 0.0f, 127.0f);
573 float fog_f = fog_index - fog_i; 585 float fog_f = fog_index - fog_i;
574 const auto& fog_lut_entry = g_state.fog.lut[static_cast<unsigned int>(fog_i)]; 586 const auto& fog_lut_entry = g_state.fog.lut[static_cast<unsigned int>(fog_i)];
575 float fog_factor = (fog_lut_entry.value + fog_lut_entry.difference * fog_f) / 587 float fog_factor = fog_lut_entry.ToFloat() + fog_lut_entry.DiffToFloat() * fog_f;
576 2047.0f; // This is signed fixed point 1.11
577 fog_factor = MathUtil::Clamp(fog_factor, 0.0f, 1.0f); 588 fog_factor = MathUtil::Clamp(fog_factor, 0.0f, 1.0f);
578 589
579 // Blend the fog 590 // Blend the fog
diff --git a/src/video_core/swrasterizer/texturing.cpp b/src/video_core/swrasterizer/texturing.cpp
index aeb6aeb8c..4f02b93f2 100644
--- a/src/video_core/swrasterizer/texturing.cpp
+++ b/src/video_core/swrasterizer/texturing.cpp
@@ -18,22 +18,33 @@ using TevStageConfig = TexturingRegs::TevStageConfig;
18 18
19int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) { 19int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) {
20 switch (mode) { 20 switch (mode) {
21 case TexturingRegs::TextureConfig::ClampToEdge2:
22 // For negative coordinate, ClampToEdge2 behaves the same as Repeat
23 if (val < 0) {
24 return static_cast<int>(static_cast<unsigned>(val) % size);
25 }
26 // [[fallthrough]]
21 case TexturingRegs::TextureConfig::ClampToEdge: 27 case TexturingRegs::TextureConfig::ClampToEdge:
22 val = std::max(val, 0); 28 val = std::max(val, 0);
23 val = std::min(val, (int)size - 1); 29 val = std::min(val, static_cast<int>(size) - 1);
24 return val; 30 return val;
25 31
26 case TexturingRegs::TextureConfig::ClampToBorder: 32 case TexturingRegs::TextureConfig::ClampToBorder:
27 return val; 33 return val;
28 34
35 case TexturingRegs::TextureConfig::ClampToBorder2:
36 // For ClampToBorder2, the case of positive coordinate beyond the texture size is already
37 // handled outside. Here we only handle the negative coordinate in the same way as Repeat.
38 case TexturingRegs::TextureConfig::Repeat2:
39 case TexturingRegs::TextureConfig::Repeat3:
29 case TexturingRegs::TextureConfig::Repeat: 40 case TexturingRegs::TextureConfig::Repeat:
30 return (int)((unsigned)val % size); 41 return static_cast<int>(static_cast<unsigned>(val) % size);
31 42
32 case TexturingRegs::TextureConfig::MirroredRepeat: { 43 case TexturingRegs::TextureConfig::MirroredRepeat: {
33 unsigned int coord = ((unsigned)val % (2 * size)); 44 unsigned int coord = (static_cast<unsigned>(val) % (2 * size));
34 if (coord >= size) 45 if (coord >= size)
35 coord = 2 * size - 1 - coord; 46 coord = 2 * size - 1 - coord;
36 return (int)coord; 47 return static_cast<int>(coord);
37 } 48 }
38 49
39 default: 50 default: