diff options
| -rw-r--r-- | src/citra/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/citra_qt/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/common/logging/backend.cpp | 1 | ||||
| -rw-r--r-- | src/common/logging/log.h | 1 | ||||
| -rw-r--r-- | src/core/hle/function_wrappers.h | 7 | ||||
| -rw-r--r-- | src/core/hle/service/soc_u.cpp | 721 | ||||
| -rw-r--r-- | src/core/hle/service/soc_u.h | 1 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 14 |
8 files changed, 726 insertions, 25 deletions
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index 94f8d13c7..bbb3374f2 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt | |||
| @@ -24,10 +24,10 @@ endif() | |||
| 24 | if (APPLE) | 24 | if (APPLE) |
| 25 | target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY}) | 25 | target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY}) |
| 26 | elseif (WIN32) | 26 | elseif (WIN32) |
| 27 | target_link_libraries(citra winmm) | 27 | target_link_libraries(citra winmm wsock32 ws2_32) |
| 28 | if (MINGW) # GCC does not support codecvt, so use iconv instead | 28 | if (MINGW) # GCC does not support codecvt, so use iconv instead |
| 29 | target_link_libraries(citra iconv) | 29 | target_link_libraries(citra iconv) |
| 30 | endif() | 30 | endif() |
| 31 | else() # Unix | 31 | else() # Unix |
| 32 | target_link_libraries(citra rt) | 32 | target_link_libraries(citra rt) |
| 33 | endif() | 33 | endif() |
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 356ec754f..8a511e02f 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -68,7 +68,7 @@ endif() | |||
| 68 | if (APPLE) | 68 | if (APPLE) |
| 69 | target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY}) | 69 | target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY}) |
| 70 | elseif (WIN32) | 70 | elseif (WIN32) |
| 71 | target_link_libraries(citra-qt winmm) | 71 | target_link_libraries(citra-qt winmm wsock32 ws2_32) |
| 72 | else() # Unix | 72 | else() # Unix |
| 73 | target_link_libraries(citra-qt rt) | 73 | target_link_libraries(citra-qt rt) |
| 74 | endif() | 74 | endif() |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 816d1bb55..7ac30ad50 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -39,6 +39,7 @@ static std::shared_ptr<Logger> global_logger; | |||
| 39 | SUB(Service, CFG) \ | 39 | SUB(Service, CFG) \ |
| 40 | SUB(Service, DSP) \ | 40 | SUB(Service, DSP) \ |
| 41 | SUB(Service, HID) \ | 41 | SUB(Service, HID) \ |
| 42 | SUB(Service, SOC) \ | ||
| 42 | CLS(HW) \ | 43 | CLS(HW) \ |
| 43 | SUB(HW, Memory) \ | 44 | SUB(HW, Memory) \ |
| 44 | SUB(HW, GPU) \ | 45 | SUB(HW, GPU) \ |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index d1c391862..06b99b07a 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -59,6 +59,7 @@ enum class Class : ClassType { | |||
| 59 | Service_CFG, ///< The CFG (Configuration) service | 59 | Service_CFG, ///< The CFG (Configuration) service |
| 60 | Service_DSP, ///< The DSP (DSP control) service | 60 | Service_DSP, ///< The DSP (DSP control) service |
| 61 | Service_HID, ///< The HID (User input) service | 61 | Service_HID, ///< The HID (User input) service |
| 62 | Service_SOC, ///< The SOC (Socket) service | ||
| 62 | HW, ///< Low-level hardware emulation | 63 | HW, ///< Low-level hardware emulation |
| 63 | HW_Memory, ///< Memory-map and address translation | 64 | HW_Memory, ///< Memory-map and address translation |
| 64 | HW_GPU, ///< GPU control emulation | 65 | HW_GPU, ///< GPU control emulation |
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 3259ce9eb..0f822f84b 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h | |||
| @@ -128,6 +128,13 @@ template<s32 func(s32*, u32, s32)> void Wrap() { | |||
| 128 | FuncReturn(retval); | 128 | FuncReturn(retval); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() { | ||
| 132 | u32 param_1 = 0; | ||
| 133 | u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)); | ||
| 134 | Core::g_app_core->SetReg(1, param_1); | ||
| 135 | FuncReturn(retval); | ||
| 136 | } | ||
| 137 | |||
| 131 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 138 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 132 | // Function wrappers that return type u32 | 139 | // Function wrappers that return type u32 |
| 133 | 140 | ||
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index ef4f9829d..9fbf18b26 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp | |||
| @@ -2,40 +2,712 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/platform.h" | ||
| 6 | |||
| 7 | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 8 | #include <winsock2.h> | ||
| 9 | #include <ws2tcpip.h> | ||
| 10 | #else | ||
| 11 | #include <sys/socket.h> | ||
| 12 | #include <netinet/in.h> | ||
| 13 | #include <netdb.h> | ||
| 14 | #include <arpa/inet.h> | ||
| 15 | #include <fcntl.h> | ||
| 16 | #include <poll.h> | ||
| 17 | #endif | ||
| 18 | |||
| 5 | #include "common/log.h" | 19 | #include "common/log.h" |
| 20 | #include "common/scope_exit.h" | ||
| 6 | #include "core/hle/hle.h" | 21 | #include "core/hle/hle.h" |
| 7 | #include "core/hle/service/soc_u.h" | 22 | #include "core/hle/service/soc_u.h" |
| 23 | #include <unordered_map> | ||
| 24 | |||
| 25 | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 26 | # define WSAEAGAIN WSAEWOULDBLOCK | ||
| 27 | # define WSAEMULTIHOP -1 // Invalid dummy value | ||
| 28 | # define ERRNO(x) WSA##x | ||
| 29 | # define GET_ERRNO WSAGetLastError() | ||
| 30 | # define poll(x, y, z) WSAPoll(x, y, z); | ||
| 31 | #else | ||
| 32 | # define ERRNO(x) x | ||
| 33 | # define GET_ERRNO errno | ||
| 34 | # define closesocket(x) close(x) | ||
| 35 | #endif | ||
| 36 | |||
| 37 | static const s32 SOCKET_ERROR_VALUE = -1; | ||
| 8 | 38 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 39 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 10 | // Namespace SOC_U | 40 | // Namespace SOC_U |
| 11 | 41 | ||
| 12 | namespace SOC_U { | 42 | namespace SOC_U { |
| 13 | 43 | ||
| 44 | /// Holds the translation from system network errors to 3DS network errors | ||
| 45 | static const std::unordered_map<int, int> error_map = { { | ||
| 46 | { E2BIG, 1 }, | ||
| 47 | { ERRNO(EACCES), 2 }, | ||
| 48 | { ERRNO(EADDRINUSE), 3 }, | ||
| 49 | { ERRNO(EADDRNOTAVAIL), 4 }, | ||
| 50 | { ERRNO(EAFNOSUPPORT), 5 }, | ||
| 51 | { ERRNO(EAGAIN), 6 }, | ||
| 52 | { ERRNO(EALREADY), 7 }, | ||
| 53 | { ERRNO(EBADF), 8 }, | ||
| 54 | { EBADMSG, 9 }, | ||
| 55 | { EBUSY, 10 }, | ||
| 56 | { ECANCELED, 11 }, | ||
| 57 | { ECHILD, 12 }, | ||
| 58 | { ERRNO(ECONNABORTED), 13 }, | ||
| 59 | { ERRNO(ECONNREFUSED), 14 }, | ||
| 60 | { ERRNO(ECONNRESET), 15 }, | ||
| 61 | { EDEADLK, 16 }, | ||
| 62 | { ERRNO(EDESTADDRREQ), 17 }, | ||
| 63 | { EDOM, 18 }, | ||
| 64 | { ERRNO(EDQUOT), 19 }, | ||
| 65 | { EEXIST, 20 }, | ||
| 66 | { ERRNO(EFAULT), 21 }, | ||
| 67 | { EFBIG, 22 }, | ||
| 68 | { ERRNO(EHOSTUNREACH), 23 }, | ||
| 69 | { EIDRM, 24 }, | ||
| 70 | { EILSEQ, 25 }, | ||
| 71 | { ERRNO(EINPROGRESS), 26 }, | ||
| 72 | { ERRNO(EINTR), 27 }, | ||
| 73 | { ERRNO(EINVAL), 28 }, | ||
| 74 | { EIO, 29 }, | ||
| 75 | { ERRNO(EISCONN), 30 }, | ||
| 76 | { EISDIR, 31 }, | ||
| 77 | { ERRNO(ELOOP), 32 }, | ||
| 78 | { ERRNO(EMFILE), 33 }, | ||
| 79 | { EMLINK, 34 }, | ||
| 80 | { ERRNO(EMSGSIZE), 35 }, | ||
| 81 | { ERRNO(EMULTIHOP), 36 }, | ||
| 82 | { ERRNO(ENAMETOOLONG), 37 }, | ||
| 83 | { ERRNO(ENETDOWN), 38 }, | ||
| 84 | { ERRNO(ENETRESET), 39 }, | ||
| 85 | { ERRNO(ENETUNREACH), 40 }, | ||
| 86 | { ENFILE, 41 }, | ||
| 87 | { ERRNO(ENOBUFS), 42 }, | ||
| 88 | { ENODATA, 43 }, | ||
| 89 | { ENODEV, 44 }, | ||
| 90 | { ENOENT, 45 }, | ||
| 91 | { ENOEXEC, 46 }, | ||
| 92 | { ENOLCK, 47 }, | ||
| 93 | { ENOLINK, 48 }, | ||
| 94 | { ENOMEM, 49 }, | ||
| 95 | { ENOMSG, 50 }, | ||
| 96 | { ERRNO(ENOPROTOOPT), 51 }, | ||
| 97 | { ENOSPC, 52 }, | ||
| 98 | { ENOSR, 53 }, | ||
| 99 | { ENOSTR, 54 }, | ||
| 100 | { ENOSYS, 55 }, | ||
| 101 | { ERRNO(ENOTCONN), 56 }, | ||
| 102 | { ENOTDIR, 57 }, | ||
| 103 | { ERRNO(ENOTEMPTY), 58 }, | ||
| 104 | { ERRNO(ENOTSOCK), 59 }, | ||
| 105 | { ENOTSUP, 60 }, | ||
| 106 | { ENOTTY, 61 }, | ||
| 107 | { ENXIO, 62 }, | ||
| 108 | { ERRNO(EOPNOTSUPP), 63 }, | ||
| 109 | { EOVERFLOW, 64 }, | ||
| 110 | { EPERM, 65 }, | ||
| 111 | { EPIPE, 66 }, | ||
| 112 | { EPROTO, 67 }, | ||
| 113 | { ERRNO(EPROTONOSUPPORT), 68 }, | ||
| 114 | { ERRNO(EPROTOTYPE), 69 }, | ||
| 115 | { ERANGE, 70 }, | ||
| 116 | { EROFS, 71 }, | ||
| 117 | { ESPIPE, 72 }, | ||
| 118 | { ESRCH, 73 }, | ||
| 119 | { ERRNO(ESTALE), 74 }, | ||
| 120 | { ETIME, 75 }, | ||
| 121 | { ERRNO(ETIMEDOUT), 76 } | ||
| 122 | }}; | ||
| 123 | |||
| 124 | /// Converts a network error from platform-specific to 3ds-specific | ||
| 125 | static int TranslateError(int error) { | ||
| 126 | auto found = error_map.find(error); | ||
| 127 | if (found != error_map.end()) | ||
| 128 | return -found->second; | ||
| 129 | |||
| 130 | return error; | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Holds information about a particular socket | ||
| 134 | struct SocketHolder { | ||
| 135 | u32 socket_fd; ///< The socket descriptor | ||
| 136 | bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows. | ||
| 137 | }; | ||
| 138 | |||
| 139 | /// Structure to represent the 3ds' pollfd structure, which is different than most implementations | ||
| 140 | struct CTRPollFD { | ||
| 141 | u32 fd; ///< Socket handle | ||
| 142 | |||
| 143 | union Events { | ||
| 144 | u32 hex; ///< The complete value formed by the flags | ||
| 145 | BitField<0, 1, u32> pollin; | ||
| 146 | BitField<1, 1, u32> pollpri; | ||
| 147 | BitField<2, 1, u32> pollhup; | ||
| 148 | BitField<3, 1, u32> pollerr; | ||
| 149 | BitField<4, 1, u32> pollout; | ||
| 150 | BitField<5, 1, u32> pollnval; | ||
| 151 | |||
| 152 | Events& operator=(const Events& other) { | ||
| 153 | hex = other.hex; | ||
| 154 | return *this; | ||
| 155 | } | ||
| 156 | |||
| 157 | /// Translates the resulting events of a Poll operation from platform-specific to 3ds specific | ||
| 158 | static Events TranslateTo3DS(u32 input_event) { | ||
| 159 | Events ev = {}; | ||
| 160 | if (input_event & POLLIN) | ||
| 161 | ev.pollin = 1; | ||
| 162 | if (input_event & POLLPRI) | ||
| 163 | ev.pollpri = 1; | ||
| 164 | if (input_event & POLLHUP) | ||
| 165 | ev.pollhup = 1; | ||
| 166 | if (input_event & POLLERR) | ||
| 167 | ev.pollerr = 1; | ||
| 168 | if (input_event & POLLOUT) | ||
| 169 | ev.pollout = 1; | ||
| 170 | if (input_event & POLLNVAL) | ||
| 171 | ev.pollnval = 1; | ||
| 172 | return ev; | ||
| 173 | } | ||
| 174 | |||
| 175 | /// Translates the resulting events of a Poll operation from 3ds specific to platform specific | ||
| 176 | static u32 TranslateToPlatform(Events input_event) { | ||
| 177 | u32 ret = 0; | ||
| 178 | if (input_event.pollin) | ||
| 179 | ret |= POLLIN; | ||
| 180 | if (input_event.pollpri) | ||
| 181 | ret |= POLLPRI; | ||
| 182 | if (input_event.pollhup) | ||
| 183 | ret |= POLLHUP; | ||
| 184 | if (input_event.pollerr) | ||
| 185 | ret |= POLLERR; | ||
| 186 | if (input_event.pollout) | ||
| 187 | ret |= POLLOUT; | ||
| 188 | if (input_event.pollnval) | ||
| 189 | ret |= POLLNVAL; | ||
| 190 | return ret; | ||
| 191 | } | ||
| 192 | }; | ||
| 193 | Events events; ///< Events to poll for (input) | ||
| 194 | Events revents; ///< Events received (output) | ||
| 195 | |||
| 196 | /// Converts a platform-specific pollfd to a 3ds specific structure | ||
| 197 | static CTRPollFD FromPlatform(pollfd const& fd) { | ||
| 198 | CTRPollFD result; | ||
| 199 | result.events.hex = Events::TranslateTo3DS(fd.events).hex; | ||
| 200 | result.revents.hex = Events::TranslateTo3DS(fd.revents).hex; | ||
| 201 | result.fd = static_cast<u32>(fd.fd); | ||
| 202 | return result; | ||
| 203 | } | ||
| 204 | |||
| 205 | /// Converts a 3ds specific pollfd to a platform-specific structure | ||
| 206 | static pollfd ToPlatform(CTRPollFD const& fd) { | ||
| 207 | pollfd result; | ||
| 208 | result.events = Events::TranslateToPlatform(fd.events); | ||
| 209 | result.revents = Events::TranslateToPlatform(fd.revents); | ||
| 210 | result.fd = fd.fd; | ||
| 211 | return result; | ||
| 212 | } | ||
| 213 | }; | ||
| 214 | |||
| 215 | /// Union to represent the 3ds' sockaddr structure | ||
| 216 | union CTRSockAddr { | ||
| 217 | /// Structure to represent a raw sockaddr | ||
| 218 | struct { | ||
| 219 | u8 len; ///< The length of the entire structure, only the set fields count | ||
| 220 | u8 sa_family; ///< The address family of the sockaddr | ||
| 221 | u8 sa_data[0x1A]; ///< The extra data, this varies, depending on the address family | ||
| 222 | } raw; | ||
| 223 | |||
| 224 | /// Structure to represent the 3ds' sockaddr_in structure | ||
| 225 | struct CTRSockAddrIn { | ||
| 226 | u8 len; ///< The length of the entire structure | ||
| 227 | u8 sin_family; ///< The address family of the sockaddr_in | ||
| 228 | u16 sin_port; ///< The port associated with this sockaddr_in | ||
| 229 | u32 sin_addr; ///< The actual address of the sockaddr_in | ||
| 230 | } in; | ||
| 231 | |||
| 232 | /// Convert a 3DS CTRSockAddr to a platform-specific sockaddr | ||
| 233 | static sockaddr ToPlatform(CTRSockAddr const& ctr_addr) { | ||
| 234 | sockaddr result; | ||
| 235 | result.sa_family = ctr_addr.raw.sa_family; | ||
| 236 | memset(result.sa_data, 0, sizeof(result.sa_data)); | ||
| 237 | |||
| 238 | // We can not guarantee ABI compatibility between platforms so we copy the fields manually | ||
| 239 | switch (result.sa_family) { | ||
| 240 | case AF_INET: | ||
| 241 | { | ||
| 242 | sockaddr_in* result_in = reinterpret_cast<sockaddr_in*>(&result); | ||
| 243 | result_in->sin_port = ctr_addr.in.sin_port; | ||
| 244 | result_in->sin_addr.s_addr = ctr_addr.in.sin_addr; | ||
| 245 | memset(result_in->sin_zero, 0, sizeof(result_in->sin_zero)); | ||
| 246 | break; | ||
| 247 | } | ||
| 248 | default: | ||
| 249 | _dbg_assert_msg_(Service_SOC, false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform"); | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | return result; | ||
| 253 | } | ||
| 254 | |||
| 255 | /// Convert a platform-specific sockaddr to a 3DS CTRSockAddr | ||
| 256 | static CTRSockAddr FromPlatform(sockaddr const& addr) { | ||
| 257 | CTRSockAddr result; | ||
| 258 | result.raw.sa_family = static_cast<u8>(addr.sa_family); | ||
| 259 | // We can not guarantee ABI compatibility between platforms so we copy the fields manually | ||
| 260 | switch (result.raw.sa_family) { | ||
| 261 | case AF_INET: | ||
| 262 | { | ||
| 263 | sockaddr_in const* addr_in = reinterpret_cast<sockaddr_in const*>(&addr); | ||
| 264 | result.raw.len = sizeof(CTRSockAddrIn); | ||
| 265 | result.in.sin_port = addr_in->sin_port; | ||
| 266 | result.in.sin_addr = addr_in->sin_addr.s_addr; | ||
| 267 | break; | ||
| 268 | } | ||
| 269 | default: | ||
| 270 | _dbg_assert_msg_(Service_SOC, false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform"); | ||
| 271 | break; | ||
| 272 | } | ||
| 273 | return result; | ||
| 274 | } | ||
| 275 | }; | ||
| 276 | |||
| 277 | /// Holds info about the currently open sockets | ||
| 278 | static std::unordered_map<u32, SocketHolder> open_sockets; | ||
| 279 | |||
| 280 | /// Close all open sockets | ||
| 281 | static void CleanupSockets() { | ||
| 282 | for (auto sock : open_sockets) | ||
| 283 | closesocket(sock.second.socket_fd); | ||
| 284 | open_sockets.clear(); | ||
| 285 | } | ||
| 286 | |||
| 287 | static void Socket(Service::Interface* self) { | ||
| 288 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 289 | u32 domain = cmd_buffer[1]; // Address family | ||
| 290 | u32 type = cmd_buffer[2]; | ||
| 291 | u32 protocol = cmd_buffer[3]; | ||
| 292 | |||
| 293 | // Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use | ||
| 294 | if (protocol != 0) { | ||
| 295 | cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code | ||
| 296 | return; | ||
| 297 | } | ||
| 298 | |||
| 299 | if (domain != AF_INET) { | ||
| 300 | cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code | ||
| 301 | return; | ||
| 302 | } | ||
| 303 | |||
| 304 | if (type != SOCK_DGRAM && type != SOCK_STREAM) { | ||
| 305 | cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code | ||
| 306 | return; | ||
| 307 | } | ||
| 308 | |||
| 309 | u32 socket_handle = static_cast<u32>(::socket(domain, type, protocol)); | ||
| 310 | |||
| 311 | if (socket_handle != SOCKET_ERROR_VALUE) | ||
| 312 | open_sockets[socket_handle] = { socket_handle, true }; | ||
| 313 | |||
| 314 | int result = 0; | ||
| 315 | if (socket_handle == SOCKET_ERROR_VALUE) | ||
| 316 | result = TranslateError(GET_ERRNO); | ||
| 317 | |||
| 318 | cmd_buffer[1] = result; | ||
| 319 | cmd_buffer[2] = socket_handle; | ||
| 320 | } | ||
| 321 | |||
| 322 | static void Bind(Service::Interface* self) { | ||
| 323 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 324 | u32 socket_handle = cmd_buffer[1]; | ||
| 325 | u32 len = cmd_buffer[2]; | ||
| 326 | CTRSockAddr* ctr_sock_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6])); | ||
| 327 | |||
| 328 | if (ctr_sock_addr == nullptr) { | ||
| 329 | cmd_buffer[1] = -1; // TODO(Subv): Correct code | ||
| 330 | return; | ||
| 331 | } | ||
| 332 | |||
| 333 | sockaddr sock_addr = CTRSockAddr::ToPlatform(*ctr_sock_addr); | ||
| 334 | |||
| 335 | int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len)); | ||
| 336 | |||
| 337 | int result = 0; | ||
| 338 | if (res != 0) | ||
| 339 | result = TranslateError(GET_ERRNO); | ||
| 340 | |||
| 341 | cmd_buffer[2] = res; | ||
| 342 | cmd_buffer[1] = result; | ||
| 343 | } | ||
| 344 | |||
| 345 | static void Fcntl(Service::Interface* self) { | ||
| 346 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 347 | u32 socket_handle = cmd_buffer[1]; | ||
| 348 | u32 ctr_cmd = cmd_buffer[2]; | ||
| 349 | u32 ctr_arg = cmd_buffer[3]; | ||
| 350 | |||
| 351 | int result = 0; | ||
| 352 | u32 posix_ret = 0; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX) | ||
| 353 | SCOPE_EXIT({ | ||
| 354 | cmd_buffer[1] = result; | ||
| 355 | cmd_buffer[2] = posix_ret; | ||
| 356 | }); | ||
| 357 | |||
| 358 | if (ctr_cmd == 3) { // F_GETFL | ||
| 359 | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 360 | posix_ret = 0; | ||
| 361 | auto iter = open_sockets.find(socket_handle); | ||
| 362 | if (iter != open_sockets.end() && iter->second.blocking == false) | ||
| 363 | posix_ret |= 4; // O_NONBLOCK | ||
| 364 | #else | ||
| 365 | int ret = ::fcntl(socket_handle, F_GETFL, 0); | ||
| 366 | if (ret == SOCKET_ERROR_VALUE) { | ||
| 367 | result = TranslateError(GET_ERRNO); | ||
| 368 | posix_ret = -1; | ||
| 369 | return; | ||
| 370 | } | ||
| 371 | posix_ret = 0; | ||
| 372 | if (ret & O_NONBLOCK) | ||
| 373 | posix_ret |= 4; // O_NONBLOCK | ||
| 374 | #endif | ||
| 375 | } else if (ctr_cmd == 4) { // F_SETFL | ||
| 376 | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 377 | unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0; | ||
| 378 | int ret = ioctlsocket(socket_handle, FIONBIO, &tmp); | ||
| 379 | if (ret == SOCKET_ERROR_VALUE) { | ||
| 380 | result = TranslateError(GET_ERRNO); | ||
| 381 | posix_ret = -1; | ||
| 382 | return; | ||
| 383 | } | ||
| 384 | auto iter = open_sockets.find(socket_handle); | ||
| 385 | if (iter != open_sockets.end()) | ||
| 386 | iter->second.blocking = (tmp == 0); | ||
| 387 | #else | ||
| 388 | int flags = ::fcntl(socket_handle, F_GETFL, 0); | ||
| 389 | if (flags == SOCKET_ERROR_VALUE) { | ||
| 390 | result = TranslateError(GET_ERRNO); | ||
| 391 | posix_ret = -1; | ||
| 392 | return; | ||
| 393 | } | ||
| 394 | |||
| 395 | flags &= ~O_NONBLOCK; | ||
| 396 | if (ctr_arg & 4) // O_NONBLOCK | ||
| 397 | flags |= O_NONBLOCK; | ||
| 398 | |||
| 399 | int ret = ::fcntl(socket_handle, F_SETFL, flags); | ||
| 400 | if (ret == SOCKET_ERROR_VALUE) { | ||
| 401 | result = TranslateError(GET_ERRNO); | ||
| 402 | posix_ret = -1; | ||
| 403 | return; | ||
| 404 | } | ||
| 405 | #endif | ||
| 406 | } else { | ||
| 407 | LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call"); | ||
| 408 | result = TranslateError(EINVAL); // TODO: Find the correct error | ||
| 409 | posix_ret = -1; | ||
| 410 | return; | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | static void Listen(Service::Interface* self) { | ||
| 415 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 416 | u32 socket_handle = cmd_buffer[1]; | ||
| 417 | u32 backlog = cmd_buffer[2]; | ||
| 418 | |||
| 419 | int ret = ::listen(socket_handle, backlog); | ||
| 420 | int result = 0; | ||
| 421 | if (ret != 0) | ||
| 422 | result = TranslateError(GET_ERRNO); | ||
| 423 | |||
| 424 | cmd_buffer[2] = ret; | ||
| 425 | cmd_buffer[1] = result; | ||
| 426 | } | ||
| 427 | |||
| 428 | static void Accept(Service::Interface* self) { | ||
| 429 | // TODO(Subv): Calling this function on a blocking socket will block the emu thread, | ||
| 430 | // preventing graceful shutdown when closing the emulator, this can be fixed by always | ||
| 431 | // performing nonblocking operations and spinlock until the data is available | ||
| 432 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 433 | u32 socket_handle = cmd_buffer[1]; | ||
| 434 | socklen_t max_addr_len = static_cast<socklen_t>(cmd_buffer[2]); | ||
| 435 | sockaddr addr; | ||
| 436 | socklen_t addr_len = sizeof(addr); | ||
| 437 | u32 ret = static_cast<u32>(::accept(socket_handle, &addr, &addr_len)); | ||
| 438 | |||
| 439 | if (ret != SOCKET_ERROR_VALUE) | ||
| 440 | open_sockets[ret] = { ret, true }; | ||
| 441 | |||
| 442 | int result = 0; | ||
| 443 | if (ret == SOCKET_ERROR_VALUE) { | ||
| 444 | result = TranslateError(GET_ERRNO); | ||
| 445 | } else { | ||
| 446 | CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr); | ||
| 447 | Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len); | ||
| 448 | } | ||
| 449 | |||
| 450 | cmd_buffer[2] = ret; | ||
| 451 | cmd_buffer[1] = result; | ||
| 452 | } | ||
| 453 | |||
| 454 | static void GetHostId(Service::Interface* self) { | ||
| 455 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 456 | |||
| 457 | char name[128]; | ||
| 458 | gethostname(name, sizeof(name)); | ||
| 459 | hostent* host = gethostbyname(name); | ||
| 460 | in_addr* addr = reinterpret_cast<in_addr*>(host->h_addr); | ||
| 461 | |||
| 462 | cmd_buffer[2] = addr->s_addr; | ||
| 463 | cmd_buffer[1] = 0; | ||
| 464 | } | ||
| 465 | |||
| 466 | static void Close(Service::Interface* self) { | ||
| 467 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 468 | u32 socket_handle = cmd_buffer[1]; | ||
| 469 | |||
| 470 | int ret = 0; | ||
| 471 | open_sockets.erase(socket_handle); | ||
| 472 | |||
| 473 | ret = closesocket(socket_handle); | ||
| 474 | |||
| 475 | int result = 0; | ||
| 476 | if (ret != 0) | ||
| 477 | result = TranslateError(GET_ERRNO); | ||
| 478 | |||
| 479 | cmd_buffer[2] = ret; | ||
| 480 | cmd_buffer[1] = result; | ||
| 481 | } | ||
| 482 | |||
| 483 | static void SendTo(Service::Interface* self) { | ||
| 484 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 485 | u32 socket_handle = cmd_buffer[1]; | ||
| 486 | u32 len = cmd_buffer[2]; | ||
| 487 | u32 flags = cmd_buffer[3]; | ||
| 488 | u32 addr_len = cmd_buffer[4]; | ||
| 489 | |||
| 490 | u8* input_buff = Memory::GetPointer(cmd_buffer[8]); | ||
| 491 | CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[10])); | ||
| 492 | |||
| 493 | if (ctr_dest_addr == nullptr) { | ||
| 494 | cmd_buffer[1] = -1; // TODO(Subv): Find the right error code | ||
| 495 | return; | ||
| 496 | } | ||
| 497 | |||
| 498 | int ret = -1; | ||
| 499 | if (addr_len > 0) { | ||
| 500 | sockaddr dest_addr = CTRSockAddr::ToPlatform(*ctr_dest_addr); | ||
| 501 | ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, &dest_addr, sizeof(dest_addr)); | ||
| 502 | } else { | ||
| 503 | ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, nullptr, 0); | ||
| 504 | } | ||
| 505 | |||
| 506 | int result = 0; | ||
| 507 | if (ret == SOCKET_ERROR_VALUE) | ||
| 508 | result = TranslateError(GET_ERRNO); | ||
| 509 | |||
| 510 | cmd_buffer[2] = ret; | ||
| 511 | cmd_buffer[1] = result; | ||
| 512 | } | ||
| 513 | |||
| 514 | static void RecvFrom(Service::Interface* self) { | ||
| 515 | // TODO(Subv): Calling this function on a blocking socket will block the emu thread, | ||
| 516 | // preventing graceful shutdown when closing the emulator, this can be fixed by always | ||
| 517 | // performing nonblocking operations and spinlock until the data is available | ||
| 518 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 519 | u32 socket_handle = cmd_buffer[1]; | ||
| 520 | u32 len = cmd_buffer[2]; | ||
| 521 | u32 flags = cmd_buffer[3]; | ||
| 522 | socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]); | ||
| 523 | |||
| 524 | u8* output_buff = Memory::GetPointer(cmd_buffer[0x104 >> 2]); | ||
| 525 | sockaddr src_addr; | ||
| 526 | socklen_t src_addr_len = sizeof(src_addr); | ||
| 527 | int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len); | ||
| 528 | |||
| 529 | if (cmd_buffer[0x1A0 >> 2] != 0) { | ||
| 530 | CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x1A0 >> 2])); | ||
| 531 | *ctr_src_addr = CTRSockAddr::FromPlatform(src_addr); | ||
| 532 | } | ||
| 533 | |||
| 534 | int result = 0; | ||
| 535 | int total_received = ret; | ||
| 536 | if (ret == SOCKET_ERROR_VALUE) { | ||
| 537 | result = TranslateError(GET_ERRNO); | ||
| 538 | total_received = 0; | ||
| 539 | } | ||
| 540 | |||
| 541 | cmd_buffer[1] = result; | ||
| 542 | cmd_buffer[2] = ret; | ||
| 543 | cmd_buffer[3] = total_received; | ||
| 544 | } | ||
| 545 | |||
| 546 | static void Poll(Service::Interface* self) { | ||
| 547 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 548 | u32 nfds = cmd_buffer[1]; | ||
| 549 | int timeout = cmd_buffer[2]; | ||
| 550 | CTRPollFD* input_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[6])); | ||
| 551 | CTRPollFD* output_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[0x104 >> 2])); | ||
| 552 | |||
| 553 | // The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes) | ||
| 554 | // so we have to copy the data | ||
| 555 | pollfd* platform_pollfd = new pollfd[nfds]; | ||
| 556 | for (unsigned current_fds = 0; current_fds < nfds; ++current_fds) | ||
| 557 | platform_pollfd[current_fds] = CTRPollFD::ToPlatform(input_fds[current_fds]); | ||
| 558 | |||
| 559 | int ret = ::poll(platform_pollfd, nfds, timeout); | ||
| 560 | |||
| 561 | // Now update the output pollfd structure | ||
| 562 | for (unsigned current_fds = 0; current_fds < nfds; ++current_fds) | ||
| 563 | output_fds[current_fds] = CTRPollFD::FromPlatform(platform_pollfd[current_fds]); | ||
| 564 | |||
| 565 | delete[] platform_pollfd; | ||
| 566 | |||
| 567 | int result = 0; | ||
| 568 | if (ret == SOCKET_ERROR_VALUE) | ||
| 569 | result = TranslateError(GET_ERRNO); | ||
| 570 | |||
| 571 | cmd_buffer[1] = result; | ||
| 572 | cmd_buffer[2] = ret; | ||
| 573 | } | ||
| 574 | |||
| 575 | static void GetSockName(Service::Interface* self) { | ||
| 576 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 577 | u32 socket_handle = cmd_buffer[1]; | ||
| 578 | socklen_t ctr_len = cmd_buffer[2]; | ||
| 579 | |||
| 580 | CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2])); | ||
| 581 | |||
| 582 | sockaddr dest_addr; | ||
| 583 | socklen_t dest_addr_len = sizeof(dest_addr); | ||
| 584 | int ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len); | ||
| 585 | |||
| 586 | if (ctr_dest_addr != nullptr) { | ||
| 587 | *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr); | ||
| 588 | } else { | ||
| 589 | cmd_buffer[1] = -1; // TODO(Subv): Verify error | ||
| 590 | return; | ||
| 591 | } | ||
| 592 | |||
| 593 | int result = 0; | ||
| 594 | if (ret != 0) | ||
| 595 | result = TranslateError(GET_ERRNO); | ||
| 596 | |||
| 597 | cmd_buffer[2] = ret; | ||
| 598 | cmd_buffer[1] = result; | ||
| 599 | } | ||
| 600 | |||
| 601 | static void Shutdown(Service::Interface* self) { | ||
| 602 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 603 | u32 socket_handle = cmd_buffer[1]; | ||
| 604 | int how = cmd_buffer[2]; | ||
| 605 | |||
| 606 | int ret = ::shutdown(socket_handle, how); | ||
| 607 | int result = 0; | ||
| 608 | if (ret != 0) | ||
| 609 | result = TranslateError(GET_ERRNO); | ||
| 610 | cmd_buffer[2] = ret; | ||
| 611 | cmd_buffer[1] = result; | ||
| 612 | } | ||
| 613 | |||
| 614 | static void GetPeerName(Service::Interface* self) { | ||
| 615 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 616 | u32 socket_handle = cmd_buffer[1]; | ||
| 617 | socklen_t len = cmd_buffer[2]; | ||
| 618 | |||
| 619 | CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2])); | ||
| 620 | |||
| 621 | sockaddr dest_addr; | ||
| 622 | socklen_t dest_addr_len = sizeof(dest_addr); | ||
| 623 | int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len); | ||
| 624 | |||
| 625 | if (ctr_dest_addr != nullptr) { | ||
| 626 | *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr); | ||
| 627 | } else { | ||
| 628 | cmd_buffer[1] = -1; | ||
| 629 | return; | ||
| 630 | } | ||
| 631 | |||
| 632 | int result = 0; | ||
| 633 | if (ret != 0) | ||
| 634 | result = TranslateError(GET_ERRNO); | ||
| 635 | |||
| 636 | cmd_buffer[2] = ret; | ||
| 637 | cmd_buffer[1] = result; | ||
| 638 | } | ||
| 639 | |||
| 640 | static void Connect(Service::Interface* self) { | ||
| 641 | // TODO(Subv): Calling this function on a blocking socket will block the emu thread, | ||
| 642 | // preventing graceful shutdown when closing the emulator, this can be fixed by always | ||
| 643 | // performing nonblocking operations and spinlock until the data is available | ||
| 644 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 645 | u32 socket_handle = cmd_buffer[1]; | ||
| 646 | socklen_t len = cmd_buffer[2]; | ||
| 647 | |||
| 648 | CTRSockAddr* ctr_input_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6])); | ||
| 649 | if (ctr_input_addr == nullptr) { | ||
| 650 | cmd_buffer[1] = -1; // TODO(Subv): Verify error | ||
| 651 | return; | ||
| 652 | } | ||
| 653 | |||
| 654 | sockaddr input_addr = CTRSockAddr::ToPlatform(*ctr_input_addr); | ||
| 655 | int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr)); | ||
| 656 | int result = 0; | ||
| 657 | if (ret != 0) | ||
| 658 | result = TranslateError(GET_ERRNO); | ||
| 659 | cmd_buffer[2] = ret; | ||
| 660 | cmd_buffer[1] = result; | ||
| 661 | } | ||
| 662 | |||
| 663 | static void InitializeSockets(Service::Interface* self) { | ||
| 664 | // TODO(Subv): Implement | ||
| 665 | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 666 | WSADATA data; | ||
| 667 | WSAStartup(MAKEWORD(2, 2), &data); | ||
| 668 | #endif | ||
| 669 | |||
| 670 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 671 | cmd_buffer[1] = 0; | ||
| 672 | } | ||
| 673 | |||
| 674 | static void ShutdownSockets(Service::Interface* self) { | ||
| 675 | // TODO(Subv): Implement | ||
| 676 | CleanupSockets(); | ||
| 677 | |||
| 678 | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 679 | WSACleanup(); | ||
| 680 | #endif | ||
| 681 | |||
| 682 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||
| 683 | cmd_buffer[1] = 0; | ||
| 684 | } | ||
| 685 | |||
| 14 | const Interface::FunctionInfo FunctionTable[] = { | 686 | const Interface::FunctionInfo FunctionTable[] = { |
| 15 | {0x00010044, nullptr, "InitializeSockets"}, | 687 | {0x00010044, InitializeSockets, "InitializeSockets"}, |
| 16 | {0x000200C2, nullptr, "socket"}, | 688 | {0x000200C2, Socket, "Socket"}, |
| 17 | {0x00030082, nullptr, "listen"}, | 689 | {0x00030082, Listen, "Listen"}, |
| 18 | {0x00040082, nullptr, "accept"}, | 690 | {0x00040082, Accept, "Accept"}, |
| 19 | {0x00050084, nullptr, "bind"}, | 691 | {0x00050084, Bind, "Bind"}, |
| 20 | {0x00060084, nullptr, "connect"}, | 692 | {0x00060084, Connect, "Connect"}, |
| 21 | {0x00070104, nullptr, "recvfrom_other"}, | 693 | {0x00070104, nullptr, "recvfrom_other"}, |
| 22 | {0x00080102, nullptr, "recvfrom"}, | 694 | {0x00080102, RecvFrom, "RecvFrom"}, |
| 23 | {0x00090106, nullptr, "sendto_other"}, | 695 | {0x00090106, nullptr, "sendto_other"}, |
| 24 | {0x000A0106, nullptr, "sendto"}, | 696 | {0x000A0106, SendTo, "SendTo"}, |
| 25 | {0x000B0042, nullptr, "close"}, | 697 | {0x000B0042, Close, "Close"}, |
| 26 | {0x000C0082, nullptr, "shutdown"}, | 698 | {0x000C0082, Shutdown, "Shutdown"}, |
| 27 | {0x000D0082, nullptr, "gethostbyname"}, | 699 | {0x000D0082, nullptr, "GetHostByName"}, |
| 28 | {0x000E00C2, nullptr, "gethostbyaddr"}, | 700 | {0x000E00C2, nullptr, "GetHostByAddr"}, |
| 29 | {0x000F0106, nullptr, "unknown_resolve_ip"}, | 701 | {0x000F0106, nullptr, "unknown_resolve_ip"}, |
| 30 | {0x00110102, nullptr, "getsockopt"}, | 702 | {0x00110102, nullptr, "GetSockOpt"}, |
| 31 | {0x00120104, nullptr, "setsockopt"}, | 703 | {0x00120104, nullptr, "SetSockOpt"}, |
| 32 | {0x001300C2, nullptr, "fcntl"}, | 704 | {0x001300C2, Fcntl, "Fcntl"}, |
| 33 | {0x00140084, nullptr, "poll"}, | 705 | {0x00140084, Poll, "Poll"}, |
| 34 | {0x00150042, nullptr, "sockatmark"}, | 706 | {0x00150042, nullptr, "SockAtMark"}, |
| 35 | {0x00160000, nullptr, "gethostid"}, | 707 | {0x00160000, GetHostId, "GetHostId"}, |
| 36 | {0x00170082, nullptr, "getsockname"}, | 708 | {0x00170082, GetSockName, "GetSockName"}, |
| 37 | {0x00180082, nullptr, "getpeername"}, | 709 | {0x00180082, GetPeerName, "GetPeerName"}, |
| 38 | {0x00190000, nullptr, "ShutdownSockets"}, | 710 | {0x00190000, ShutdownSockets, "ShutdownSockets"}, |
| 39 | {0x001A00C0, nullptr, "GetNetworkOpt"}, | 711 | {0x001A00C0, nullptr, "GetNetworkOpt"}, |
| 40 | {0x001B0040, nullptr, "ICMPSocket"}, | 712 | {0x001B0040, nullptr, "ICMPSocket"}, |
| 41 | {0x001C0104, nullptr, "ICMPPing"}, | 713 | {0x001C0104, nullptr, "ICMPPing"}, |
| @@ -52,4 +724,11 @@ Interface::Interface() { | |||
| 52 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 724 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 53 | } | 725 | } |
| 54 | 726 | ||
| 727 | Interface::~Interface() { | ||
| 728 | CleanupSockets(); | ||
| 729 | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 730 | WSACleanup(); | ||
| 731 | #endif | ||
| 732 | } | ||
| 733 | |||
| 55 | } // namespace | 734 | } // namespace |
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h index 2edf3b482..483b3111b 100644 --- a/src/core/hle/service/soc_u.h +++ b/src/core/hle/service/soc_u.h | |||
| @@ -14,6 +14,7 @@ namespace SOC_U { | |||
| 14 | class Interface : public Service::Interface { | 14 | class Interface : public Service::Interface { |
| 15 | public: | 15 | public: |
| 16 | Interface(); | 16 | Interface(); |
| 17 | ~Interface(); | ||
| 17 | 18 | ||
| 18 | std::string GetPortName() const override { | 19 | std::string GetPortName() const override { |
| 19 | return "soc:U"; | 20 | return "soc:U"; |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 25944fc68..c25409a9f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -352,6 +352,18 @@ static s64 GetSystemTick() { | |||
| 352 | return (s64)Core::g_app_core->GetTicks(); | 352 | return (s64)Core::g_app_core->GetTicks(); |
| 353 | } | 353 | } |
| 354 | 354 | ||
| 355 | /// Creates a memory block at the specified address with the specified permissions and size | ||
| 356 | static Result CreateMemoryBlock(Handle* memblock, u32 addr, u32 size, u32 my_permission, | ||
| 357 | u32 other_permission) { | ||
| 358 | |||
| 359 | // TODO(Subv): Implement this function | ||
| 360 | |||
| 361 | Handle shared_memory = Kernel::CreateSharedMemory(); | ||
| 362 | *memblock = shared_memory; | ||
| 363 | LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr); | ||
| 364 | return 0; | ||
| 365 | } | ||
| 366 | |||
| 355 | const HLE::FunctionDef SVC_Table[] = { | 367 | const HLE::FunctionDef SVC_Table[] = { |
| 356 | {0x00, nullptr, "Unknown"}, | 368 | {0x00, nullptr, "Unknown"}, |
| 357 | {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"}, | 369 | {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"}, |
| @@ -383,7 +395,7 @@ const HLE::FunctionDef SVC_Table[] = { | |||
| 383 | {0x1B, nullptr, "SetTimer"}, | 395 | {0x1B, nullptr, "SetTimer"}, |
| 384 | {0x1C, nullptr, "CancelTimer"}, | 396 | {0x1C, nullptr, "CancelTimer"}, |
| 385 | {0x1D, nullptr, "ClearTimer"}, | 397 | {0x1D, nullptr, "ClearTimer"}, |
| 386 | {0x1E, nullptr, "CreateMemoryBlock"}, | 398 | {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"}, |
| 387 | {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, | 399 | {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, |
| 388 | {0x20, nullptr, "UnmapMemoryBlock"}, | 400 | {0x20, nullptr, "UnmapMemoryBlock"}, |
| 389 | {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"}, | 401 | {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"}, |