[Kernel] Update XamUserCreateAchievementEnumerator to work with GPDs
Seems to work fine, tested with Tetris TGM ACE which uses achievements to track progression & unlock things in the game, and unlocking achievements in the GPDs does unlock things in game as intended. Haven't actually tested unlocking the achievements in-game and then checking though (my tetris skills are awful :P), but I think it should work fine. Strings aren't copied into it yet since I didn't want to alloc guest memory for every CreateEnumerator call... Not many games actually have in-game achievement lists though, but I've added a placeholder string for any that might. Like said in the comment, maybe we should alloc those strings in guest mem when the game GPD/SPA is first loaded in?
This commit is contained in:
parent
8368e8ce06
commit
e510972691
|
@ -448,6 +448,20 @@ dword_result_t XamShowSigninUI(dword_t unk, dword_t unk_mask) {
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamShowSigninUI, kUserProfiles, kStub);
|
DECLARE_XAM_EXPORT1(XamShowSigninUI, kUserProfiles, kStub);
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct X_XACHIEVEMENT_DETAILS {
|
||||||
|
xe::be<uint32_t> id;
|
||||||
|
xe::be<uint32_t> label_ptr;
|
||||||
|
xe::be<uint32_t> description_ptr;
|
||||||
|
xe::be<uint32_t> unachieved_ptr;
|
||||||
|
xe::be<uint32_t> image_id;
|
||||||
|
xe::be<uint32_t> gamerscore;
|
||||||
|
xe::be<uint64_t> unlock_time;
|
||||||
|
xe::be<uint32_t> flags;
|
||||||
|
};
|
||||||
|
static_assert_size(X_XACHIEVEMENT_DETAILS, 36);
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
dword_result_t XamUserCreateAchievementEnumerator(dword_t title_id,
|
dword_result_t XamUserCreateAchievementEnumerator(dword_t title_id,
|
||||||
dword_t user_index,
|
dword_t user_index,
|
||||||
dword_t xuid, dword_t flags,
|
dword_t xuid, dword_t flags,
|
||||||
|
@ -455,14 +469,59 @@ dword_result_t XamUserCreateAchievementEnumerator(dword_t title_id,
|
||||||
lpdword_t buffer_size_ptr,
|
lpdword_t buffer_size_ptr,
|
||||||
lpdword_t handle_ptr) {
|
lpdword_t handle_ptr) {
|
||||||
if (buffer_size_ptr) {
|
if (buffer_size_ptr) {
|
||||||
*buffer_size_ptr = 500 * count;
|
*buffer_size_ptr = sizeof(X_XACHIEVEMENT_DETAILS) * count;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto e = new XStaticEnumerator(kernel_state(), count, 500);
|
auto e = new XStaticEnumerator(kernel_state(), count,
|
||||||
|
sizeof(X_XACHIEVEMENT_DETAILS));
|
||||||
e->Initialize();
|
e->Initialize();
|
||||||
|
|
||||||
*handle_ptr = e->handle();
|
*handle_ptr = e->handle();
|
||||||
|
|
||||||
|
// Copy achievements into the enumerator if game GPD is loaded
|
||||||
|
auto* game_gpd = kernel_state()->user_profile()->GetTitleGpd();
|
||||||
|
if (!game_gpd) {
|
||||||
|
XELOGE(
|
||||||
|
"XamUserCreateAchievementEnumerator called without GPD being loaded!");
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t placeholder = 0;
|
||||||
|
|
||||||
|
if (!placeholder) {
|
||||||
|
wchar_t* placeholder_val = L"<placeholder>";
|
||||||
|
|
||||||
|
placeholder = kernel_memory()->SystemHeapAlloc(
|
||||||
|
((uint32_t)wcslen(placeholder_val) + 1) * 2);
|
||||||
|
auto* place_addr = kernel_memory()->TranslateVirtual<wchar_t*>(placeholder);
|
||||||
|
|
||||||
|
memset(place_addr, 0, (wcslen(placeholder_val) + 1) * 2);
|
||||||
|
xe::copy_and_swap(place_addr, placeholder_val, wcslen(placeholder_val));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<util::XdbfAchievement> achievements;
|
||||||
|
game_gpd->GetAchievements(&achievements);
|
||||||
|
|
||||||
|
for (auto ach : achievements) {
|
||||||
|
auto* details = (X_XACHIEVEMENT_DETAILS*)e->AppendItem();
|
||||||
|
details->id = ach.id;
|
||||||
|
details->image_id = ach.image_id;
|
||||||
|
details->gamerscore = ach.gamerscore;
|
||||||
|
details->unlock_time = ach.unlock_time;
|
||||||
|
details->flags = ach.flags;
|
||||||
|
|
||||||
|
// TODO: these, allocating guest mem for them every CreateEnum call would be
|
||||||
|
// very bad...
|
||||||
|
|
||||||
|
// maybe we could alloc these in guest when the title GPD is first loaded?
|
||||||
|
details->label_ptr = placeholder;
|
||||||
|
details->description_ptr = placeholder;
|
||||||
|
details->unachieved_ptr = placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
XELOGD("XamUserCreateAchievementEnumerator: added %d items to enumerator",
|
||||||
|
e->item_count());
|
||||||
|
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamUserCreateAchievementEnumerator, kUserProfiles,
|
DECLARE_XAM_EXPORT1(XamUserCreateAchievementEnumerator, kUserProfiles,
|
||||||
|
|
Loading…
Reference in New Issue