mirror of
https://github.com/Awakenchan/LanternEngine.git
synced 2026-11-10 17:02:57 -10:00
428 lines
13 KiB
C++
428 lines
13 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
// bbport co-op: what shadPS4 0.20.0's network and NP libraries need from the rest of
|
|
// shadPS4, implemented on top of the bbport runtime, and their registration.
|
|
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <deque>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#ifdef _WIN32
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#include <windows.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "common/elf_info.h"
|
|
#include "common/types.h"
|
|
#include "core/emulator_settings.h"
|
|
#include "core/debugger.h"
|
|
#include "core/libraries/kernel/kernel.h"
|
|
#include "core/libraries/kernel/memory.h"
|
|
#include "core/libraries/kernel/threads/pthread.h"
|
|
#include "core/libraries/kernel/time.h"
|
|
#include "core/libraries/libs.h"
|
|
#include "core/libraries/system/systemservice.h"
|
|
#include "core/libraries/system/userservice.h"
|
|
#include "core/user_manager.h"
|
|
#include "imgui/invitation_prompt_layer.h"
|
|
#include "imgui/shadnet_notifications_layer.h"
|
|
|
|
#include "core/libraries/network/http.h"
|
|
#include "core/libraries/network/http2.h"
|
|
#include "core/libraries/net/net.h"
|
|
#include "core/libraries/network/netctl.h"
|
|
#include "core/libraries/network/ssl2.h"
|
|
#include "core/libraries/np/np_auth.h"
|
|
#include "core/libraries/np/np_common.h"
|
|
#include "core/libraries/np/np_manager.h"
|
|
#include "core/libraries/np/np_matching2/np_matching2.h"
|
|
#include "core/libraries/np/np_partner.h"
|
|
#include "core/libraries/np/np_party.h"
|
|
#include "core/libraries/np/np_score/np_score.h"
|
|
#include "core/libraries/np/np_signaling/np_signaling.h"
|
|
#include "core/libraries/np/np_tus/np_tus.h"
|
|
#include "core/libraries/np/np_utility/np_utility.h"
|
|
#include "core/libraries/np/np_web_api/np_web_api.h"
|
|
#include "core/libraries/np/np_web_api2/np_web_api2.h"
|
|
#include "thd/thd.h"
|
|
|
|
extern "C" {
|
|
int32_t runtime_thread_spawn(void** out, void* PS4_SYSV_ABI (*entry)(void*), void* argument,
|
|
uint64_t stack, const char* name);
|
|
int32_t runtime_thread_join_spawned(void* thread, void** result);
|
|
[[noreturn]] void runtime_thread_exit(void* value);
|
|
}
|
|
|
|
// ---- Kernel ---------------------------------------------------------------------------------
|
|
|
|
namespace Libraries::Kernel {
|
|
|
|
struct BbNetMutex {
|
|
std::recursive_mutex mutex;
|
|
bool recursive{};
|
|
int depth{};
|
|
};
|
|
struct BbNetMutexAttr {
|
|
PthreadMutexType type{PthreadMutexType::ErrorCheck};
|
|
};
|
|
struct BbNetCond {
|
|
std::condition_variable_any cond;
|
|
};
|
|
struct BbNetCondAttr {};
|
|
struct BbNetThreadAttr {
|
|
u64 stack{};
|
|
};
|
|
|
|
s32 ErrnoToSceKernelError(s32 e) {
|
|
return e > 0 && e <= 0xffff ? s32(0x80020000u | u32(e)) : e;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceKernelGetSystemSwVersion(SwVersionStruct* ret) {
|
|
if (!ret) {
|
|
return ORBIS_KERNEL_ERROR_EINVAL;
|
|
}
|
|
std::memset(ret->text_representation, 0, sizeof(ret->text_representation));
|
|
std::snprintf(ret->text_representation, sizeof(ret->text_representation), "%s", "11.000.000");
|
|
ret->hex_representation = 0x11000001;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceKernelClockGettime(u32 clock_id, OrbisKernelTimespec* tp) {
|
|
if (!tp) {
|
|
return ORBIS_KERNEL_ERROR_EFAULT;
|
|
}
|
|
s64 ns;
|
|
if (clock_id == ORBIS_CLOCK_REALTIME || clock_id == ORBIS_CLOCK_REALTIME_PRECISE ||
|
|
clock_id == ORBIS_CLOCK_REALTIME_FAST || clock_id == ORBIS_CLOCK_SECOND) {
|
|
ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
std::chrono::system_clock::now().time_since_epoch())
|
|
.count();
|
|
} else {
|
|
ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
std::chrono::steady_clock::now().time_since_epoch())
|
|
.count();
|
|
}
|
|
tp->tv_sec = ns / 1000000000;
|
|
tp->tv_nsec = ns % 1000000000;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32, s32,
|
|
const char*) {
|
|
if (!addr_in_out || !len) {
|
|
return ORBIS_KERNEL_ERROR_EINVAL;
|
|
}
|
|
void* memory = std::calloc(1, len);
|
|
if (!memory) {
|
|
return ORBIS_KERNEL_ERROR_ENOMEM;
|
|
}
|
|
*addr_in_out = memory;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceKernelMunmap(void* addr, u64) {
|
|
std::free(addr);
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
int posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) {
|
|
if (!attr) return 22;
|
|
*attr = new BbNetMutexAttr;
|
|
return 0;
|
|
}
|
|
int posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type) {
|
|
if (!attr || !*attr) return 22;
|
|
(*attr)->type = type;
|
|
return 0;
|
|
}
|
|
int posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) {
|
|
if (!attr) return 22;
|
|
delete *attr;
|
|
*attr = nullptr;
|
|
return 0;
|
|
}
|
|
int scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* attr, const char*) {
|
|
if (!mutex) return 22;
|
|
*mutex = new BbNetMutex;
|
|
(*mutex)->recursive = attr && *attr && (*attr)->type == PthreadMutexType::Recursive;
|
|
return 0;
|
|
}
|
|
int posix_pthread_mutex_destroy(PthreadMutexT* mutex) {
|
|
if (!mutex || !*mutex) return 22;
|
|
delete *mutex;
|
|
*mutex = nullptr;
|
|
return 0;
|
|
}
|
|
int posix_pthread_mutex_lock(PthreadMutexT* mutex) {
|
|
if (!mutex || !*mutex) return 22;
|
|
(*mutex)->mutex.lock();
|
|
if (++(*mutex)->depth > 1 && !(*mutex)->recursive) {
|
|
--(*mutex)->depth;
|
|
(*mutex)->mutex.unlock();
|
|
return 11; // EDEADLK
|
|
}
|
|
return 0;
|
|
}
|
|
int posix_pthread_mutex_trylock(PthreadMutexT* mutex) {
|
|
if (!mutex || !*mutex) return 22;
|
|
if (!(*mutex)->mutex.try_lock()) return 16; // EBUSY
|
|
if (++(*mutex)->depth > 1 && !(*mutex)->recursive) {
|
|
--(*mutex)->depth;
|
|
(*mutex)->mutex.unlock();
|
|
return 16;
|
|
}
|
|
return 0;
|
|
}
|
|
int posix_pthread_mutex_unlock(PthreadMutexT* mutex) {
|
|
if (!mutex || !*mutex) return 22;
|
|
--(*mutex)->depth;
|
|
(*mutex)->mutex.unlock();
|
|
return 0;
|
|
}
|
|
|
|
int posix_pthread_condattr_init(PthreadCondAttrT* attr) {
|
|
if (!attr) return 22;
|
|
*attr = new BbNetCondAttr;
|
|
return 0;
|
|
}
|
|
int posix_pthread_condattr_destroy(PthreadCondAttrT* attr) {
|
|
if (!attr) return 22;
|
|
delete *attr;
|
|
*attr = nullptr;
|
|
return 0;
|
|
}
|
|
int scePthreadCondInit(PthreadCondT* cond, const PthreadCondAttrT*, const char*) {
|
|
if (!cond) return 22;
|
|
*cond = new BbNetCond;
|
|
return 0;
|
|
}
|
|
int posix_pthread_cond_destroy(PthreadCondT* cond) {
|
|
if (!cond || !*cond) return 22;
|
|
delete *cond;
|
|
*cond = nullptr;
|
|
return 0;
|
|
}
|
|
int posix_pthread_cond_signal(PthreadCondT* cond) {
|
|
if (!cond || !*cond) return 22;
|
|
(*cond)->cond.notify_one();
|
|
return 0;
|
|
}
|
|
namespace {
|
|
// The caller holds the mutex once; the wait releases and retakes it like pthread_cond_wait.
|
|
struct HeldMutex {
|
|
BbNetMutex* mutex;
|
|
void lock() {
|
|
mutex->mutex.lock();
|
|
++mutex->depth;
|
|
}
|
|
void unlock() {
|
|
--mutex->depth;
|
|
mutex->mutex.unlock();
|
|
}
|
|
};
|
|
} // namespace
|
|
int posix_pthread_cond_wait(PthreadCondT* cond, PthreadMutexT* mutex) {
|
|
if (!cond || !*cond || !mutex || !*mutex) return 22;
|
|
HeldMutex held{*mutex};
|
|
(*cond)->cond.wait(held);
|
|
return 0;
|
|
}
|
|
int posix_pthread_cond_reltimedwait_np(PthreadCondT* cond, PthreadMutexT* mutex, u64 usec) {
|
|
if (!cond || !*cond || !mutex || !*mutex) return 22;
|
|
HeldMutex held{*mutex};
|
|
const auto status = (*cond)->cond.wait_for(held, std::chrono::microseconds(usec));
|
|
return status == std::cv_status::timeout ? 60 : 0; // ETIMEDOUT
|
|
}
|
|
|
|
int posix_pthread_attr_init(PthreadAttrT* attr) {
|
|
if (!attr) return 22;
|
|
*attr = new BbNetThreadAttr;
|
|
return 0;
|
|
}
|
|
int posix_pthread_attr_destroy(PthreadAttrT* attr) {
|
|
if (!attr) return 22;
|
|
delete *attr;
|
|
*attr = nullptr;
|
|
return 0;
|
|
}
|
|
int posix_pthread_attr_setstacksize(PthreadAttrT* attr, size_t stack_size) {
|
|
if (!attr || !*attr) return 22;
|
|
(*attr)->stack = stack_size;
|
|
return 0;
|
|
}
|
|
int posix_pthread_attr_setinheritsched(PthreadAttrT*, int) {
|
|
return 0;
|
|
}
|
|
int posix_pthread_attr_setschedpolicy(PthreadAttrT*, SchedPolicy) {
|
|
return 0;
|
|
}
|
|
int posix_pthread_attr_setschedparam(PthreadAttrT*, const SchedParam*) {
|
|
return 0;
|
|
}
|
|
int scePthreadAttrSetaffinity(PthreadAttrT*, u64) {
|
|
return 0;
|
|
}
|
|
int posix_pthread_create_name_np(PthreadT* thread, const PthreadAttrT* attr,
|
|
PthreadEntryFunc start, void* arg, const char* name) {
|
|
if (!thread || !start) return 22;
|
|
const u64 stack = attr && *attr ? (*attr)->stack : 0;
|
|
return runtime_thread_spawn(thread, start, arg, stack, name ? name : "np");
|
|
}
|
|
int posix_pthread_join(PthreadT thread, void** ret) {
|
|
return runtime_thread_join_spawned(thread, ret);
|
|
}
|
|
void posix_pthread_exit(void* status) {
|
|
runtime_thread_exit(status);
|
|
}
|
|
|
|
} // namespace Libraries::Kernel
|
|
|
|
// ---- User and system service, debugger ----------------------------------------------------------
|
|
|
|
namespace Libraries::UserService {
|
|
s32 PS4_SYSV_ABI sceUserServiceGetInitialUser(int* user_id) {
|
|
if (!user_id) {
|
|
return s32(0x80960009); // SCE_USER_SERVICE_ERROR_INVALID_ARGUMENT
|
|
}
|
|
*user_id = UserManager::RuntimeUserId;
|
|
return ORBIS_OK;
|
|
}
|
|
} // namespace Libraries::UserService
|
|
|
|
namespace Libraries::SystemService {
|
|
void PushSystemServiceEvent(const OrbisSystemServiceEvent& event) {
|
|
std::printf("Co-op: system service event %#x not delivered (invitations are not ported)\n",
|
|
unsigned(event.event_type));
|
|
}
|
|
} // namespace Libraries::SystemService
|
|
|
|
namespace Core::Debugger {
|
|
s32 GetCurrentPid() {
|
|
#ifdef _WIN32
|
|
return s32(GetCurrentProcessId());
|
|
#else
|
|
return s32(getpid());
|
|
#endif
|
|
}
|
|
} // namespace Core::Debugger
|
|
|
|
// ---- Notifications (log only for now) ------------------------------------------------------------
|
|
|
|
namespace ImGui::ShadNetNotify {
|
|
namespace {
|
|
std::mutex history_mutex;
|
|
std::vector<HistoryEntry> history;
|
|
std::size_t unread{};
|
|
} // namespace
|
|
KindDisplay DisplayOf(Kind kind) {
|
|
switch (kind) {
|
|
case Kind::FriendRequest:
|
|
return {"Friend request", {0.9f, 0.8f, 0.3f, 1.0f}};
|
|
case Kind::FriendNew:
|
|
return {"Friend", {0.4f, 0.9f, 0.4f, 1.0f}};
|
|
case Kind::FriendLost:
|
|
return {"Friend", {0.9f, 0.4f, 0.4f, 1.0f}};
|
|
case Kind::Online:
|
|
return {"Online", {0.4f, 0.8f, 0.9f, 1.0f}};
|
|
case Kind::Warning:
|
|
return {"Warning", {0.95f, 0.6f, 0.2f, 1.0f}};
|
|
default:
|
|
return {"Info", {0.8f, 0.8f, 0.8f, 1.0f}};
|
|
}
|
|
}
|
|
void Push(Kind kind, std::string text) {
|
|
std::printf("Co-op: %s: %s\n", DisplayOf(kind).tag, text.c_str());
|
|
std::scoped_lock lock{history_mutex};
|
|
history.push_back({kind, std::move(text), {}});
|
|
++unread;
|
|
}
|
|
std::vector<HistoryEntry> GetHistory() {
|
|
std::scoped_lock lock{history_mutex};
|
|
return history;
|
|
}
|
|
void ClearHistory() {
|
|
std::scoped_lock lock{history_mutex};
|
|
history.clear();
|
|
unread = 0;
|
|
}
|
|
std::size_t UnreadCount() {
|
|
std::scoped_lock lock{history_mutex};
|
|
return unread;
|
|
}
|
|
void MarkRead() {
|
|
std::scoped_lock lock{history_mutex};
|
|
unread = 0;
|
|
}
|
|
void Register() {}
|
|
void Unregister() {}
|
|
} // namespace ImGui::ShadNetNotify
|
|
|
|
namespace ImGui::InvitationPrompt {
|
|
void Push(s32, std::string invitation_id, std::string, std::string from_npid) {
|
|
std::printf("Co-op: invitation %s from %s (invitations are not supported yet)\n",
|
|
invitation_id.c_str(), from_npid.c_str());
|
|
}
|
|
void Dismiss(const std::string&) {}
|
|
void Register() {}
|
|
void Unregister() {}
|
|
} // namespace ImGui::InvitationPrompt
|
|
|
|
// ---- Registration ------------------------------------------------------------------------------
|
|
|
|
namespace Libraries::Np::NpManager { void InitializeRuntime(); }
|
|
extern "C" void bbnet_initialize(void) {
|
|
if (BbThd::Enabled()) {
|
|
BbThd::Initialize();
|
|
return;
|
|
}
|
|
Libraries::Np::NpManager::InitializeRuntime();
|
|
}
|
|
|
|
extern "C" void bbnet_set_image(unsigned char* image, uint64_t size) {
|
|
BbThd::SetImage(image, size);
|
|
}
|
|
|
|
extern "C" void bbnet_register(void) {
|
|
Core::Loader::SymbolsResolver resolver;
|
|
Core::Loader::SymbolsResolver* sym = &resolver;
|
|
if (BbThd::Enabled()) {
|
|
// The Hunter's Dream: its NP and sceNet imports; the game's HTTP stays sceHttp's.
|
|
BbThd::Register(sym);
|
|
Libraries::Http::RegisterLib(sym);
|
|
Libraries::Http2::RegisterLib(sym);
|
|
Libraries::Ssl2::RegisterLib(sym);
|
|
return;
|
|
}
|
|
Libraries::Net::RegisterLib(sym);
|
|
Libraries::NetCtl::RegisterLib(sym);
|
|
Libraries::Http::RegisterLib(sym);
|
|
Libraries::Http2::RegisterLib(sym);
|
|
// libSceSsl: not registered, as in shadPS4 (its loading is behind a title workaround).
|
|
Libraries::Ssl2::RegisterLib(sym);
|
|
Libraries::Np::NpAuth::RegisterLib(sym);
|
|
Libraries::Np::NpCommon::RegisterLib(sym);
|
|
Libraries::Np::NpManager::RegisterLib(sym);
|
|
Libraries::Np::NpMatching2::RegisterLib(sym);
|
|
Libraries::Np::NpPartner::RegisterLib(sym);
|
|
Libraries::Np::NpParty::RegisterLib(sym);
|
|
Libraries::Np::NpScore::RegisterLib(sym);
|
|
Libraries::Np::NpSignaling::RegisterLib(sym);
|
|
Libraries::Np::NpTus::RegisterLib(sym);
|
|
Libraries::Np::NpUtility::RegisterLib(sym);
|
|
Libraries::Np::NpWebApi::RegisterLib(sym);
|
|
Libraries::Np::NpWebApi2::RegisterLib(sym);
|
|
}
|