From 467b961e9cf63d7bc5cd5f0632a8b458dc397c8f Mon Sep 17 00:00:00 2001 From: Sketch <75850871+SketchMaster2001@users.noreply.github.com> Date: Thu, 6 Apr 2023 22:10:05 -0400 Subject: [PATCH] IOS/Network/KD: Implement GetSchedulerStat --- Source/Core/Common/HttpRequest.cpp | 13 ++++ Source/Core/Common/HttpRequest.h | 1 + Source/Core/Core/IOS/Network/KD/NWC24Config.h | 2 + .../Core/Core/IOS/Network/KD/NetKDRequest.cpp | 73 +++++++++++++++++-- .../Core/Core/IOS/Network/KD/NetKDRequest.h | 16 +++- .../Core/Core/IOS/Network/KD/VFF/VFFUtil.cpp | 6 +- 6 files changed, 100 insertions(+), 11 deletions(-) diff --git a/Source/Core/Common/HttpRequest.cpp b/Source/Core/Common/HttpRequest.cpp index a8ed0d26ed..d98adf9826 100644 --- a/Source/Core/Common/HttpRequest.cpp +++ b/Source/Core/Common/HttpRequest.cpp @@ -30,6 +30,7 @@ public: void SetCookies(const std::string& cookies); void UseIPv4(); void FollowRedirects(long max); + s32 GetLastResponseCode(); Response Fetch(const std::string& url, Method method, const Headers& headers, const u8* payload, size_t size, AllowedReturnCodes codes = AllowedReturnCodes::Ok_Only); @@ -76,6 +77,11 @@ std::string HttpRequest::EscapeComponent(const std::string& string) return m_impl->EscapeComponent(string); } +s32 HttpRequest::GetLastResponseCode() const +{ + return m_impl->GetLastResponseCode(); +} + HttpRequest::Response HttpRequest::Get(const std::string& url, const Headers& headers, AllowedReturnCodes codes) { @@ -143,6 +149,13 @@ bool HttpRequest::Impl::IsValid() const return m_curl != nullptr; } +s32 HttpRequest::Impl::GetLastResponseCode() +{ + s32 response_code{}; + curl_easy_getinfo(m_curl.get(), CURLINFO_RESPONSE_CODE, &response_code); + return response_code; +} + void HttpRequest::Impl::SetCookies(const std::string& cookies) { curl_easy_setopt(m_curl.get(), CURLOPT_COOKIE, cookies.c_str()); diff --git a/Source/Core/Common/HttpRequest.h b/Source/Core/Common/HttpRequest.h index 921051c9c2..85148cdec7 100644 --- a/Source/Core/Common/HttpRequest.h +++ b/Source/Core/Common/HttpRequest.h @@ -39,6 +39,7 @@ public: void SetCookies(const std::string& cookies); void UseIPv4(); void FollowRedirects(long max = 1); + s32 GetLastResponseCode() const; std::string EscapeComponent(const std::string& string); Response Get(const std::string& url, const Headers& headers = {}, AllowedReturnCodes codes = AllowedReturnCodes::Ok_Only); diff --git a/Source/Core/Core/IOS/Network/KD/NWC24Config.h b/Source/Core/Core/IOS/Network/KD/NWC24Config.h index be8f4c9bc9..0f3682449e 100644 --- a/Source/Core/Core/IOS/Network/KD/NWC24Config.h +++ b/Source/Core/Core/IOS/Network/KD/NWC24Config.h @@ -19,6 +19,8 @@ enum ErrorCode : s32 { WC24_OK = 0, WC24_ERR_FATAL = -1, + WC24_ERR_INVALID_VALUE = -3, + WC24_ERR_NULL = -5, WC24_ERR_NOT_FOUND = -13, WC24_ERR_BROKEN = -14, WC24_ERR_FILE_OPEN = -16, diff --git a/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp b/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp index 8c5f80b6d4..c273a43b22 100644 --- a/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp +++ b/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp @@ -150,6 +150,9 @@ s32 NWC24MakeUserID(u64* nwc24_id, u32 hollywood_id, u16 id_ctr, HardwareModel h NetKDRequestDevice::NetKDRequestDevice(Kernel& ios, const std::string& device_name) : Device(ios, device_name), config{ios.GetFS()}, m_dl_list{ios.GetFS()} { + // Enable all NWC24 permissions + m_scheduler_buffer[1] = Common::swap32(-1); + m_work_queue.Reset("WiiConnect24 Worker", [this](AsyncTask task) { const IPCReply reply = task.handler(); { @@ -177,6 +180,34 @@ void NetKDRequestDevice::Update() } } +void NetKDRequestDevice::LogError(ErrorType error_type, s32 error_code) +{ + s32 new_code{}; + switch (error_type) + { + case ErrorType::Account: + new_code = -(101200 - error_code); + break; + case ErrorType::Client: + new_code = -(107300 - error_code); + break; + case ErrorType::KD_Download: + new_code = -(107200 - error_code); + break; + case ErrorType::Server: + new_code = -(117000 + error_code); + break; + } + + std::lock_guard lg(m_scheduler_buffer_lock); + + m_scheduler_buffer[32 + (m_error_count % 32)] = Common::swap32(new_code); + m_error_count++; + + m_scheduler_buffer[5] = Common::swap32(m_error_count); + m_scheduler_buffer[2] = Common::swap32(new_code); +} + NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index, const std::optional subtask_id) { @@ -194,7 +225,14 @@ NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index, if (!response) { - ERROR_LOG_FMT(IOS_WC24, "Failed to request data at {}", url); + const s32 last_response_code = m_http.GetLastResponseCode(); + ERROR_LOG_FMT(IOS_WC24, "Failed to request data at {}. HTTP Status Code: {}", url, + last_response_code); + + // On a real Wii, KD throws 107305 if it cannot connect to the host. While other issues other + // than invalid host may arise, this code is essentially a catch-all for HTTP client failure. + LogError(last_response_code ? ErrorType::Server : ErrorType::Client, + last_response_code ? last_response_code : NWC24::WC24_ERR_NULL); return NWC24::WC24_ERR_SERVER; } @@ -202,6 +240,7 @@ NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index, if (response->size() < sizeof(NWC24::WC24File)) { ERROR_LOG_FMT(IOS_WC24, "File at {} is too small to be a valid file.", url); + LogError(ErrorType::KD_Download, NWC24::WC24_ERR_BROKEN); return NWC24::WC24_ERR_BROKEN; } @@ -228,6 +267,9 @@ NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index, NWC24::ErrorCode reply = IOS::HLE::NWC24::OpenVFF(m_dl_list.GetVFFPath(entry_index), content_name, m_ios.GetFS(), file_data); + if (reply != NWC24::WC24_OK) + LogError(ErrorType::KD_Download, reply); + return reply; } @@ -249,15 +291,17 @@ IPCReply NetKDRequestDevice::HandleNWC24DownloadNowEx(const IOCtlRequest& reques if (entry_index >= NWC24::NWC24Dl::MAX_ENTRIES) { ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: Entry index out of range."); - WriteReturnValue(NWC24::WC24_ERR_BROKEN, request.buffer_out); - return IPCReply(NWC24::WC24_ERR_BROKEN); + LogError(ErrorType::KD_Download, NWC24::WC24_ERR_INVALID_VALUE); + WriteReturnValue(NWC24::WC24_ERR_INVALID_VALUE, request.buffer_out); + return IPCReply(IPC_SUCCESS); } if (!m_dl_list.DoesEntryExist(entry_index)) { ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: Requested entry does not exist in download list!"); + LogError(ErrorType::KD_Download, NWC24::WC24_ERR_NOT_FOUND); WriteReturnValue(NWC24::WC24_ERR_NOT_FOUND, request.buffer_out); - return IPCReply(NWC24::WC24_ERR_NOT_FOUND); + return IPCReply(IPC_SUCCESS); } // While in theory reply will always get initialized by KDDownload, things happen. @@ -287,7 +331,7 @@ IPCReply NetKDRequestDevice::HandleNWC24DownloadNowEx(const IOCtlRequest& reques } WriteReturnValue(reply, request.buffer_out); - return IPCReply(reply); + return IPCReply(IPC_SUCCESS); } std::optional NetKDRequestDevice::IOCtl(const IOCtlRequest& request) @@ -405,6 +449,7 @@ std::optional NetKDRequestDevice::IOCtl(const IOCtlRequest& request) } else { + LogError(ErrorType::Account, NWC24::WC24_ERR_INVALID_VALUE); WriteReturnValue(NWC24::WC24_ERR_FATAL, request.buffer_out); } } @@ -421,8 +466,24 @@ std::optional NetKDRequestDevice::IOCtl(const IOCtlRequest& request) break; case IOCTL_NWC24_GET_SCHEDULER_STAT: - INFO_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_GET_SCHEDULER_STAT - NI"); + { + if (request.buffer_out == 0 || request.buffer_out % 4 != 0 || request.buffer_out_size < 16) + { + return_value = IPC_EINVAL; + ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_GET_SCHEDULER_STAT = IPC_EINVAL"); + break; + } + + INFO_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_GET_SCHEDULER_STAT - buffer out size: {}", + request.buffer_out_size); + + // On a real Wii, GetSchedulerStat copies memory containing a list of error codes recorded by + // KD among other things. In most instances there will never be more than one error code + // recorded as we do not have a scheduler. + const u32 out_size = std::min(request.buffer_out_size, 256U); + memory.CopyToEmu(request.buffer_out, m_scheduler_buffer.data(), out_size); break; + } case IOCTL_NWC24_SAVE_MAIL_NOW: INFO_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_SAVE_MAIL_NOW - NI"); diff --git a/Source/Core/Core/IOS/Network/KD/NetKDRequest.h b/Source/Core/Core/IOS/Network/KD/NetKDRequest.h index aae52a11f5..cb8af0d3f5 100644 --- a/Source/Core/Core/IOS/Network/KD/NetKDRequest.h +++ b/Source/Core/Core/IOS/Network/KD/NetKDRequest.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -16,8 +17,6 @@ namespace IOS::HLE { -constexpr const char DL_CNT_PATH[] = "/" WII_WC24CONF_DIR "/dlcnt.bin"; - // KD is the IOS module responsible for implementing WiiConnect24 functionality. // It can perform HTTPS downloads, send and receive mail via SMTP, and execute a // JavaScript-like language while the Wii is in standby mode. @@ -52,11 +51,24 @@ private: return std::nullopt; } + enum class ErrorType + { + Account, + KD_Download, + Client, + Server, + }; + + void LogError(ErrorType error_type, s32 error_code); + NWC24::NWC24Config config; NWC24::NWC24Dl m_dl_list; Common::WorkQueueThread m_work_queue; std::mutex m_async_reply_lock; + std::mutex m_scheduler_buffer_lock; std::queue m_async_replies; + u32 m_error_count = 0; + std::array m_scheduler_buffer{}; // TODO: Maybe move away from Common::HttpRequest? Common::HttpRequest m_http{std::chrono::minutes{1}}; }; diff --git a/Source/Core/Core/IOS/Network/KD/VFF/VFFUtil.cpp b/Source/Core/Core/IOS/Network/KD/VFF/VFFUtil.cpp index 16ba1b1ad7..4a1389c626 100644 --- a/Source/Core/Core/IOS/Network/KD/VFF/VFFUtil.cpp +++ b/Source/Core/Core/IOS/Network/KD/VFF/VFFUtil.cpp @@ -267,7 +267,7 @@ ErrorCode OpenVFF(const std::string& path, const std::string& filename, if (!temp) { ERROR_LOG_FMT(IOS_WC24, "Failed to open VFF at: {}", path); - return_value = WC24_ERR_NOT_FOUND; + return_value = WC24_ERR_FILE_OPEN; return; } @@ -281,7 +281,7 @@ ErrorCode OpenVFF(const std::string& path, const std::string& filename, { // The VFF is most likely broken. ERROR_LOG_FMT(IOS_WC24, "Failed to mount VFF at: {}", path); - return_value = WC24_ERR_BROKEN; + return_value = WC24_ERR_FILE_READ; return; } @@ -290,7 +290,7 @@ ErrorCode OpenVFF(const std::string& path, const std::string& filename, { // The VFF is most likely broken. ERROR_LOG_FMT(IOS_WC24, "Failed to mount VFF at: {}", path); - return_value = WC24_ERR_BROKEN; + return_value = WC24_ERR_FILE_READ; return; }