diff --git a/src/graphics/presentation/videoOut.cpp b/src/graphics/presentation/videoOut.cpp index 11e989e43..22f00feb4 100644 --- a/src/graphics/presentation/videoOut.cpp +++ b/src/graphics/presentation/videoOut.cpp @@ -145,6 +145,7 @@ struct VideoOutOutputStatus { uint64_t flags = 0; uint64_t reserved[3] = {}; }; +static_assert(sizeof(VideoOutOutputStatus) == 0x30); // Also used by VideoOutVrrStatus. struct VideoOutOutputOptions { uint32_t internalData[16] = {}; diff --git a/src/libs/libNet.cpp b/src/libs/libNet.cpp index f1aff5fc3..1667ec6e6 100644 --- a/src/libs/libNet.cpp +++ b/src/libs/libNet.cpp @@ -18,6 +18,9 @@ namespace Libs { +namespace LibNpEntitlementAccess { +void InitNet_1_NpEntitlementAccess(Loader::SymbolDatabase* s); +} namespace LibNpTrophy2 { void InitNet_1_NpTrophy2(Loader::SymbolDatabase* s); } @@ -1663,152 +1666,6 @@ LIB_DEFINE(InitNet_1_NpSessionSignaling) { } // namespace LibNpSessionSignaling -namespace LibNpEntitlementAccess { - -LIB_VERSION("NpEntitlementAccess", 1, "NpEntitlementAccess", 1, 1); - -constexpr int NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER = -2122514430; /* 0x817D0002 */ -constexpr int NP_ENTITLEMENT_ACCESS_ERROR_NO_ENTITLEMENT = -2122514425; /* 0x817D0007 */ -constexpr uint32_t NP_ENTITLEMENT_ACCESS_SKU_FLAG_FULL = 3; - -struct NpEntitlementAccessInitParam { - char reserved[32]; -}; - -struct NpEntitlementAccessBootParam { - char reserved[32]; -}; - -struct NpUnifiedEntitlementLabel { - char data[17]; - char padding[3]; -}; - -struct NpEntitlementAccessAddcontEntitlementInfo { - NpUnifiedEntitlementLabel entitlement_label; - uint32_t package_type; - uint32_t download_status; -}; - -struct NpEntitlementAccessEntitlementKey { - uint8_t data[16]; -}; - -static constexpr NpEntitlementAccessAddcontEntitlementInfo NP_ENTITLEMENT_ACCESS_ADDON_LIST[] = { - {{{"85y-je"}, {}}, 3, 4}, // GTA V hash 0xf4315381 - {{{"5d5c48"}, {}}, 3, 4}, // GTA V hash 0x961c34b0 - {{{"_mtqu6"}, {}}, 3, 4}, // GTA V hash 0x9cd1bcad -}; - -static int KYTY_SYSV_ABI NpEntitlementAccessInitialize( - const NpEntitlementAccessInitParam* init_param, NpEntitlementAccessBootParam* boot_param) { - PRINT_NAME(); - - if (init_param == nullptr || boot_param == nullptr) { - return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; - } - - memset(boot_param, 0, sizeof(*boot_param)); - - return 0; -} - -static int KYTY_SYSV_ABI NpEntitlementAccessGetSkuFlag(uint32_t* sku_flag) { - PRINT_NAME(); - - if (sku_flag == nullptr) { - return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; - } - - *sku_flag = NP_ENTITLEMENT_ACCESS_SKU_FLAG_FULL; - return 0; -} - -static int KYTY_SYSV_ABI NpEntitlementAccessGetAddcontEntitlementInfoList( - uint32_t service_label, NpEntitlementAccessAddcontEntitlementInfo* list, uint32_t list_num, - uint32_t* hit_num) { - PRINT_NAME(); - - LOGF("\t service_label = %" PRIu32 "\n", service_label); - LOGF("\t list = 0x%016" PRIx64 "\n", reinterpret_cast(list)); - LOGF("\t list_num = %" PRIu32 "\n", list_num); - LOGF("\t hit_num = 0x%016" PRIx64 "\n", reinterpret_cast(hit_num)); - - if (hit_num == nullptr || (list == nullptr && list_num != 0)) { - return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; - } - - *hit_num = static_cast(sizeof(NP_ENTITLEMENT_ACCESS_ADDON_LIST) / - sizeof(NP_ENTITLEMENT_ACCESS_ADDON_LIST[0])); - - if (list != nullptr && list_num != 0) { - memset(list, 0, sizeof(*list) * list_num); - - const auto copy_num = (list_num < *hit_num ? list_num : *hit_num); - for (uint32_t i = 0; i < copy_num; i++) { - list[i] = NP_ENTITLEMENT_ACCESS_ADDON_LIST[i]; - } - } - - return 0; -} - -static int KYTY_SYSV_ABI NpEntitlementAccessGetAddcontEntitlementInfo( - uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, - NpEntitlementAccessAddcontEntitlementInfo* info) { - PRINT_NAME(); - - LOGF("\t service_label = %" PRIu32 "\n", service_label); - LOGF("\t entitlement_label = 0x%016" PRIx64 "\n", - reinterpret_cast(entitlement_label)); - LOGF("\t info = 0x%016" PRIx64 "\n", reinterpret_cast(info)); - - if (entitlement_label == nullptr || info == nullptr) { - return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; - } - - memset(info, 0, sizeof(*info)); - - for (const auto& entitlement: NP_ENTITLEMENT_ACCESS_ADDON_LIST) { - if (strncmp(entitlement_label->data, entitlement.entitlement_label.data, - sizeof(entitlement_label->data)) == 0) { - *info = entitlement; - return 0; - } - } - - return NP_ENTITLEMENT_ACCESS_ERROR_NO_ENTITLEMENT; -} - -static int KYTY_SYSV_ABI NpEntitlementAccessGetEntitlementKey( - uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, - NpEntitlementAccessEntitlementKey* key) { - PRINT_NAME(); - - LOGF("\t service_label = %" PRIu32 "\n" - "\t entitlement_label = 0x%016" PRIx64 "\n" - "\t key = 0x%016" PRIx64 "\n", - service_label, reinterpret_cast(entitlement_label), - reinterpret_cast(key)); - - if (entitlement_label == nullptr || key == nullptr) { - return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; - } - - return NP_ENTITLEMENT_ACCESS_ERROR_NO_ENTITLEMENT; -} - -LIB_DEFINE(InitNet_1_NpEntitlementAccess) { - LIB_FUNC("jO8DM8oyego", LibNpEntitlementAccess::NpEntitlementAccessInitialize); - LIB_FUNC("lPDO62PpJIA", LibNpEntitlementAccess::NpEntitlementAccessGetSkuFlag); - LIB_FUNC("TFyU+KFBv54", - LibNpEntitlementAccess::NpEntitlementAccessGetAddcontEntitlementInfoList); - LIB_FUNC("xddD23+8TfQ", LibNpEntitlementAccess::NpEntitlementAccessGetAddcontEntitlementInfo); - LIB_FUNC("5LiMEPuW0DQ", LibNpEntitlementAccess::NpEntitlementAccessGetEntitlementKey); -} - -} // namespace LibNpEntitlementAccess - namespace LibNpAuth { LIB_VERSION("NpAuth", 1, "NpAuth", 1, 1); diff --git a/src/libs/libNpEntitlementAccess.cpp b/src/libs/libNpEntitlementAccess.cpp new file mode 100644 index 000000000..ff521daef --- /dev/null +++ b/src/libs/libNpEntitlementAccess.cpp @@ -0,0 +1,494 @@ +#include "common/abi.h" +#include "common/logging/log.h" +#include "libs/libs.h" +#include "loader/symbolDatabase.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Libs { + +namespace LibNpEntitlementAccess { + +LIB_VERSION("NpEntitlementAccess", 1, "NpEntitlementAccess", 1, 1); + +constexpr int NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED = -2122514431; // 0x817D0001 +constexpr int NP_ENTITLEMENT_ACCESS_ERROR_OUT_OF_MEMORY = -2122514416; // 0x817D0010 +constexpr int NP_ENTITLEMENT_ACCESS_ERROR_REQUEST_NOT_FOUND = -2122514411; // 0x817D0015 +constexpr int NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER = -2122514430; /* 0x817D0002 */ +constexpr int NP_ENTITLEMENT_ACCESS_ERROR_NO_ENTITLEMENT = -2122514425; /* 0x817D0007 */ +constexpr uint32_t NP_ENTITLEMENT_ACCESS_SKU_FLAG_FULL = 3; +constexpr uint32_t NP_ENTITLEMENT_ACCESS_GAME_TRIALS_FLAG_OFF = 0; +constexpr int32_t NP_ENTITLEMENT_ACCESS_INVALID_OFFSET = -1; +constexpr int32_t NP_ENTITLEMENT_ACCESS_ENTITLEMENT_INFO_LIST_MAX_SIZE = 100; + +struct NpEntitlementAccessInitParam { + char reserved[32]; +}; + +struct NpEntitlementAccessBootParam { + char reserved[32]; +}; + +struct NpUnifiedEntitlementLabel { + char data[17]; + char padding[3]; +}; + +struct NpEntitlementAccessAddcontEntitlementInfo { + NpUnifiedEntitlementLabel entitlement_label; + uint32_t package_type; + uint32_t download_status; +}; + +struct NpEntitlementAccessEntitlementKey { + uint8_t data[16]; +}; + +struct NpServiceEntitlementLabel { + char data[7]; + char padding[13]; +}; + +struct NpEntitlementAccessTransactionId { + char data[65]; + char padding[7]; +}; + +struct NpEntitlementAccessRequestEntitlementInfoListParam { + size_t size; + uint32_t entitlement_type; + int32_t offset; + int32_t limit; + uint32_t sort; + uint32_t direction; + uint32_t package_type; +}; + +struct NpEntitlementAccessUnifiedEntitlementInfo { + NpUnifiedEntitlementLabel entitlement_label; + uint64_t active_date; + uint64_t inactive_date; + uint32_t entitlement_type; + int32_t use_count; + int32_t use_limit; + uint32_t package_type; + bool active_flag; + int8_t reserved[3]; +}; + +struct NpEntitlementAccessServiceEntitlementInfo { + NpServiceEntitlementLabel entitlement_label; + uint64_t active_date; + uint64_t inactive_date; + uint32_t entitlement_type; + int32_t use_count; + int32_t use_limit; + uint32_t reserved1; + bool active_flag; + bool is_consumable; + int8_t reserved2[2]; +}; + +static_assert(sizeof(NpUnifiedEntitlementLabel) == 20); +static_assert(sizeof(NpServiceEntitlementLabel) == 20); +static_assert(sizeof(NpEntitlementAccessTransactionId) == 72); +static_assert(sizeof(NpEntitlementAccessRequestEntitlementInfoListParam) == 32); +static_assert(sizeof(NpEntitlementAccessUnifiedEntitlementInfo) == 64); +static_assert(offsetof(NpEntitlementAccessUnifiedEntitlementInfo, active_date) == 24); +static_assert(sizeof(NpEntitlementAccessServiceEntitlementInfo) == 64); +static_assert(offsetof(NpEntitlementAccessServiceEntitlementInfo, active_date) == 24); + +enum class EntitlementRequestKind { UnifiedInfo, UnifiedList, ServiceList, ConsumeUnified }; + +struct EntitlementRequest { + EntitlementRequestKind kind; + int32_t result; +}; + +static std::atomic g_np_entitlement_initialized {false}; +static std::mutex g_np_entitlement_request_mutex; +static uint64_t g_np_entitlement_next_request_id = 1; +static uint64_t g_np_entitlement_next_transaction = 1; +static std::map g_np_entitlement_requests; + +static int np_entitlement_create_request(EntitlementRequestKind kind, int32_t result, + int64_t* request_id) { + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + if (request_id == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + std::scoped_lock lock(g_np_entitlement_request_mutex); + if (g_np_entitlement_next_request_id > + static_cast(std::numeric_limits::max())) { + return NP_ENTITLEMENT_ACCESS_ERROR_OUT_OF_MEMORY; + } + const auto id = static_cast(g_np_entitlement_next_request_id); + g_np_entitlement_requests.emplace(id, EntitlementRequest {kind, result}); + ++g_np_entitlement_next_request_id; + *request_id = id; + return 0; +} + +static int np_entitlement_poll_request(int64_t request_id, EntitlementRequestKind kind, + int32_t* result) { + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + if (result == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + std::scoped_lock lock(g_np_entitlement_request_mutex); + const auto it = g_np_entitlement_requests.find(request_id); + if (it == g_np_entitlement_requests.end()) { + return NP_ENTITLEMENT_ACCESS_ERROR_REQUEST_NOT_FOUND; + } + if (it->second.kind != kind) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + *result = it->second.result; + return 0; +} + +static constexpr NpEntitlementAccessAddcontEntitlementInfo NP_ENTITLEMENT_ACCESS_ADDON_LIST[] = { + {{{"85y-je"}, {}}, 3, 4}, // GTA V hash 0xf4315381 + {{{"5d5c48"}, {}}, 3, 4}, // GTA V hash 0x961c34b0 + {{{"_mtqu6"}, {}}, 3, 4}, // GTA V hash 0x9cd1bcad +}; + +static int KYTY_SYSV_ABI NpEntitlementAccessInitialize( + const NpEntitlementAccessInitParam* init_param, NpEntitlementAccessBootParam* boot_param) { + PRINT_NAME(); + + if (init_param == nullptr || boot_param == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + + std::scoped_lock lock(g_np_entitlement_request_mutex); + memset(boot_param, 0, sizeof(*boot_param)); + g_np_entitlement_initialized.store(true); + + return 0; +} + +static int KYTY_SYSV_ABI NpEntitlementAccessGetSkuFlag(uint32_t* sku_flag) { + PRINT_NAME(); + + if (sku_flag == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + + *sku_flag = NP_ENTITLEMENT_ACCESS_SKU_FLAG_FULL; + return 0; +} + +static int KYTY_SYSV_ABI NpEntitlementAccessGetGameTrialsFlag(uint32_t* flag) { + PRINT_NAME(); + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + if (flag == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + *flag = NP_ENTITLEMENT_ACCESS_GAME_TRIALS_FLAG_OFF; + return 0; +} + +static int KYTY_SYSV_ABI +NpEntitlementAccessGenerateTransactionId(NpEntitlementAccessTransactionId* transaction_id) { + PRINT_NAME(); + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + if (transaction_id == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + std::scoped_lock lock(g_np_entitlement_request_mutex); + if (g_np_entitlement_next_transaction == 0) { + return NP_ENTITLEMENT_ACCESS_ERROR_OUT_OF_MEMORY; + } + memset(transaction_id, 0, sizeof(*transaction_id)); + std::snprintf(transaction_id->data, sizeof(transaction_id->data), "6b797479%024" PRIx64, + g_np_entitlement_next_transaction++); + return 0; +} + +static int +np_entitlement_request_list(EntitlementRequestKind kind, const void* list, uint32_t list_num, + const NpEntitlementAccessRequestEntitlementInfoListParam* param, + int64_t* request_id) { + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + if (param == nullptr || request_id == nullptr || (list == nullptr && list_num != 0)) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + if (param->size != sizeof(*param) || param->offset < 0 || param->limit < 1 || + param->limit > NP_ENTITLEMENT_ACCESS_ENTITLEMENT_INFO_LIST_MAX_SIZE || param->sort > 1 || + param->direction > 2) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + const bool unified = kind == EntitlementRequestKind::UnifiedList; + if (param->entitlement_type != (unified ? 2u : 1u) || + (unified + ? (param->package_type != 0 && (param->package_type < 4 || param->package_type > 6)) + : param->package_type != 0)) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + // The offline implementation has no server entitlements. These completed requests + // remain available to their matching poll function until explicitly deleted. + return np_entitlement_create_request(kind, 0, request_id); +} + +static int KYTY_SYSV_ABI NpEntitlementAccessRequestUnifiedEntitlementInfoList( + int32_t user_id, uint32_t service_label, const NpUnifiedEntitlementLabel* list, + uint32_t list_num, const NpEntitlementAccessRequestEntitlementInfoListParam* param, + int64_t* request_id) { + PRINT_NAME(); + LOGF("\t user_id = %" PRId32 ", service_label = %" PRIu32 "\n", user_id, service_label); + return np_entitlement_request_list(EntitlementRequestKind::UnifiedList, list, list_num, param, + request_id); +} + +static int KYTY_SYSV_ABI NpEntitlementAccessRequestServiceEntitlementInfoList( + int32_t user_id, uint32_t service_label, const NpServiceEntitlementLabel* list, + uint32_t list_num, const NpEntitlementAccessRequestEntitlementInfoListParam* param, + int64_t* request_id) { + PRINT_NAME(); + LOGF("\t user_id = %" PRId32 ", service_label = %" PRIu32 "\n", user_id, service_label); + return np_entitlement_request_list(EntitlementRequestKind::ServiceList, list, list_num, param, + request_id); +} + +static int KYTY_SYSV_ABI NpEntitlementAccessRequestUnifiedEntitlementInfo( + int32_t user_id, uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, + int64_t* request_id) { + PRINT_NAME(); + LOGF("\t user_id = %" PRId32 ", service_label = %" PRIu32 "\n", user_id, service_label); + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + if (entitlement_label == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + return np_entitlement_create_request(EntitlementRequestKind::UnifiedInfo, + NP_ENTITLEMENT_ACCESS_ERROR_NO_ENTITLEMENT, request_id); +} + +static int KYTY_SYSV_ABI NpEntitlementAccessRequestConsumeUnifiedEntitlement( + int32_t user_id, uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, + const NpEntitlementAccessTransactionId* transaction_id, int32_t use_count, + int64_t* request_id) { + PRINT_NAME(); + LOGF("\t user_id = %" PRId32 ", service_label = %" PRIu32 ", use_count = %" PRId32 "\n", + user_id, service_label, use_count); + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + if (entitlement_label == nullptr || transaction_id == nullptr || use_count <= 0) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + return np_entitlement_create_request(EntitlementRequestKind::ConsumeUnified, + NP_ENTITLEMENT_ACCESS_ERROR_NO_ENTITLEMENT, request_id); +} + +template +static int np_entitlement_poll_list(int64_t request_id, EntitlementRequestKind kind, + int32_t* result, Info* list, uint32_t list_num, + uint32_t* hit_num, int32_t* next_offset, + int32_t* previous_offset) { + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + if (result == nullptr || hit_num == nullptr || next_offset == nullptr || + previous_offset == nullptr || (list == nullptr && list_num != 0)) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + if (const int ret = np_entitlement_poll_request(request_id, kind, result); ret != 0) { + return ret; + } + *hit_num = 0; + *next_offset = NP_ENTITLEMENT_ACCESS_INVALID_OFFSET; + *previous_offset = NP_ENTITLEMENT_ACCESS_INVALID_OFFSET; + return 0; +} + +static int KYTY_SYSV_ABI NpEntitlementAccessPollUnifiedEntitlementInfoList( + int64_t request_id, int32_t* result, NpEntitlementAccessUnifiedEntitlementInfo* list, + uint32_t list_num, uint32_t* hit_num, int32_t* next_offset, int32_t* previous_offset) { + PRINT_NAME(); + return np_entitlement_poll_list(request_id, EntitlementRequestKind::UnifiedList, result, list, + list_num, hit_num, next_offset, previous_offset); +} + +static int KYTY_SYSV_ABI NpEntitlementAccessPollServiceEntitlementInfoList( + int64_t request_id, int32_t* result, NpEntitlementAccessServiceEntitlementInfo* list, + uint32_t list_num, uint32_t* hit_num, int32_t* next_offset, int32_t* previous_offset) { + PRINT_NAME(); + return np_entitlement_poll_list(request_id, EntitlementRequestKind::ServiceList, result, list, + list_num, hit_num, next_offset, previous_offset); +} + +static int KYTY_SYSV_ABI NpEntitlementAccessPollUnifiedEntitlementInfo( + int64_t request_id, int32_t* result, NpEntitlementAccessUnifiedEntitlementInfo* info) { + PRINT_NAME(); + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + if (info == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + // No info is written when the offline request result is NO_ENTITLEMENT. + return np_entitlement_poll_request(request_id, EntitlementRequestKind::UnifiedInfo, result); +} + +static int KYTY_SYSV_ABI NpEntitlementAccessPollConsumeEntitlement(int64_t request_id, + int32_t* result, + int32_t* use_limit) { + PRINT_NAME(); + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + if (use_limit == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + if (const int ret = + np_entitlement_poll_request(request_id, EntitlementRequestKind::ConsumeUnified, result); + ret != 0) { + return ret; + } + if (*result == 0) { + *use_limit = 0; + } + return 0; +} + +static int KYTY_SYSV_ABI NpEntitlementAccessAbortRequest(int64_t request_id) { + PRINT_NAME(); + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + std::scoped_lock lock(g_np_entitlement_request_mutex); + // Offline requests finish at creation, so there is no running work to abort. + return g_np_entitlement_requests.contains(request_id) + ? 0 + : NP_ENTITLEMENT_ACCESS_ERROR_REQUEST_NOT_FOUND; +} + +static int KYTY_SYSV_ABI NpEntitlementAccessDeleteRequest(int64_t request_id) { + PRINT_NAME(); + if (!g_np_entitlement_initialized.load()) { + return NP_ENTITLEMENT_ACCESS_ERROR_NOT_INITIALIZED; + } + std::scoped_lock lock(g_np_entitlement_request_mutex); + return g_np_entitlement_requests.erase(request_id) != 0 + ? 0 + : NP_ENTITLEMENT_ACCESS_ERROR_REQUEST_NOT_FOUND; +} + +static int KYTY_SYSV_ABI NpEntitlementAccessGetAddcontEntitlementInfoList( + uint32_t service_label, NpEntitlementAccessAddcontEntitlementInfo* list, uint32_t list_num, + uint32_t* hit_num) { + PRINT_NAME(); + + LOGF("\t service_label = %" PRIu32 "\n", service_label); + LOGF("\t list = 0x%016" PRIx64 "\n", reinterpret_cast(list)); + LOGF("\t list_num = %" PRIu32 "\n", list_num); + LOGF("\t hit_num = 0x%016" PRIx64 "\n", reinterpret_cast(hit_num)); + + if (hit_num == nullptr || (list == nullptr && list_num != 0)) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + + *hit_num = static_cast(sizeof(NP_ENTITLEMENT_ACCESS_ADDON_LIST) / + sizeof(NP_ENTITLEMENT_ACCESS_ADDON_LIST[0])); + + if (list != nullptr && list_num != 0) { + memset(list, 0, sizeof(*list) * list_num); + + const auto copy_num = (list_num < *hit_num ? list_num : *hit_num); + for (uint32_t i = 0; i < copy_num; i++) { + list[i] = NP_ENTITLEMENT_ACCESS_ADDON_LIST[i]; + } + } + + return 0; +} + +static int KYTY_SYSV_ABI NpEntitlementAccessGetAddcontEntitlementInfo( + uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, + NpEntitlementAccessAddcontEntitlementInfo* info) { + PRINT_NAME(); + + LOGF("\t service_label = %" PRIu32 "\n", service_label); + LOGF("\t entitlement_label = 0x%016" PRIx64 "\n", + reinterpret_cast(entitlement_label)); + LOGF("\t info = 0x%016" PRIx64 "\n", reinterpret_cast(info)); + + if (entitlement_label == nullptr || info == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + + memset(info, 0, sizeof(*info)); + + for (const auto& entitlement: NP_ENTITLEMENT_ACCESS_ADDON_LIST) { + if (strncmp(entitlement_label->data, entitlement.entitlement_label.data, + sizeof(entitlement_label->data)) == 0) { + *info = entitlement; + return 0; + } + } + + return NP_ENTITLEMENT_ACCESS_ERROR_NO_ENTITLEMENT; +} + +static int KYTY_SYSV_ABI NpEntitlementAccessGetEntitlementKey( + uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, + NpEntitlementAccessEntitlementKey* key) { + PRINT_NAME(); + + LOGF("\t service_label = %" PRIu32 "\n" + "\t entitlement_label = 0x%016" PRIx64 "\n" + "\t key = 0x%016" PRIx64 "\n", + service_label, reinterpret_cast(entitlement_label), + reinterpret_cast(key)); + + if (entitlement_label == nullptr || key == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + + return NP_ENTITLEMENT_ACCESS_ERROR_NO_ENTITLEMENT; +} + +LIB_DEFINE(InitNet_1_NpEntitlementAccess) { + LIB_FUNC("jO8DM8oyego", LibNpEntitlementAccess::NpEntitlementAccessInitialize); + LIB_FUNC("lPDO62PpJIA", LibNpEntitlementAccess::NpEntitlementAccessGetSkuFlag); + LIB_FUNC("TFyU+KFBv54", + LibNpEntitlementAccess::NpEntitlementAccessGetAddcontEntitlementInfoList); + LIB_FUNC("xddD23+8TfQ", LibNpEntitlementAccess::NpEntitlementAccessGetAddcontEntitlementInfo); + LIB_FUNC("5LiMEPuW0DQ", LibNpEntitlementAccess::NpEntitlementAccessGetEntitlementKey); + LIB_FUNC("y7JD1gfwyfA", NpEntitlementAccessGetGameTrialsFlag); + LIB_FUNC("IQtb-TaIjSM", NpEntitlementAccessGenerateTransactionId); + LIB_FUNC("uCZf2L27th8", NpEntitlementAccessRequestUnifiedEntitlementInfoList); + LIB_FUNC("brbRxzr7qyI", NpEntitlementAccessRequestServiceEntitlementInfoList); + LIB_FUNC("jautSRs4OdQ", NpEntitlementAccessRequestUnifiedEntitlementInfo); + LIB_FUNC("eOxGbG3sPb0", NpEntitlementAccessRequestConsumeUnifiedEntitlement); + LIB_FUNC("nAEqawEZG5s", NpEntitlementAccessPollUnifiedEntitlementInfoList); + LIB_FUNC("aFv8qms6XTM", NpEntitlementAccessPollServiceEntitlementInfoList); + LIB_FUNC("M4XlLFnzQaQ", NpEntitlementAccessPollUnifiedEntitlementInfo); + LIB_FUNC("69u+XqsoNd0", NpEntitlementAccessPollConsumeEntitlement); + LIB_FUNC("HFcQl9TMcFQ", NpEntitlementAccessAbortRequest); + LIB_FUNC("Z0eQj8m7XA8", NpEntitlementAccessDeleteRequest); +} + +} // namespace LibNpEntitlementAccess + +} // namespace Libs diff --git a/src/libs/libVideoOut.cpp b/src/libs/libVideoOut.cpp index 25457863a..fe491188d 100644 --- a/src/libs/libVideoOut.cpp +++ b/src/libs/libVideoOut.cpp @@ -53,20 +53,4 @@ LIB_DEFINE(InitVideoOut_1) { } // namespace LibGen5 -namespace LibGen5::VrrStatus { - -LIB_VERSION("VideoOutVrrStatus", 1, "VideoOut", 1, 1); - -static KYTY_SYSV_ABI int VideoOutVrrStatus_kP2L8t3j_aM() { - // The observed guest call passes no arguments. - // Return success for Kyty's fixed-refresh path. - return OK; -} - -LIB_DEFINE(InitVideoOutVrrStatus_1) { - LIB_FUNC("kP2L8t3j-aM", VideoOutVrrStatus_kP2L8t3j_aM); -} - -} // namespace LibGen5::VrrStatus - } // namespace Libs diff --git a/src/libs/libVideoOutVrrStatus.cpp b/src/libs/libVideoOutVrrStatus.cpp new file mode 100644 index 000000000..e1a1b5f8d --- /dev/null +++ b/src/libs/libVideoOutVrrStatus.cpp @@ -0,0 +1,71 @@ +#include "common/abi.h" +#include "graphics/presentation/videoOut.h" +#include "libs/errno.h" +#include "libs/libs.h" +#include "loader/symbolDatabase.h" + +#include +#include + +namespace Libs::LibGen5::VrrStatus { + +LIB_VERSION("VideoOutVrrStatus", 1, "VideoOut", 1, 1); + +static KYTY_SYSV_ABI int VideoOutVrrStatus_kP2L8t3j_aM() { + // The observed guest call passes no arguments. + // Return success for Kyty's fixed-refresh path. + return OK; +} + +static KYTY_SYSV_ABI int VideoOutVrrStatus_LibwuIonIBw(LibKernel::EventQueue::KernelEqueue eq, + int handle, void* udata) { + PRINT_NAME(); + + LOGF("\t eq = 0x%016" PRIx64 "\n", static_cast(eq)); + LOGF("\t handle = %d\n", handle); + LOGF("\t udata = 0x%016" PRIx64 "\n", reinterpret_cast(udata)); + + // Fixed-refresh output has no VRR status changes to signal. + return OK; +} + +static KYTY_SYSV_ABI int VideoOutVrrStatus__q_vbO859Tw(LibKernel::EventQueue::KernelEqueue eq, + int handle) { + PRINT_NAME(); + + LOGF("\t eq = 0x%016" PRIx64 "\n", static_cast(eq)); + LOGF("\t handle = %d\n", handle); + + return OK; +} + +static KYTY_SYSV_ABI int VideoOutVrrStatus_gWT7X8H0bYs(int handle, void* status) { + PRINT_NAME(); + + LOGF("\t handle = %d\n", handle); + LOGF("\t status = 0x%016" PRIx64 "\n", reinterpret_cast(status)); + + if (status == nullptr) { + return VideoOut::VIDEO_OUT_ERROR_INVALID_ADDRESS; + } + + const int result = VideoOut::VideoOutGetOutputStatus( + handle, static_cast(status)); + if (result != OK) { + return result; + } + + // The guest's private status layout extends the 0x30-byte output status + // with an 8-byte VRR range. GetOutputStatus clears flags, including bit 4 (VRR off). + std::memset(static_cast(status) + 0x30, 0, 0x08); + return OK; +} + +LIB_DEFINE(InitVideoOutVrrStatus_1) { + LIB_FUNC("kP2L8t3j-aM", VideoOutVrrStatus_kP2L8t3j_aM); + LIB_FUNC("LibwuIonIBw", VideoOutVrrStatus_LibwuIonIBw); + LIB_FUNC("-q-vbO859Tw", VideoOutVrrStatus__q_vbO859Tw); + LIB_FUNC("gWT7X8H0bYs", VideoOutVrrStatus_gWT7X8H0bYs); +} + +} // namespace Libs::LibGen5::VrrStatus