Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/services/AchievementRuntimeExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,22 @@ class AchievementRuntimeExports : private AchievementRuntime
return rc_client_get_subset_info(pClient, subset_id);
}

static rc_client_subset_list_info_t* create_subset_list()
{
auto* pClient = ra::services::ServiceLocator::Get<ra::context::IRcClient>().GetClient();
GSL_SUPPRESS_TYPE1
auto* list = reinterpret_cast<rc_client_subset_list_info_t*>(
rc_client_create_subset_list(pClient));
list->destroy_func = destroy_subset_list;
return list;
}

static void get_user_subset_summary(uint32_t subset_id, rc_client_user_game_summary_t* summary)
{
const auto* pClient = ra::services::ServiceLocator::Get<ra::context::IRcClient>().GetClient();
rc_client_get_user_subset_summary(pClient, subset_id, summary);
}

static void get_user_game_summary(rc_client_user_game_summary_t* summary)
{
const auto* pClient = ra::services::ServiceLocator::Get<ra::context::IRcClient>().GetClient();
Expand Down Expand Up @@ -419,6 +435,13 @@ class AchievementRuntimeExports : private AchievementRuntime
return rc_client_get_achievement_info(pClient, id);
}

static const rc_client_achievement_t* get_next_achievement_info(uint32_t id, int32_t grouping)
{
auto* pClient = ra::services::ServiceLocator::Get<ra::context::IRcClient>().GetClient();
auto* pAchievement = id ? rc_client_get_achievement_info(pClient, id) : nullptr;
return rc_client_get_next_achievement_info(pClient, pAchievement, grouping);
}

static rc_client_leaderboard_list_info_t* create_leaderboard_list(int grouping)
{
auto* pClient = ra::services::ServiceLocator::Get<ra::context::IRcClient>().GetClient();
Expand Down Expand Up @@ -983,6 +1006,12 @@ class AchievementRuntimeExports : private AchievementRuntime
return 0;
}

static void destroy_subset_list(rc_client_subset_list_info_t* list) noexcept
{
if (list)
free(list);
}

static void destroy_achievement_list(rc_client_achievement_list_info_t* list) noexcept
{
if (list)
Expand Down Expand Up @@ -1245,6 +1274,31 @@ static void GetExternalClientV4(rc_client_external_t* pClientExternal) noexcept
ra::services::AchievementRuntimeExports::set_allow_background_memory_reads;
}

static void GetExternalClientV5(rc_client_external_t* pClientExternal) noexcept
{
// ASSERT: the v5 summary structure is a superset of the v1 summary structures with all
// fields at the same offset, so we can pass pointers to a v5 summary structure to
// the client as either a v5 summary structure or v1 summary structure and the client
// will be able to find the data they're looking for.
pClientExternal->get_user_game_summary_v5 =
ra::services::AchievementRuntimeExports::get_user_game_summary;

pClientExternal->get_user_subset_summary =
ra::services::AchievementRuntimeExports::get_user_subset_summary;
}

static void GetExternalClientV6(rc_client_external_t* pClientExternal) noexcept
{
pClientExternal->create_subset_list =
ra::services::AchievementRuntimeExports::create_subset_list;
}

static void GetExternalClientV7(rc_client_external_t* pClientExternal) noexcept
{
pClientExternal->get_next_achievement_info =
ra::services::AchievementRuntimeExports::get_next_achievement_info;
}

