[Kernel] Let XGIUserWriteAchievements unlock achievements

XGIUserWriteAchievements seems to be the main (only?) way achievements are unlocked, when used that'll now try getting the GPD from UserProfile and unlock the achievements passed to it.

Dash GPD info is recalced and GPDs get written to disk whenever achievements are unlocked, letting achievements persist across instances.

TODO:
- "Achievement Unlocked!" UI, writing to log isn't much of an indicator :P
- XamUserCreateAchievementEnumerator, needs to return XACHIEVEMENT_DETAILS structs, which also contain pointers to strings in guest memory...
- Copy over more data from SPA? (need to check what data 360 copies to it)
- Optimize GPD writing, atm every GPD gets rewritten even if it hasn't been changed...
- Change profile folder path? Maybe allow using 360 profiles eventually?
- GPD settings
This commit is contained in:
emoose 2018-11-16 05:03:23 +00:00
parent 2a5ab07024
commit 8368e8ce06
No known key found for this signature in database
GPG Key ID: 3735C67912F5FF97
1 changed files with 31 additions and 0 deletions

View File

@ -17,6 +17,11 @@ namespace kernel {
namespace xam {
namespace apps {
struct X_XUSER_ACHIEVEMENT {
xe::be<uint32_t> user_idx;
xe::be<uint32_t> achievement_id;
};
XgiApp::XgiApp(KernelState* kernel_state) : App(kernel_state, 0xFB) {}
// http://mb.mirage.org/bugzilla/xliveless/main.c
@ -55,6 +60,32 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
uint32_t achievements_ptr = xe::load_and_swap<uint32_t>(buffer + 4);
XELOGD("XGIUserWriteAchievements(%.8X, %.8X)", achievement_count,
achievements_ptr);
auto* game_gpd = kernel_state_->user_profile()->GetTitleGpd();
if (!game_gpd) {
XELOGE("XGIUserWriteAchievements failed, no game GPD set?");
return X_ERROR_SUCCESS;
}
bool modified = false;
auto* achievement =
(X_XUSER_ACHIEVEMENT*)memory_->TranslateVirtual(achievements_ptr);
for (uint32_t i = 0; i < achievement_count; i++) {
util::XdbfAchievement ach;
if (game_gpd->GetAchievement(achievement->achievement_id, &ach)) {
if (!ach.IsUnlocked()) {
XELOGI("Achievement Unlocked! %ws (%d gamerscore) - %ws",
ach.label.c_str(), ach.gamerscore, ach.description.c_str());
ach.Unlock(false);
game_gpd->UpdateAchievement(ach);
modified = true;
}
}
}
if (modified) {
kernel_state_->user_profile()->UpdateGpdFiles();
}
return X_ERROR_SUCCESS;
}
case 0x000B0010: {