AnyPS5/core/libs/tests/GuestOfflineImports.cpp
screamm 5690b84a49 feat(libSceNpEntitlementAccess): implement sceNpEntitlementAccessGetGameTrialsFlag
PRAGMATA (PPSA02530) calls it after sceRazorCpuSync and stops on main
with "sceNpEntitlementAccessGetGameTrialsFlag not implemented". From
called once; I logged four register values but cannot tell which are
arguments. Answering 0 without writing anything carried on." Killing
Floor 3 (PPSA08427) and RE4 also import it.

The title is reported as a full game, not a game trial: 0 is written to
the uint32_t output and 0 returned; a null pointer returns 0x817D0002.
Signature, value, one-word write and error follow sharpemu
src/SharpEmu.Libs/Np/NpEntitlementAccessExports.cs:98-116 and its test
NpEntitlementAccessExportsTests.cs:16-36 (a2983692930564815a77f81e74c151268f5b935f),
used as a source of facts; 0x817D0002 is also the parameter error of
KytyPS5, prosper and sceNpEntitlementAccessGetEntitlementKey here. The
library has no initialization state, so none is checked. The NID
y7JD1gfwyfA hashes from the name and is in aerolib. Unverified points
are in TechnicalDebt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 21:58:36 +02:00

100 lines
4.7 KiB
C++

#include "SceTypes.hpp"
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <string_view>
extern "C" {
int APS5_VABI sceHttpSetCookieEnabled(int, int);
int APS5_VABI sceHttpSendRequest(int, const void*, std::size_t);
int APS5_VABI sceNpEntitlementAccessGetEntitlementKey(
std::uint32_t, const NpUnifiedEntitlementLabel*, NpEntitlementAccessEntitlementKey*);
int APS5_VABI sceNpEntitlementAccessGetGameTrialsFlag(std::uint32_t*);
int APS5_VABI sceNpEntitlementAccessRequestUnifiedEntitlementInfoList();
int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfoList();
int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfoList();
int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfoList();
int APS5_VABI sceNpEntitlementAccessRequestUnifiedEntitlementInfo();
int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfo();
int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfo();
int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfo();
int APS5_VABI sceRudpInit_nid_postfix(void*, int);
int APS5_VABI sceRudpActivate();
int APS5_VABI sceRudpGetStatus(void*, std::size_t);
int APS5_VABI sceRudpTerminate();
}
static void Require(bool value) { if (!value) std::abort(); }
int main() {
constexpr int invalidValue = static_cast<int>(0x804311FE);
constexpr int network = static_cast<int>(0x80431063);
Require(sceHttpSetCookieEnabled(1, 0) == 0);
Require(sceHttpSendRequest(1, nullptr, 0) == network);
bool cookieUnsupported = false;
try {
sceHttpSetCookieEnabled(1, 1);
} catch (const std::runtime_error& error) {
cookieUnsupported = std::string_view(error.what()) == "sceHttpSetCookieEnabled not implemented";
}
Require(cookieUnsupported);
for (int enabled : {-1, 2, 0x100}) {
Require(sceHttpSetCookieEnabled(1, enabled) == invalidValue);
}
constexpr int parameter = static_cast<int>(0x817D0002);
constexpr int noEntitlement = static_cast<int>(0x817D0007);
static_assert(sizeof(NpEntitlementAccessEntitlementKey) == 16);
NpUnifiedEntitlementLabel label{};
std::memcpy(label.data, "unowned-addon", 14);
struct KeyBuffer {
std::uint64_t before;
NpEntitlementAccessEntitlementKey key;
std::uint64_t after;
} output;
std::memset(&output, 0xa5, sizeof(output));
std::array<unsigned char, sizeof(output)> original{};
std::memcpy(original.data(), &output, sizeof(output));
Require(sceNpEntitlementAccessGetEntitlementKey(0, nullptr, &output.key) == parameter);
Require(sceNpEntitlementAccessGetEntitlementKey(0, &label, nullptr) == parameter);
Require(sceNpEntitlementAccessGetEntitlementKey(0, nullptr, nullptr) == parameter);
for (std::uint32_t serviceLabel : {0u, 1u, 0xffffffffu}) {
Require(sceNpEntitlementAccessGetEntitlementKey(serviceLabel, &label, &output.key) == noEntitlement);
Require(std::memcmp(&output, original.data(), sizeof(output)) == 0);
}
std::array<std::uint32_t, 2> trialsFlag{0xa5a5a5a5u, 0x5a5a5a5au};
Require(sceNpEntitlementAccessGetGameTrialsFlag(trialsFlag.data()) == 0);
Require(trialsFlag[0] == 0 && trialsFlag[1] == 0x5a5a5a5au);
Require(sceNpEntitlementAccessGetGameTrialsFlag(nullptr) == parameter);
constexpr int signedOut = static_cast<int>(0x80550006);
Require(sceNpEntitlementAccessRequestUnifiedEntitlementInfoList() == signedOut);
Require(sceNpEntitlementAccessPollUnifiedEntitlementInfoList() == signedOut);
Require(sceNpEntitlementAccessRequestServiceEntitlementInfoList() == signedOut);
Require(sceNpEntitlementAccessPollServiceEntitlementInfoList() == signedOut);
Require(sceNpEntitlementAccessRequestUnifiedEntitlementInfo() == signedOut);
Require(sceNpEntitlementAccessPollUnifiedEntitlementInfo() == signedOut);
Require(sceNpEntitlementAccessRequestServiceEntitlementInfo() == signedOut);
Require(sceNpEntitlementAccessPollServiceEntitlementInfo() == signedOut);
Require(sceRudpActivate() == 0);
std::array<unsigned char, 248> status;
status.fill(0x5a);
const auto originalStatus = status;
constexpr int rudpNotInitialized = static_cast<int>(0x80770001);
Require(sceRudpGetStatus(status.data(), status.size()) == rudpNotInitialized);
Require(status == originalStatus);
alignas(8) static unsigned char rudpPool[0xC58];
Require(sceRudpInit_nid_postfix(rudpPool, sizeof(rudpPool)) == 0);
Require(sceRudpActivate() == 0);
Require(sceRudpGetStatus(status.data(), status.size()) == 0);
for (unsigned char byte : status) Require(byte == 0);
Require(sceRudpGetStatus(nullptr, 0) == static_cast<int>(0x80770004));
Require(sceRudpTerminate() == 0);
return 0;
}