#ifdef __cplusplus
extern "C" {
#endif
Expand All @@ -1257,6 +1311,18 @@ API int CCONV _Rcheevos_GetExternalClient(rc_client_external_t* pClientExternal,
RA_LOG_WARN("Unknown rc_client_external interface version: %s", nVersion);
__fallthrough;

case 7:
GetExternalClientV7(pClientExternal);
__fallthrough;

case 6:
GetExternalClientV6(pClientExternal);
__fallthrough;

case 5:
GetExternalClientV5(pClientExternal);
__fallthrough;

case 4:
GetExternalClientV4(pClientExternal);
__fallthrough;
Expand Down
106 changes: 104 additions & 2 deletions tests/services/AchievementRuntimeExports_Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,14 +315,14 @@ TEST_CLASS(AchievementRuntimeExports_Tests)
}

GSL_SUPPRESS_TYPE4
static void AssertV2Exports(const rc_client_external_t & pClient)
static void AssertV2Exports(const rc_client_external_t & pClient)
{
Assert::IsNotNull((void*)pClient.add_game_hash, L"add_game_hash not set");
Assert::IsNotNull((void*)pClient.load_unknown_game, L"load_unknown_game not set");
}

GSL_SUPPRESS_TYPE4
static void AssertV3Exports(const rc_client_external_t & pClient)
static void AssertV3Exports(const rc_client_external_t & pClient)
{
Assert::IsNotNull((void*)pClient.get_user_info_v3, L"get_user_info_v3 not set");
Assert::IsNotNull((void*)pClient.get_game_info_v3, L"get_game_info_v3 not set");
Expand All @@ -331,6 +331,34 @@ TEST_CLASS(AchievementRuntimeExports_Tests)
Assert::IsNotNull((void*)pClient.create_achievement_list_v3, L"create_achievement_list_v3 not set");
}

GSL_SUPPRESS_TYPE4
static void AssertV4Exports(const rc_client_external_t& pClient)
{
Assert::IsNotNull((void*)pClient.get_user_info_v3, L"get_user_info_v3 not set");
Assert::IsNotNull((void*)pClient.get_game_info_v3, L"get_game_info_v3 not set");
Assert::IsNotNull((void*)pClient.get_subset_info_v3, L"get_subset_info_v3 not set");
Assert::IsNotNull((void*)pClient.get_achievement_info_v3, L"get_achievement_info_v3 not set");
Assert::IsNotNull((void*)pClient.create_achievement_list_v3, L"create_achievement_list_v3 not set");
}

GSL_SUPPRESS_TYPE4
static void AssertV5Exports(const rc_client_external_t& pClient)
{
Assert::IsNotNull((void*)pClient.get_user_game_summary_v5, L"get_user_game_summary_v5 not set");
Assert::IsNotNull((void*)pClient.get_user_subset_summary, L"get_user_subset_summary not set");
}

GSL_SUPPRESS_TYPE4
static void AssertV6Exports(const rc_client_external_t& pClient)
{
Assert::IsNotNull((void*)pClient.create_subset_list, L"create_subset_list not set");
}

GSL_SUPPRESS_TYPE4
static void AssertV7Exports(const rc_client_external_t& pClient)
{
Assert::IsNotNull((void*)pClient.get_next_achievement_info, L"get_next_achievement_info not set");
}
public:
TEST_METHOD(TestGetExternalClientV1)
{
Expand Down Expand Up @@ -377,6 +405,80 @@ TEST_CLASS(AchievementRuntimeExports_Tests)
Assert::IsTrue(IsExternalRcheevosClient());
}

TEST_METHOD(TestGetExternalClientV4)
{
AchievementRuntimeExportsHarness runtime;

rc_client_external_t pClient;
memset(&pClient, 0, sizeof(pClient));

_Rcheevos_GetExternalClient(&pClient, 4);

AssertV1Exports(pClient);
AssertV2Exports(pClient);
AssertV3Exports(pClient);
AssertV4Exports(pClient);

Assert::IsTrue(IsExternalRcheevosClient());
}

TEST_METHOD(TestGetExternalClientV5)
{
AchievementRuntimeExportsHarness runtime;

rc_client_external_t pClient;
memset(&pClient, 0, sizeof(pClient));

_Rcheevos_GetExternalClient(&pClient, 5);

AssertV1Exports(pClient);
AssertV2Exports(pClient);
AssertV3Exports(pClient);
AssertV4Exports(pClient);
AssertV5Exports(pClient);

Assert::IsTrue(IsExternalRcheevosClient());
}

TEST_METHOD(TestGetExternalClientV6)
{
AchievementRuntimeExportsHarness runtime;

rc_client_external_t pClient;
memset(&pClient, 0, sizeof(pClient));

_Rcheevos_GetExternalClient(&pClient, 6);

AssertV1Exports(pClient);
AssertV2Exports(pClient);
AssertV3Exports(pClient);
AssertV4Exports(pClient);
AssertV5Exports(pClient);
AssertV6Exports(pClient);

Assert::IsTrue(IsExternalRcheevosClient());
}

TEST_METHOD(TestGetExternalClientV7)
{
AchievementRuntimeExportsHarness runtime;

rc_client_external_t pClient;
memset(&pClient, 0, sizeof(pClient));

_Rcheevos_GetExternalClient(&pClient, 7);

AssertV1Exports(pClient);
AssertV2Exports(pClient);
AssertV3Exports(pClient);
AssertV4Exports(pClient);
AssertV5Exports(pClient);
AssertV6Exports(pClient);
AssertV7Exports(pClient);

Assert::IsTrue(IsExternalRcheevosClient());
}

TEST_METHOD(TestRAIntegrationGetMenu)
{
AchievementRuntimeExportsHarness runtime;
Expand Down
Loading