mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-07 13:39:55 +00:00
181 lines
12 KiB
C++
181 lines
12 KiB
C++
#include "prx/libSceNpParty/libSceNpParty.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
constexpr int kAlreadyInitialized = static_cast<int>(0x80552502u);
|
|
constexpr int kNotInitialized = static_cast<int>(0x80552503u);
|
|
constexpr int kInvalidArgument = static_cast<int>(0x80552504u);
|
|
constexpr int kNotInParty = static_cast<int>(0x80552506u);
|
|
constexpr int kHandlerState = static_cast<int>(0x80552511u);
|
|
|
|
bool check(bool value, const char* message) {
|
|
if (!value) std::fprintf(stderr, "%s\n", message);
|
|
return value;
|
|
}
|
|
|
|
bool uninitializedTests() {
|
|
std::uint16_t state = 0xa5a5;
|
|
std::uint8_t out[64];
|
|
std::memset(out, 0xa5, sizeof(out));
|
|
bool ok = check(sceNpPartyGetState(&state) == kNotInitialized && sceNpPartyGetState(nullptr) == kNotInitialized, "get state before initialize did not fail")
|
|
&& check(sceNpPartyGetStateAsUserA(1, &state) == kNotInitialized && sceNpPartyGetStateAsUserA(-1, nullptr) == kNotInitialized, "get user state before initialize did not fail")
|
|
&& check(state == 0xa5a5, "a state was written before initialize")
|
|
&& check(sceNpPartyGetId(out) == kNotInitialized && sceNpPartyGetId(nullptr) == kNotInitialized, "get id before initialize did not fail")
|
|
&& check(sceNpPartyGetMembers(out) == kNotInitialized && sceNpPartyGetMembersA(out) == kNotInitialized, "get members before initialize did not fail")
|
|
&& check(sceNpPartyGetMemberInfo(1, out) == kNotInitialized && sceNpPartyGetMemberInfoA(1, nullptr) == kNotInitialized, "get member info before initialize did not fail")
|
|
&& check(sceNpPartyGetMemberSessionInfo(1, out) == kNotInitialized && sceNpPartyGetMemberVoiceInfo(1, 2, out) == kNotInitialized, "get session or voice info before initialize did not fail")
|
|
&& check(sceNpPartySendBinaryMessage(out) == kNotInitialized && sceNpPartySendBinaryMessage(nullptr) == kNotInitialized, "send before initialize did not fail")
|
|
&& check(sceNpPartyGetVoiceChatPriority(nullptr) == kNotInitialized, "get priority before initialize did not fail")
|
|
&& check(sceNpPartySetVoiceChatPriority(1) == kNotInitialized, "set priority before initialize did not fail")
|
|
&& check(sceNpPartyRegisterPrivateHandler(out) == kNotInitialized, "register private handler before initialize did not fail")
|
|
&& check(sceNpPartyUnregisterPrivateHandler() == kNotInitialized, "unregister private handler before initialize did not fail");
|
|
for (std::uint8_t byte : out) ok = ok && check(byte == 0xa5, "a call before initialize wrote its output");
|
|
return ok;
|
|
}
|
|
|
|
bool stateTests() {
|
|
std::uint16_t states[3];
|
|
std::memset(states, 0xa5, sizeof(states));
|
|
return check(sceNpPartyGetStateAsUserA(-1, &states[1]) == kInvalidArgument && states[1] == 0xa5a5, "user -1 was not refused with INVALID_ARGUMENT")
|
|
&& check(sceNpPartyGetState(nullptr) == kInvalidArgument, "a null state was not refused with INVALID_ARGUMENT")
|
|
&& check(sceNpPartyGetState(&states[1]) == 0 && states[1] == 2, "the state is not NOT_IN_PARTY")
|
|
&& check(states[0] == 0xa5a5 && states[2] == 0xa5a5, "sceNpPartyGetState wrote past its 16-bit state")
|
|
&& check(sceNpPartyGetStateAsUserA(1, nullptr) == kInvalidArgument, "a null user state was not refused with INVALID_ARGUMENT")
|
|
&& check(sceNpPartyGetStateAsUserA(1, &states[0]) == 0 && states[0] == 2, "the user state is not NOT_IN_PARTY")
|
|
&& check(states[2] == 0xa5a5, "sceNpPartyGetStateAsUserA wrote past its 16-bit state");
|
|
}
|
|
|
|
bool lifecycleTests() {
|
|
std::uint64_t param[4] = {};
|
|
std::uint64_t handlers[8] = {};
|
|
handlers[1] = 1;
|
|
return check(sceNpPartyTerminate() == kNotInitialized, "terminate before initialize did not fail")
|
|
&& check(sceNpPartyCheckCallback() == kNotInitialized, "check callback before initialize did not fail")
|
|
&& check(sceNpPartyRegisterHandlerA(handlers, nullptr) == kNotInitialized, "register handler before initialize did not fail")
|
|
&& check(sceNpPartyRegisterHandler(handlers, nullptr) == kNotInitialized, "the plain register handler before initialize did not fail")
|
|
&& check(sceNpPartyInitialize(nullptr) == kInvalidArgument, "a null initialize param was not refused")
|
|
&& check(sceNpPartyInitialize(param) == 0, "initialize failed")
|
|
&& check(sceNpPartyInitialize(param) == kAlreadyInitialized, "a second initialize did not fail")
|
|
&& check(sceNpPartyRegisterHandlerA(nullptr, nullptr) == kInvalidArgument, "null handlers were not refused")
|
|
&& check(sceNpPartyRegisterPrivateHandler(handlers) == kHandlerState, "a private handler without a public handler was accepted")
|
|
&& check(sceNpPartyUnregisterPrivateHandler() == kHandlerState, "unregister without a public handler did not fail")
|
|
&& check(sceNpPartyRegisterHandlerA(handlers, param) == 0, "register handler failed")
|
|
&& check(sceNpPartyRegisterHandler(nullptr, nullptr) == kInvalidArgument, "null plain handlers were not refused")
|
|
&& check(sceNpPartyRegisterHandler(handlers, param) == 0, "the plain register handler failed")
|
|
&& check(sceNpPartyRegisterHandlerA(handlers, param) == kHandlerState, "a second A registration did not fail")
|
|
&& check(sceNpPartyRegisterPrivateHandler(handlers) == 0, "register private handler failed")
|
|
&& check(sceNpPartyUnregisterPrivateHandler() == 0, "unregister private handler failed")
|
|
&& check(sceNpPartyUnregisterPrivateHandler() == kHandlerState, "a second unregister did not fail")
|
|
&& check(sceNpPartyRegisterPrivateHandler(nullptr) == 0, "a null private handler was not accepted")
|
|
&& check(sceNpPartyUnregisterPrivateHandler() == kHandlerState, "a null private handler was not treated as absent")
|
|
&& check(sceNpPartyRegisterPrivateHandler(handlers) == 0, "a non-null private handler was not accepted")
|
|
&& check(sceNpPartyUnregisterPrivateHandler() == 0, "unregister of a non-null private handler failed")
|
|
&& check(sceNpPartyCheckCallback() == 0, "check callback failed")
|
|
&& check(sceNpPartyRegisterPrivateHandler(handlers) == 0, "private handler before terminate failed")
|
|
&& check(sceNpPartyTerminate() == 0, "terminate failed")
|
|
&& check(sceNpPartyTerminate() == kNotInitialized, "a second terminate did not fail")
|
|
&& check(sceNpPartyInitialize(param) == 0, "initialize after terminate failed")
|
|
&& check(sceNpPartyRegisterPrivateHandler(handlers) == kHandlerState, "a private handler survived reinitialize")
|
|
&& check(sceNpPartyUnregisterPrivateHandler() == kHandlerState, "private handler state survived reinitialize")
|
|
&& check(sceNpPartyRegisterHandlerA(handlers, param) == 0, "a fresh A registration after reinitialize failed")
|
|
&& check(sceNpPartyRegisterPrivateHandler(handlers) == 0, "private handler after a fresh registration failed")
|
|
&& check(sceNpPartyTerminate() == 0, "terminate after reinitialize failed");
|
|
}
|
|
|
|
bool handlerContentionTests() {
|
|
std::uint64_t param[4] = {};
|
|
std::uint64_t handlers[8] = {};
|
|
handlers[1] = 1;
|
|
bool ok = true;
|
|
for (int round = 0; round < 16; ++round) {
|
|
ok = check(sceNpPartyInitialize(param) == 0, "contention initialize failed") && ok;
|
|
std::atomic<bool> go{false};
|
|
std::atomic<int> succeeded{0};
|
|
std::atomic<int> rejected{0};
|
|
std::vector<std::thread> threads;
|
|
for (int i = 0; i < 8; ++i) {
|
|
threads.emplace_back([&] {
|
|
while (!go.load()) std::this_thread::yield();
|
|
const int result = sceNpPartyRegisterHandlerA(handlers, nullptr);
|
|
if (result == 0) ++succeeded;
|
|
if (result == kHandlerState) ++rejected;
|
|
});
|
|
}
|
|
go.store(true);
|
|
for (auto& thread : threads) thread.join();
|
|
ok = check(succeeded.load() == 1 && rejected.load() == 7, "only one concurrent A registration must succeed") && ok;
|
|
ok = check(sceNpPartyRegisterPrivateHandler(handlers) == 0, "private handler before unregister contention failed") && ok;
|
|
succeeded.store(0);
|
|
rejected.store(0);
|
|
go.store(false);
|
|
threads.clear();
|
|
for (int i = 0; i < 8; ++i) {
|
|
threads.emplace_back([&] {
|
|
while (!go.load()) std::this_thread::yield();
|
|
const int result = sceNpPartyUnregisterPrivateHandler();
|
|
if (result == 0) ++succeeded;
|
|
if (result == kHandlerState) ++rejected;
|
|
});
|
|
}
|
|
go.store(true);
|
|
for (auto& thread : threads) thread.join();
|
|
ok = check(succeeded.load() == 1 && rejected.load() == 7, "only one concurrent private unregister must succeed") && ok;
|
|
ok = check(sceNpPartyTerminate() == 0, "contention terminate failed") && ok;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool priorityTests() {
|
|
std::uint32_t priority = 0xa5a5a5a5;
|
|
return check(sceNpPartyGetVoiceChatPriority(nullptr) == kInvalidArgument, "a null priority was not refused")
|
|
&& check(sceNpPartyGetVoiceChatPriority(&priority) == kNotInParty && priority == 0xa5a5a5a5, "get priority did not report NOT_IN_PARTY without writing")
|
|
&& check(sceNpPartySetVoiceChatPriority(0) == kInvalidArgument, "priority 0 was not refused")
|
|
&& check(sceNpPartySetVoiceChatPriority(3) == kInvalidArgument, "priority 3 was not refused")
|
|
&& check(sceNpPartySetVoiceChatPriority(-1) == kInvalidArgument, "priority -1 was not refused")
|
|
&& check(sceNpPartySetVoiceChatPriority(1) == kNotInParty, "priority 1 did not report NOT_IN_PARTY")
|
|
&& check(sceNpPartySetVoiceChatPriority(2) == kNotInParty, "priority 2 did not report NOT_IN_PARTY");
|
|
}
|
|
|
|
bool notInPartyTests() {
|
|
std::uint8_t out[64];
|
|
std::memset(out, 0xa5, sizeof(out));
|
|
bool ok = check(sceNpPartyGetId(nullptr) == kInvalidArgument, "a null party id was not refused")
|
|
&& check(sceNpPartyGetId(out) == kNotInParty, "get id did not report NOT_IN_PARTY")
|
|
&& check(sceNpPartyGetMembersA(nullptr) == kInvalidArgument, "a null member list was not refused")
|
|
&& check(sceNpPartyGetMembersA(out) == kNotInParty, "get members did not report NOT_IN_PARTY")
|
|
&& check(sceNpPartyGetMemberInfoA(1, nullptr) == kInvalidArgument, "a null member info was not refused")
|
|
&& check(sceNpPartyGetMemberInfoA(1, out) == kNotInParty, "get member info did not report NOT_IN_PARTY")
|
|
&& check(sceNpPartyGetMembers(nullptr) == kInvalidArgument, "a null plain member list was not refused")
|
|
&& check(sceNpPartyGetMembers(out) == kNotInParty, "the plain get members did not report NOT_IN_PARTY")
|
|
&& check(sceNpPartyGetMemberInfo(1, nullptr) == kInvalidArgument, "a null plain member info was not refused")
|
|
&& check(sceNpPartyGetMemberInfo(1, out) == kNotInParty, "the plain get member info did not report NOT_IN_PARTY")
|
|
&& check(sceNpPartyGetMemberSessionInfo(1, nullptr) == kInvalidArgument, "a null session info was not refused")
|
|
&& check(sceNpPartyGetMemberSessionInfo(1, out) == kNotInParty, "get session info did not report NOT_IN_PARTY")
|
|
&& check(sceNpPartyGetMemberVoiceInfo(1, 2, nullptr) == kInvalidArgument, "a null voice state was not refused")
|
|
&& check(sceNpPartyGetMemberVoiceInfo(1, 2, out) == kNotInParty, "get voice info did not report NOT_IN_PARTY")
|
|
&& check(sceNpPartySendBinaryMessage(nullptr) == kInvalidArgument, "null send params were not refused")
|
|
&& check(sceNpPartySendBinaryMessage(out) == kNotInParty, "send did not report NOT_IN_PARTY");
|
|
for (std::uint8_t byte : out) ok = ok && check(byte == 0xa5, "a NOT_IN_PARTY call wrote its output");
|
|
return ok;
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
std::uint64_t param[4] = {};
|
|
if (!uninitializedTests() || !lifecycleTests() || !handlerContentionTests()) return 1;
|
|
if (!check(sceNpPartyInitialize(param) == 0, "initialize for the state checks failed")) return 1;
|
|
if (!stateTests() || !priorityTests() || !notInPartyTests()) return 1;
|
|
if (!check(sceNpPartyTerminate() == 0, "terminate after the state checks failed") || !uninitializedTests()) return 1;
|
|
std::puts("NpParty tests passed");
|
|
return 0;
|
|
}
|