#include "prx/libSceCompanionHttpd/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include namespace { constexpr int InvalidParam = static_cast(0x80E40004u); constexpr int NotInitialized = static_cast(0x80E40006u); constexpr int AlreadyInitialized = static_cast(0x80E40007u); constexpr int NoEvent = static_cast(0x80E40008u); constexpr int AlreadyStarted = static_cast(0x80E4000Au); constexpr int NotStarted = static_cast(0x80E4000Bu); constexpr int AlreadyRegistered = static_cast(0x80E4000Cu); void Require(bool condition, const char* message) { if (!condition) { std::fprintf(stderr, "CompanionHttpd: %s\n", message); std::abort(); } } template void RequireNotImplemented(TCall call, const std::string& name) { try { call(); } catch (const std::runtime_error& error) { Require(std::string(error.what()) == name + " not implemented", "a companion function threw another error"); return; } Require(false, "a companion function that should throw returned"); } int APS5_VABI RequestCallback(int, int, const void*, void*, void*) { std::fprintf(stderr, "CompanionHttpd: the request callback was called\n"); std::abort(); } void CheckOptionBlock() { unsigned char block[0x70]; std::memset(block, 0x5a, sizeof(block)); Require(sceCompanionHttpdOptParamInitialize(nullptr) == InvalidParam, "a null option block must be rejected"); Require(sceCompanionHttpdOptParamInitialize(reinterpret_cast(block)) == 0, "sceCompanionHttpdOptParamInitialize failed"); for (std::size_t index = 0; index < sizeof(block); ++index) { Require(block[index] == (index < 0x60 ? 0u : 0x5au), "the option block must be zeroed over its 0x60 bytes and nothing past them"); } } void CheckStates() { unsigned char param[0x60]{}; unsigned char event[0x104]; std::memset(event, 0x5a, sizeof(event)); unsigned char untouched[0x104]; std::memset(untouched, 0x5a, sizeof(untouched)); auto* callback = reinterpret_cast(&RequestCallback); Require(sceCompanionHttpdGetEvent(nullptr) == InvalidParam, "a null event before initialization"); Require(sceCompanionHttpdGetEvent(event) == NotInitialized, "an event before initialization"); Require(sceCompanionHttpdStart() == NotInitialized, "a start before initialization"); Require(sceCompanionHttpdStop() == NotInitialized, "a stop before initialization"); Require(sceCompanionHttpdTerminate() == NotInitialized, "a termination before initialization"); Require(sceCompanionHttpdRegisterRequestCallback(nullptr, nullptr) == InvalidParam, "a null callback before initialization"); Require(sceCompanionHttpdRegisterRequestCallback(reinterpret_cast(callback), nullptr) == NotInitialized, "a callback before initialization"); Require(sceCompanionHttpdUnregisterRequestCallback() == NotInitialized, "an unregistration before initialization"); Require(sceCompanionHttpdInitialize(nullptr) == InvalidParam, "a null parameter must be rejected"); Require(sceCompanionHttpdInitialize(reinterpret_cast(param)) == 0, "sceCompanionHttpdInitialize failed"); Require(sceCompanionHttpdInitialize(reinterpret_cast(param)) == AlreadyInitialized, "a second initialization"); Require(sceCompanionHttpdRegisterRequestCallback(nullptr, nullptr) == InvalidParam, "a null callback must be rejected"); Require(sceCompanionHttpdRegisterRequestCallback(reinterpret_cast(callback), param) == 0, "registering the request callback failed"); Require(sceCompanionHttpdRegisterRequestCallback(reinterpret_cast(callback), param) == AlreadyRegistered, "a second callback"); Require(sceCompanionHttpdGetEvent(nullptr) == InvalidParam, "a null event must be rejected"); Require(sceCompanionHttpdGetEvent(event) == NoEvent, "no companion device connects before the start"); Require(sceCompanionHttpdStop() == NotStarted, "a stop before the start"); Require(sceCompanionHttpdStart() == 0, "sceCompanionHttpdStart failed"); Require(sceCompanionHttpdStart() == AlreadyStarted, "a second start"); Require(sceCompanionHttpdGetEvent(event) == NoEvent, "no companion device connects after the start"); Require(std::memcmp(event, untouched, sizeof(event)) == 0, "sceCompanionHttpdGetEvent wrote the event"); RequireNotImplemented([] { sceCompanionHttpdTerminate(); }, "sceCompanionHttpdTerminate"); Require(sceCompanionHttpdStop() == 0, "sceCompanionHttpdStop failed"); Require(sceCompanionHttpdUnregisterRequestCallback() == 0, "unregistering the request callback failed"); RequireNotImplemented([] { sceCompanionHttpdUnregisterRequestCallback(); }, "sceCompanionHttpdUnregisterRequestCallback"); Require(sceCompanionHttpdTerminate() == 0, "sceCompanionHttpdTerminate failed"); Require(sceCompanionHttpdTerminate() == NotInitialized, "a second termination"); Require(sceCompanionHttpdInitialize(reinterpret_cast(param)) == 0, "initializing again after the termination failed"); Require(sceCompanionHttpdRegisterRequestCallback(reinterpret_cast(callback), nullptr) == 0, "the termination kept the callback"); Require(sceCompanionHttpdTerminate() == 0, "the last termination failed"); } void CheckResponseBuilders() { unsigned char response[0x20]{}; RequireNotImplemented([&] { sceCompanionHttpdAddHeader("key", "value", response); }, "sceCompanionHttpdAddHeader"); RequireNotImplemented([&] { sceCompanionHttpdSetStatus(200, response); }, "sceCompanionHttpdSetStatus"); RequireNotImplemented([&] { sceCompanionHttpdSetBody("body", 4, response); }, "sceCompanionHttpdSetBody"); RequireNotImplemented([] { sceCompanionHttpdGet2ndScreenStatus(); }, "sceCompanionHttpdGet2ndScreenStatus"); RequireNotImplemented([] { sceCompanionHttpdGetUserId(); }, "sceCompanionHttpdGetUserId"); RequireNotImplemented([] { sceCompanionHttpdInitialize2(); }, "sceCompanionHttpdInitialize2"); RequireNotImplemented([] { sceCompanionHttpdRegisterRequestBodyReceptionCallback(); }, "sceCompanionHttpdRegisterRequestBodyReceptionCallback"); RequireNotImplemented([] { sceCompanionHttpdRegisterRequestCallback2(); }, "sceCompanionHttpdRegisterRequestCallback2"); RequireNotImplemented([] { sceCompanionHttpdUnregisterRequestBodyReceptionCallback(); }, "sceCompanionHttpdUnregisterRequestBodyReceptionCallback"); } } int main() { CheckOptionBlock(); CheckStates(); CheckResponseBuilders(); return 0; }