diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 661da88b0c..e5f2d85dde 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -5,33 +5,136 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include +#include +#include +#include + #include "core/core.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nim/nim.h" +#include "core/hle/service/os/event.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" namespace Service::NIM { - class IShopServiceAsync final : public ServiceFramework { public: explicit IShopServiceAsync(Core::System& system_) - : ServiceFramework{system_, "IShopServiceAsync"} { + : ServiceFramework{system_, "IShopServiceAsync"}, + service_context{system_, "IShopServiceAsync"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Cancel"}, - {1, nullptr, "GetSize"}, - {2, nullptr, "Read"}, - {3, nullptr, "GetErrorCode"}, - {4, nullptr, "Request"}, - {5, nullptr, "Prepare"}, + {0, D<&IShopServiceAsync::Cancel>, "Cancel"}, + {1, D<&IShopServiceAsync::GetSize>, "GetSize"}, + {2, D<&IShopServiceAsync::Read>, "Read"}, + {3, D<&IShopServiceAsync::GetErrorCode>, "GetErrorCode"}, + {4, D<&IShopServiceAsync::Request>, "Request"}, + {5, D<&IShopServiceAsync::Prepare>, "Prepare"}, }; // clang-format on RegisterHandlers(functions); + + completion_event = service_context.CreateEvent("IShopServiceAsync:Completion"); + } + + ~IShopServiceAsync() override { + CancelImpl(); + service_context.CloseEvent(completion_event); + } + + Kernel::KReadableEvent* GetEvent() const { + return &completion_event->GetReadableEvent(); + } + +private: + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* completion_event; + + std::jthread worker; + std::atomic error_code{0}; + + std::mutex data_mutex; + std::vector download_data; + + void CancelImpl() { + worker.request_stop(); + if (worker.joinable()) { + worker.join(); + } + } + + Result Cancel() { + LOG_DEBUG(Service_NIM, "called"); + CancelImpl(); + R_SUCCEED(); + } + + Result GetSize(Out out_size) { + LOG_DEBUG(Service_NIM, "called"); + std::scoped_lock lock{data_mutex}; + *out_size = download_data.size(); + R_SUCCEED(); + } + + Result Read(Out out_size, u64 offset, OutBuffer out_buffer) { + std::scoped_lock lock{data_mutex}; + + u64 actual_read = 0; + if (offset < download_data.size()) { + actual_read = std::min(out_buffer.size(), download_data.size() - offset); + std::memcpy(out_buffer.data(), download_data.data() + offset, actual_read); + } + + *out_size = actual_read; + R_SUCCEED(); + } + + Result GetErrorCode(Out out_error_code) { + LOG_DEBUG(Service_NIM, "called"); + *out_error_code = error_code.load(); + R_SUCCEED(); + } + + Result Request() { + LOG_DEBUG(Service_NIM, "(STUBBED) called"); + CancelImpl(); + + error_code.store(0); + completion_event->Clear(system.Kernel()); + + { + std::scoped_lock lock{data_mutex}; + download_data.clear(); + } + + worker = std::jthread([this](const std::stop_token& stop_token) { + if (stop_token.stop_requested()) { + error_code.store(1); + } else { + std::scoped_lock lock{data_mutex}; + // Dummy JSON response, else it fails... + const std::string dummy_response = "{}"; + download_data.assign(dummy_response.begin(), dummy_response.end()); + error_code.store(0); + } + completion_event->Signal(system.Kernel()); + }); + + R_SUCCEED(); + } + + Result Prepare(InArray in_path, InArray in_post) { + LOG_DEBUG(Service_NIM, "called"); + if (!in_path.empty()) { + std::string url(in_path.data(), in_path.size()); + LOG_INFO(Service_NIM, "Preparing request for URL: {}", url); + } + R_SUCCEED(); } }; @@ -49,11 +152,13 @@ public: } private: - void CreateAsyncInterface(HLERequestContext& ctx) { - LOG_WARNING(Service_NIM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + void CreateAsyncInterface(HLERequestContext& ctx) {LOG_DEBUG(Service_NIM, "called"); + auto async_interface = std::make_shared(system); + + IPC::ResponseBuilder rb{ctx, 2, 1, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(ctx, system); + rb.PushCopyObjects(ctx, async_interface->GetEvent()); + rb.PushIpcInterface(ctx, std::move(async_interface)); } };