[Kernel] Added support for writing/reading GPD files

This breaks settings in games that are using them and savefiles in games that use settings to store progress
This commit is contained in:
Gliniak
2024-12-15 13:58:08 +01:00
committed by Radosław Gliński
parent ccf7adf015
commit 1110cdd372
58 changed files with 4642 additions and 2122 deletions

View File

@@ -28,48 +28,45 @@ void GpdAchievementBackend::EarnAchievement(const uint64_t xuid,
return;
}
auto achievement = GetAchievementInfoInternal(xuid, title_id, achievement_id);
if (!achievement) {
return;
}
XELOGI("Player: {} Unlocked Achievement: {}", user->name(),
xe::to_utf8(xe::load_and_swap<std::u16string>(
achievement->achievement_name.c_str())));
const uint64_t unlock_time = Clock::QueryHostSystemTime();
// We're adding achieved online flag because on console locally achieved
// entries don't have valid unlock time.
achievement->flags = achievement->flags |
static_cast<uint32_t>(AchievementFlags::kAchieved) |
static_cast<uint32_t>(AchievementFlags::kAchievedOnline);
achievement->unlock_time = unlock_time;
SaveAchievementData(xuid, title_id, achievement_id);
kernel_state()->xam_state()->user_tracker()->UnlockAchievement(
xuid, achievement_id);
}
AchievementGpdStructure* GpdAchievementBackend::GetAchievementInfoInternal(
const std::optional<Achievement> GpdAchievementBackend::GetAchievementInfo(
const uint64_t xuid, const uint32_t title_id,
const uint32_t achievement_id) const {
const auto user = kernel_state()->xam_state()->GetUserProfile(xuid);
if (!user) {
return nullptr;
return std::nullopt;
}
return user->GetAchievement(title_id, achievement_id);
}
auto entry = user->games_gpd_.find(title_id);
if (entry == user->games_gpd_.cend()) {
return std::nullopt;
}
const AchievementGpdStructure* GpdAchievementBackend::GetAchievementInfo(
const uint64_t xuid, const uint32_t title_id,
const uint32_t achievement_id) const {
return GetAchievementInfoInternal(xuid, title_id, achievement_id);
const auto achievement_entry =
entry->second.GetAchievementEntry(achievement_id);
if (!achievement_entry) {
return std::nullopt;
}
Achievement achievement(achievement_entry);
achievement.achievement_name =
entry->second.GetAchievementTitle(achievement_id);
achievement.unlocked_description =
entry->second.GetAchievementDescription(achievement_id);
achievement.locked_description =
entry->second.GetAchievementUnachievedDescription(achievement_id);
return achievement;
}
bool GpdAchievementBackend::IsAchievementUnlocked(
const uint64_t xuid, const uint32_t title_id,
const uint32_t achievement_id) const {
const auto achievement =
GetAchievementInfoInternal(xuid, title_id, achievement_id);
const auto achievement = GetAchievementInfo(xuid, title_id, achievement_id);
if (!achievement) {
return false;
@@ -79,53 +76,48 @@ bool GpdAchievementBackend::IsAchievementUnlocked(
static_cast<uint32_t>(AchievementFlags::kAchieved)) != 0;
}
const std::vector<AchievementGpdStructure>*
GpdAchievementBackend::GetTitleAchievements(const uint64_t xuid,
const uint32_t title_id) const {
const std::vector<Achievement> GpdAchievementBackend::GetTitleAchievements(
const uint64_t xuid, const uint32_t title_id) const {
const auto user = kernel_state()->xam_state()->GetUserProfile(xuid);
if (!user) {
return {};
}
return user->GetTitleAchievements(title_id);
return kernel_state()->xam_state()->user_tracker()->GetUserTitleAchievements(
xuid, title_id);
}
bool GpdAchievementBackend::LoadAchievementsData(
const uint64_t xuid, const util::XdbfGameData title_data) {
const std::span<const uint8_t> GpdAchievementBackend::GetAchievementIcon(
const uint64_t xuid, const uint32_t title_id,
const uint32_t achievement_id) const {
const auto user = kernel_state()->xam_state()->GetUserProfile(xuid);
if (!user) {
return {};
}
return kernel_state()->xam_state()->user_tracker()->GetAchievementIcon(
xuid, title_id, achievement_id);
}
bool GpdAchievementBackend::LoadAchievementsData(const uint64_t xuid) {
auto user = kernel_state()->xam_state()->GetUserProfile(xuid);
if (!user) {
return false;
}
// Question. Should loading for GPD for profile be directly done by profile or
// here?
if (!title_data.is_valid()) {
return false;
}
const auto achievements = title_data.GetAchievements();
if (achievements.empty()) {
return true;
}
const auto title_id = title_data.GetTitleInformation().title_id;
const XLanguage title_language = title_data.GetExistingLanguage(
static_cast<XLanguage>(cvars::user_language));
for (const auto& achievement : achievements) {
AchievementGpdStructure achievementData(title_language, title_data,
achievement);
user->achievements_[title_id].push_back(achievementData);
}
// TODO(Gliniak): Here should be loader of GPD file for loaded title. That way
// we can load flags and unlock_time from specific user.
// GPDs are handled by UserTracker
return true;
}
bool GpdAchievementBackend::SaveAchievementData(const uint64_t xuid,
const uint32_t title_id,
const uint32_t achievement_id) {
bool GpdAchievementBackend::SaveAchievementData(
const uint64_t xuid, const uint32_t title_id,
const Achievement* achievement) {
auto user = kernel_state()->xam_state()->GetUserProfile(xuid);
if (!user) {
return false;
}
// GPDs are handled by UserTracker
return true;
}

View File

@@ -15,8 +15,8 @@
#include <string>
#include <vector>
#include "xenia/kernel/util/xdbf_utils.h"
#include "xenia/kernel/xam/achievement_manager.h"
#include "xenia/kernel/xam/xdbf/gpd_info.h"
#include "xenia/xbox.h"
namespace xe {
@@ -32,25 +32,23 @@ class GpdAchievementBackend : public AchievementBackendInterface {
const uint32_t achievement_id) override;
bool IsAchievementUnlocked(const uint64_t xuid, const uint32_t title_id,
const uint32_t achievement_id) const override;
const AchievementGpdStructure* GetAchievementInfo(
const std::optional<Achievement> GetAchievementInfo(
const uint64_t xuid, const uint32_t title_id,
const uint32_t achievement_id) const override;
const std::vector<AchievementGpdStructure>* GetTitleAchievements(
const std::vector<Achievement> GetTitleAchievements(
const uint64_t xuid, const uint32_t title_id) const override;
bool LoadAchievementsData(const uint64_t xuid,
const util::XdbfGameData title_data) override;
const std::span<const uint8_t> GetAchievementIcon(
const uint64_t xuid, const uint32_t title_id,
const uint32_t achievement_id) const override;
bool LoadAchievementsData(const uint64_t xuid) override;
private:
AchievementGpdStructure* GetAchievementInfoInternal(
const uint64_t xuid, const uint32_t title_id,
const uint32_t achievement_id) const;
bool SaveAchievementsData(const uint64_t xuid,
const uint32_t title_id) override {
return 0;
};
bool SaveAchievementData(const uint64_t xuid, const uint32_t title_id,
const uint32_t achievement_id) override;
const Achievement* achievement) override;
};
} // namespace xam