Save unlocked achievements

This commit is contained in:
NicknineTheEagle 2024-06-19 19:06:38 +03:00
parent 738c8dbd11
commit 65b326fba3
6 changed files with 119 additions and 20 deletions

View File

@ -1323,6 +1323,8 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
main_thread_ = main_thread;
on_launch(title_id_.value(), title_name_);
kernel_state_->achievement_manager()->Load(0);
// Plugins must be loaded after calling LaunchModule() and
// FinishLoadingUserModule() which will apply TUs and patching to the main
// xex.

View File

@ -22,11 +22,19 @@ DECLARE_int32(user_language);
namespace xe {
namespace kernel {
AchievementManager::AchievementManager() { unlocked_achievements.clear(); };
AchievementManager::AchievementManager() {
for (auto& achievements : unlocked_achievements) {
achievements.clear();
}
}
void AchievementManager::EarnAchievement(uint64_t xuid, uint32_t title_id,
void AchievementManager::EarnAchievement(uint32_t user_index,
uint32_t achievement_id) {
if (IsAchievementUnlocked(achievement_id)) {
if (user_index >= 4) {
return;
}
if (IsAchievementUnlocked(user_index, achievement_id)) {
return;
}
@ -53,7 +61,9 @@ void AchievementManager::EarnAchievement(uint64_t xuid, uint32_t title_id,
XELOGI("Achievement unlocked: {}", label);
unlocked_achievements[achievement_id] = Clock::QueryHostSystemTime();
unlocked_achievements[user_index][achievement_id] =
Clock::QueryHostSystemTime();
Save(user_index);
// Even if we disable popup we still should store info that this
// achievement was earned.
if (!cvars::show_achievement_notification) {
@ -70,20 +80,106 @@ void AchievementManager::EarnAchievement(uint64_t xuid, uint32_t title_id,
});
}
bool AchievementManager::IsAchievementUnlocked(uint32_t achievement_id) {
auto itr = unlocked_achievements.find(achievement_id);
bool AchievementManager::IsAchievementUnlocked(uint32_t user_index,
uint32_t achievement_id) {
if (user_index >= 4) {
return false;
}
auto itr = unlocked_achievements[user_index].find(achievement_id);
return itr != unlocked_achievements.cend();
return itr != unlocked_achievements[user_index].cend();
}
uint64_t AchievementManager::GetAchievementUnlockTime(uint32_t achievement_id) {
auto itr = unlocked_achievements.find(achievement_id);
if (itr == unlocked_achievements.cend()) {
uint64_t AchievementManager::GetAchievementUnlockTime(uint32_t user_index,
uint32_t achievement_id) {
if (user_index >= 4) {
return 0;
}
auto itr = unlocked_achievements[user_index].find(achievement_id);
if (itr == unlocked_achievements[user_index].cend()) {
return 0;
}
return itr->second;
}
void AchievementManager::Save(uint32_t user_index) {
if (user_index >= 4) {
return;
}
if (!kernel_state()->title_id()) {
return;
}
const std::filesystem::path content_dir =
kernel_state()->content_manager()->ResolveGameUserContentPath();
std::filesystem::create_directories(content_dir);
std::filesystem::path file_path =
content_dir / xe::to_path("achievements.bin");
FILE* file = xe::filesystem::OpenFile(file_path, "wb");
if (!file) {
return;
}
auto& ach_map = unlocked_achievements[user_index];
uint32_t num_unlocks = static_cast<uint32_t>(ach_map.size());
fwrite(&num_unlocks, sizeof(uint32_t), 1, file);
for (auto& elem : ach_map) {
uint32_t ach_id = elem.first;
uint64_t ach_time = elem.second;
fwrite(&ach_id, sizeof(uint32_t), 1, file);
fwrite(&ach_time, sizeof(uint64_t), 1, file);
}
fclose(file);
}
void AchievementManager::Load(uint32_t user_index) {
if (user_index >= 4) {
return;
}
auto& ach_map = unlocked_achievements[user_index];
ach_map.clear();
if (!kernel_state()->title_id()) {
return;
}
const std::filesystem::path content_dir =
kernel_state()->content_manager()->ResolveGameUserContentPath();
std::filesystem::path file_path =
content_dir / xe::to_path("achievements.bin");
FILE* file = xe::filesystem::OpenFile(file_path, "rb");
if (!file) {
return;
}
uint32_t num_unlocks;
if (fread(&num_unlocks, sizeof(uint32_t), 1, file) != 1) {
fclose(file);
return;
}
for (uint32_t i = 0; i < num_unlocks; i++) {
uint32_t ach_id;
uint64_t ach_time;
if (fread(&ach_id, sizeof(uint32_t), 1, file) != 1 ||
fread(&ach_time, sizeof(uint64_t), 1, file) != 1) {
ach_map.clear();
fclose(file);
return;
}
ach_map[ach_id] = ach_time;
}
fclose(file);
}
} // namespace kernel
} // namespace xe

View File

@ -43,16 +43,16 @@ class AchievementManager {
public:
AchievementManager();
void EarnAchievement(uint64_t xuid, uint32_t title_id,
uint32_t achievement_id);
void EarnAchievement(uint32_t user_index, uint32_t achievement_id);
bool IsAchievementUnlocked(uint32_t achievement_id);
uint64_t GetAchievementUnlockTime(uint32_t achievement_id);
bool IsAchievementUnlocked(uint32_t user_index, uint32_t achievement_id);
uint64_t GetAchievementUnlockTime(uint32_t user_index,
uint32_t achievement_id);
void Save(uint32_t user_index);
void Load(uint32_t user_index);
private:
std::map<uint32_t, uint64_t> unlocked_achievements;
// void Load();
// void Save();
std::map<uint32_t, uint64_t> unlocked_achievements[4];
};
} // namespace kernel

View File

@ -1121,6 +1121,7 @@ void KernelState::UpdateUsedUserProfiles() {
if (!IsUserSignedIn(i) && is_used) {
user_profiles_.emplace(i, std::make_unique<xam::UserProfile>(i));
achievement_manager()->Load(i);
BroadcastNotification(0x12, 0);
}
}

View File

@ -92,7 +92,7 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
(X_XUSER_ACHIEVEMENT*)memory_->TranslateVirtual(achievements_ptr);
for (uint32_t i = 0; i < achievement_count; i++, achievement++) {
kernel_state_->achievement_manager()->EarnAchievement(
achievement->user_idx, 0, achievement->achievement_id);
achievement->user_idx, achievement->achievement_id);
}
return X_E_SUCCESS;
}

View File

@ -607,10 +607,10 @@ dword_result_t XamUserCreateAchievementEnumerator_entry(
for (const util::XdbfAchievementTableEntry& entry : achievement_list) {
auto is_unlocked =
kernel_state()->achievement_manager()->IsAchievementUnlocked(
entry.id);
user_index, entry.id);
auto unlock_time =
kernel_state()->achievement_manager()->GetAchievementUnlockTime(
entry.id);
user_index, entry.id);
auto item = XAchievementEnumerator::AchievementDetails{
entry.id,