mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
Merge pull request #10614 from mrfelek/fix/gameupdate-no-update
fix(libSceGameUpdate): answer no update instead of throwing on request creation
This commit is contained in:
3 files changed
+134
-15
No files matched your search
@@ -1,7 +1,9 @@
|
||||
#include "prx/libSceGameUpdate/include/Api.hpp"
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
@@ -11,20 +13,58 @@ static constexpr int SCE_GAME_UPDATE_ERROR_ALREADY_INITIALIZED = static_cast<int
|
||||
static constexpr int SCE_GAME_UPDATE_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80412803);
|
||||
static constexpr int SCE_GAME_UPDATE_ERROR_INVALID_SIZE = static_cast<int>(0x80412804);
|
||||
static constexpr int SCE_GAME_UPDATE_ERROR_REQUEST_NOT_FOUND = static_cast<int>(0x80412805);
|
||||
static constexpr int SCE_GAME_UPDATE_ERROR_TOO_MANY_REQUESTS = static_cast<int>(0x80412806);
|
||||
static constexpr int SCE_GAME_UPDATE_ERROR_ABORTED = static_cast<int>(0x80412807);
|
||||
static constexpr int FIRST_REQUEST_ID = 0x20000000;
|
||||
static constexpr int LAST_REQUEST_ID = 0x2FFFFFFF;
|
||||
|
||||
static_assert(sizeof(GameUpdateCheckParam) == 0x30);
|
||||
static_assert(sizeof(GameUpdateCheckResult) == 0x30);
|
||||
|
||||
static std::atomic<bool> initialized{false};
|
||||
struct Request {
|
||||
int id;
|
||||
bool aborted;
|
||||
};
|
||||
|
||||
static int checkRequestId(int requestId) {
|
||||
static std::mutex stateMutex;
|
||||
static bool initialized = false;
|
||||
static int nextRequestId = FIRST_REQUEST_ID;
|
||||
static std::array<Request, 8> requests{};
|
||||
|
||||
static Request* findRequest(int requestId) {
|
||||
for (Request& request : requests) {
|
||||
if (request.id != 0 && request.id == requestId) {
|
||||
return &request;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int followingRequestId(int requestId) {
|
||||
return requestId == LAST_REQUEST_ID ? FIRST_REQUEST_ID : requestId + 1;
|
||||
}
|
||||
|
||||
static int takeRequestId() {
|
||||
int requestId = nextRequestId;
|
||||
while (findRequest(requestId) != nullptr) {
|
||||
requestId = followingRequestId(requestId);
|
||||
}
|
||||
nextRequestId = followingRequestId(requestId);
|
||||
return requestId;
|
||||
}
|
||||
|
||||
static int checkRequestId(int requestId, Request** request) {
|
||||
if (requestId <= 0) {
|
||||
return SCE_GAME_UPDATE_ERROR_REQUEST_NOT_FOUND;
|
||||
}
|
||||
if (!initialized.load()) {
|
||||
if (!initialized) {
|
||||
return SCE_GAME_UPDATE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
return SCE_GAME_UPDATE_ERROR_REQUEST_NOT_FOUND;
|
||||
*request = findRequest(requestId);
|
||||
if (*request == nullptr) {
|
||||
return SCE_GAME_UPDATE_ERROR_REQUEST_NOT_FOUND;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool hasNonZeroReserved(const GameUpdateCheckParam* param) {
|
||||
@@ -39,7 +79,13 @@ static bool hasNonZeroReserved(const GameUpdateCheckParam* param) {
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceGameUpdateAbortRequest(int requestId) {
|
||||
return checkRequestId(requestId);
|
||||
std::lock_guard lock(stateMutex);
|
||||
Request* request = nullptr;
|
||||
if (int error = checkRequestId(requestId, &request); error != 0) {
|
||||
return error;
|
||||
}
|
||||
request->aborted = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceGameUpdateCheck(int requestId, const GameUpdateCheckParam* param, GameUpdateCheckResult* result) {
|
||||
@@ -55,18 +101,43 @@ int APS5_VABI sceGameUpdateCheck(int requestId, const GameUpdateCheckParam* para
|
||||
if (param->option != 0 || hasNonZeroReserved(param)) {
|
||||
return SCE_GAME_UPDATE_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
return checkRequestId(requestId);
|
||||
std::lock_guard lock(stateMutex);
|
||||
Request* request = nullptr;
|
||||
if (int error = checkRequestId(requestId, &request); error != 0) {
|
||||
return error;
|
||||
}
|
||||
if (request->aborted) {
|
||||
return SCE_GAME_UPDATE_ERROR_ABORTED;
|
||||
}
|
||||
result->found = false;
|
||||
result->addcont_found = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceGameUpdateCreateRequest(void) {
|
||||
if (!initialized.load()) {
|
||||
std::lock_guard lock(stateMutex);
|
||||
if (!initialized) {
|
||||
return SCE_GAME_UPDATE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
throw std::runtime_error("sceGameUpdateCreateRequest: patch service request creation is not implemented");
|
||||
for (Request& request : requests) {
|
||||
if (request.id != 0) {
|
||||
continue;
|
||||
}
|
||||
const int requestId = takeRequestId();
|
||||
request = {requestId, false};
|
||||
return requestId;
|
||||
}
|
||||
return SCE_GAME_UPDATE_ERROR_TOO_MANY_REQUESTS;
|
||||
}
|
||||
|
||||
int APS5_VABI sceGameUpdateDeleteRequest(int requestId) {
|
||||
return checkRequestId(requestId);
|
||||
std::lock_guard lock(stateMutex);
|
||||
Request* request = nullptr;
|
||||
if (int error = checkRequestId(requestId, &request); error != 0) {
|
||||
return error;
|
||||
}
|
||||
*request = {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceGameUpdateGetAddcontLatestVersion(uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel, GameUpdateAddcontVersionInfo* info) {
|
||||
@@ -77,23 +148,31 @@ int APS5_VABI sceGameUpdateGetAddcontLatestVersion(uint32_t serviceLabel, const
|
||||
if (entitlementLabel == nullptr) {
|
||||
return SCE_GAME_UPDATE_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (!initialized.load()) {
|
||||
return SCE_GAME_UPDATE_ERROR_NOT_INITIALIZED;
|
||||
{
|
||||
std::lock_guard lock(stateMutex);
|
||||
if (!initialized) {
|
||||
return SCE_GAME_UPDATE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("sceGameUpdateGetAddcontLatestVersion: entitlement version lookup is not implemented");
|
||||
}
|
||||
|
||||
int APS5_VABI sceGameUpdateInitialize(void) {
|
||||
if (initialized.exchange(true)) {
|
||||
std::lock_guard lock(stateMutex);
|
||||
if (initialized) {
|
||||
return SCE_GAME_UPDATE_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceGameUpdateTerminate(void) {
|
||||
if (!initialized.exchange(false)) {
|
||||
std::lock_guard lock(stateMutex);
|
||||
if (!initialized) {
|
||||
return SCE_GAME_UPDATE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
initialized = false;
|
||||
requests = {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,42 @@ static void TestRequestArguments(int notInitialized, int invalidArgument, int in
|
||||
Require(sceGameUpdateTerminate() == 0);
|
||||
}
|
||||
|
||||
static void TestRequestLifecycle(int notFound) {
|
||||
constexpr int tooManyRequests = static_cast<int>(0x80412806);
|
||||
constexpr int aborted = static_cast<int>(0x80412807);
|
||||
GameUpdateCheckParam param{};
|
||||
param.size = sizeof(param);
|
||||
GameUpdateCheckResult result{};
|
||||
std::memset(&result, 0xAB, sizeof(result));
|
||||
result.size = sizeof(result);
|
||||
const auto original = result;
|
||||
|
||||
int ids[8];
|
||||
for (int& id : ids) {
|
||||
id = sceGameUpdateCreateRequest();
|
||||
Require(id >= 0x20000000 && id <= 0x2FFFFFFF);
|
||||
}
|
||||
Require(ids[0] != ids[1]);
|
||||
Require(sceGameUpdateCreateRequest() == tooManyRequests);
|
||||
|
||||
Require(sceGameUpdateCheck(ids[0], ¶m, &result) == 0);
|
||||
Require(!result.found && !result.addcont_found);
|
||||
Require(std::memcmp(&result, &original, 8) == 0);
|
||||
Require(std::memcmp(reinterpret_cast<const char*>(&result) + 10, reinterpret_cast<const char*>(&original) + 10, sizeof(result) - 10) == 0);
|
||||
|
||||
Require(sceGameUpdateAbortRequest(ids[1]) == 0);
|
||||
Require(sceGameUpdateCheck(ids[1], ¶m, &result) == aborted);
|
||||
|
||||
Require(sceGameUpdateDeleteRequest(ids[3]) == 0);
|
||||
Require(sceGameUpdateDeleteRequest(ids[3]) == notFound);
|
||||
Require(sceGameUpdateCheck(ids[3], ¶m, &result) == notFound);
|
||||
Require(sceGameUpdateCreateRequest() >= 0x20000000);
|
||||
|
||||
Require(sceGameUpdateTerminate() == 0);
|
||||
Require(sceGameUpdateInitialize() == 0);
|
||||
Require(sceGameUpdateCheck(ids[0], ¶m, &result) == notFound);
|
||||
}
|
||||
|
||||
int main() {
|
||||
constexpr int notInitialized = static_cast<int>(0x80412801);
|
||||
constexpr int invalidArgument = static_cast<int>(0x80412803);
|
||||
@@ -91,7 +127,7 @@ int main() {
|
||||
const auto original = info;
|
||||
RequireThrows([&] { sceGameUpdateGetAddcontLatestVersion(0, &label, &info); });
|
||||
Require(std::memcmp(&info, &original, sizeof(info)) == 0);
|
||||
RequireThrows([] { sceGameUpdateCreateRequest(); });
|
||||
TestRequestLifecycle(notFound);
|
||||
|
||||
Require(sceGameUpdateTerminate() == 0);
|
||||
Require(sceGameUpdateTerminate() == notInitialized);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# libSceGameUpdate technical debt
|
||||
|
||||
### Silent stubs
|
||||
|
||||
- [sceGameUpdateCreateRequest, sceGameUpdateCheck](../../../core/libs/prx/libSceGameUpdate/Export.cpp) (libSceGameUpdate) - requests are kept in the module's 8 slots with ids from `0x20000000` (a ninth returns `0x80412806`, an aborted request's Check `0x80412807`), as [prosper](https://github.com/mattias800/prosper) reads the PS5 3.20 `libSceGameUpdate.sprx`. Check never asks a patch service: it always answers no update (`found` and the byte after it cleared, the rest of the result untouched, return 0), which is what the module returns when its service reports `0x80412883`; KytyPS5 also answers no update. What the service returns on an offline console is not measured. The answer only decides whether a title offers a patch. PPSA22408 aborted on the throw in sceGameUpdateCreateRequest at its title screen. Closes when: measured on a PS5
|
||||
|
||||
### Functional
|
||||
|
||||
- [libSceGameUpdate](../../../core/libs/prx/libSceGameUpdate/Export.cpp): initialization tracks host state but does not connect the patch service. Request creation and entitlement version lookup throw instead of fabricating an offline error or a successful no-update result. The original maintains eight service-backed requests with IDs starting at `0x20000000`; the service's offline response is still unknown. Check, Abort and Delete implement argument/state checks but have no live requests to operate on.
|
||||
- [libSceGameUpdate](../../../core/libs/prx/libSceGameUpdate/Export.cpp): initialization tracks host state but does not connect the patch service. Entitlement version lookup throws.
|
||||
- [sceGameUpdateCheck](../../../core/libs/prx/libSceGameUpdate/Export.cpp): option zero is supported for argument validation. The original additionally permits option one when a kernel platform predicate succeeds; that platform mode is not modeled.
|
||||
- [sceGameUpdateGetAddcontLatestVersion](../../../core/libs/prx/libSceGameUpdate/Export.cpp): the supplied module confirms a 20-byte entitlement-label input and a 48-byte service output. Null pointers are rejected before initialization is checked. There is no local output-size validation or blanket clearing of the output; the host leaves it untouched when the missing service causes an exception.
|
||||
Reference in new issue
Block a user