mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-21 07:18:33 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be2e84f35a | |||
| 5c54abf353 |
@@ -5,33 +5,136 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<IShopServiceAsync> {
|
||||
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<u32> error_code{0};
|
||||
|
||||
std::mutex data_mutex;
|
||||
std::vector<u8> 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<u64> out_size) {
|
||||
LOG_DEBUG(Service_NIM, "called");
|
||||
std::scoped_lock lock{data_mutex};
|
||||
*out_size = download_data.size();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Read(Out<u64> out_size, u64 offset, OutBuffer<BufferAttr_HipcAutoSelect> out_buffer) {
|
||||
std::scoped_lock lock{data_mutex};
|
||||
|
||||
u64 actual_read = 0;
|
||||
if (offset < download_data.size()) {
|
||||
actual_read = std::min<u64>(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<u32> 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<char, BufferAttr_HipcMapAlias> in_path, InArray<char, BufferAttr_HipcMapAlias> 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<IShopServiceAsync>(system);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, system);
|
||||
rb.PushCopyObjects(ctx, async_interface->GetEvent());
|
||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, std::move(async_interface));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -74,8 +74,8 @@ static constexpr char DEFAULT_DISCORD_IMAGE[] =
|
||||
"https://git.eden-emu.dev/eden-emu/eden/raw/branch/master/dist/qt_themes/default/icons/256x256/"
|
||||
"eden.png";
|
||||
|
||||
void DiscordImpl::UpdateGameStatus(bool use_default) {
|
||||
const std::string url = use_default ? std::string{DEFAULT_DISCORD_IMAGE} : game_url;
|
||||
void DiscordImpl::UpdateGameStatus(std::string_view game_url, bool has_boxart) {
|
||||
const std::string url = std::string{has_boxart ? game_url : DEFAULT_DISCORD_IMAGE};
|
||||
s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
@@ -98,7 +98,7 @@ void DiscordImpl::Update() {
|
||||
|
||||
// Used to format Icon URL for yuzu website game compatibility page
|
||||
std::string icon_name = GetGameString(game_title);
|
||||
game_url = fmt::format(
|
||||
auto const game_url = fmt::format(
|
||||
"https://raw.githubusercontent.com/eden-emulator/boxart/refs/heads/master/img/{}.png",
|
||||
icon_name);
|
||||
|
||||
@@ -117,7 +117,7 @@ void DiscordImpl::Update() {
|
||||
};
|
||||
|
||||
auto res = client.send(request);
|
||||
UpdateGameStatus(res && res->status == 200);
|
||||
UpdateGameStatus(game_url, res && res->status == 200);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
@@ -26,11 +26,9 @@ public:
|
||||
|
||||
private:
|
||||
std::string GetGameString(const std::string& title);
|
||||
void UpdateGameStatus(bool use_default);
|
||||
void UpdateGameStatus(std::string_view game_url, bool use_default);
|
||||
|
||||
std::string game_url{};
|
||||
std::string game_title{};
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user