204 lines
5.7 KiB
C++
204 lines
5.7 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2025 Xenia Canary. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#ifndef XENIA_KERNEL_XAM_XDBF_GPD_INFO_H_
|
|
#define XENIA_KERNEL_XAM_XDBF_GPD_INFO_H_
|
|
|
|
#include <span>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "xenia/base/byte_order.h"
|
|
#include "xenia/kernel/util/xfiletime.h"
|
|
#include "xenia/kernel/xam/user_data.h"
|
|
#include "xenia/kernel/xam/xdbf/xdbf_io.h"
|
|
|
|
namespace xe {
|
|
namespace kernel {
|
|
namespace xam {
|
|
|
|
class UserSetting;
|
|
|
|
enum class GpdSection : uint16_t {
|
|
kAchievement = 0x1, // TitleGpd exclusive
|
|
kImage = 0x2,
|
|
kSetting = 0x3,
|
|
kTitle = 0x4, // Dashboard Gpd exclusive
|
|
kString = 0x5,
|
|
kProtectedAchievement = 0x6, // GFWL only
|
|
};
|
|
|
|
enum class AchievementTypeFlags : uint32_t {
|
|
kCompletion = 0x1,
|
|
kLeveling = 0x2,
|
|
kUnlock = 0x3,
|
|
kEvent = 0x4,
|
|
kTournament = 0x5,
|
|
kCheckpoint = 0x6,
|
|
kOther = 0x7,
|
|
};
|
|
|
|
enum class AchievementFlags : uint32_t {
|
|
kTypeMask = 0x7,
|
|
kShowUnachieved = 0x8,
|
|
kAchievedOnline = 0x10000,
|
|
kAchieved = 0x20000,
|
|
kNotAchievable = 0x40000,
|
|
kWasNotAchievable = 0x80000,
|
|
kPlatformMask = 0x700000,
|
|
kColorizable = 0x1000000, // avatar awards only?
|
|
};
|
|
|
|
struct X_XDBF_AVATARAWARDS_COUNTER {
|
|
uint8_t earned;
|
|
uint8_t possible;
|
|
};
|
|
static_assert_size(X_XDBF_AVATARAWARDS_COUNTER, 2);
|
|
|
|
#pragma pack(push, 1)
|
|
struct X_XDBF_GPD_ACHIEVEMENT {
|
|
xe::be<uint32_t> magic;
|
|
xe::be<uint32_t> id;
|
|
xe::be<uint32_t> image_id;
|
|
xe::be<uint32_t> gamerscore;
|
|
xe::be<uint32_t> flags;
|
|
xe::be<uint64_t> unlock_time;
|
|
// wchar_t* title;
|
|
// wchar_t* description;
|
|
// wchar_t* unlocked_description;
|
|
|
|
bool is_achievement_unlocked() const {
|
|
return flags & static_cast<uint32_t>(AchievementFlags::kAchieved);
|
|
}
|
|
};
|
|
static_assert_size(X_XDBF_GPD_ACHIEVEMENT, 0x1C);
|
|
|
|
struct X_XDBF_GPD_TITLE_PLAYED {
|
|
xe::be<uint32_t> title_id;
|
|
xe::be<uint32_t> achievements_count;
|
|
xe::be<uint32_t> achievements_unlocked;
|
|
xe::be<uint32_t> gamerscore_total;
|
|
xe::be<uint32_t> gamerscore_earned;
|
|
xe::be<uint16_t> online_achievement_count;
|
|
|
|
X_XDBF_AVATARAWARDS_COUNTER all_avatar_awards;
|
|
X_XDBF_AVATARAWARDS_COUNTER male_avatar_awards;
|
|
X_XDBF_AVATARAWARDS_COUNTER female_avatar_awards;
|
|
xe::be<uint32_t>
|
|
flags; // 1 - Offline unlocked, must be synced. 2 - Achievement Unlocked.
|
|
// Image missing. 0x10 - Avatar unlocked. Avatar missing.
|
|
X_FILETIME last_played;
|
|
// xe::be<char16_t> title_name[64]; // size seems to be variable inside GPDs.
|
|
|
|
bool include_in_enumerator() const { return achievements_count != 0; }
|
|
};
|
|
static_assert_size(X_XDBF_GPD_TITLE_PLAYED, 0x28);
|
|
|
|
struct X_XDBF_GPD_SETTING_HEADER {
|
|
xe::be<uint32_t> setting_id;
|
|
xe::be<uint32_t> unknown_1;
|
|
X_USER_DATA_TYPE setting_type;
|
|
char unknown[7];
|
|
X_USER_DATA_UNION base_data;
|
|
|
|
bool RequiresBuffer() const {
|
|
return setting_type == X_USER_DATA_TYPE::BINARY ||
|
|
setting_type == X_USER_DATA_TYPE::WSTRING;
|
|
}
|
|
};
|
|
static_assert_size(X_XDBF_GPD_SETTING_HEADER, 0x18);
|
|
#pragma pack(pop)
|
|
|
|
inline AchievementTypeFlags constexpr GetAchievementType(const uint32_t flags) {
|
|
return static_cast<AchievementTypeFlags>(
|
|
flags & static_cast<uint32_t>(AchievementFlags::kTypeMask));
|
|
}
|
|
|
|
inline std::string constexpr GetAchievementTypeName(
|
|
const AchievementTypeFlags type) {
|
|
switch (type) {
|
|
case AchievementTypeFlags::kCompletion:
|
|
return "Completion";
|
|
break;
|
|
case AchievementTypeFlags::kLeveling:
|
|
return "Leveling";
|
|
break;
|
|
case AchievementTypeFlags::kUnlock:
|
|
return "Unlock";
|
|
break;
|
|
case AchievementTypeFlags::kEvent:
|
|
return "Event";
|
|
break;
|
|
case AchievementTypeFlags::kTournament:
|
|
return "Tournament";
|
|
break;
|
|
case AchievementTypeFlags::kCheckpoint:
|
|
return "Checkpoint";
|
|
break;
|
|
case AchievementTypeFlags::kOther:
|
|
default:
|
|
return "Other";
|
|
break;
|
|
}
|
|
}
|
|
|
|
class GpdInfo : public XdbfFile {
|
|
public:
|
|
GpdInfo();
|
|
GpdInfo(const uint32_t title_id);
|
|
GpdInfo(const uint32_t title_id, const std::vector<uint8_t> buffer);
|
|
|
|
~GpdInfo() = default;
|
|
|
|
// Normally GPD ALWAYS contains one free entry that indicates EOF
|
|
bool IsValid() const { return !free_entries_.empty(); }
|
|
// r/w image, setting, string.
|
|
std::span<const uint8_t> GetImage(uint64_t id) const;
|
|
void AddImage(uint32_t id, std::span<const uint8_t> image_data);
|
|
|
|
X_XDBF_GPD_SETTING_HEADER* GetSetting(uint32_t id);
|
|
std::span<const uint8_t> GetSettingData(uint32_t id);
|
|
|
|
void UpsertSetting(const UserSetting* setting_data);
|
|
|
|
std::u16string GetString(uint32_t id) const;
|
|
void AddString(uint32_t id, std::u16string string_data);
|
|
|
|
std::vector<uint8_t> Serialize() const;
|
|
|
|
protected:
|
|
static bool IsSyncEntry(const Entry* const entry);
|
|
static bool IsEntryOfSection(const Entry* const entry,
|
|
const GpdSection section);
|
|
|
|
void UpsertEntry(Entry* entry);
|
|
void DeleteEntry(const Entry* entry);
|
|
|
|
uint32_t FindFreeLocation(const uint32_t entry_size);
|
|
|
|
private:
|
|
static constexpr uint32_t base_entry_count = 512;
|
|
|
|
uint32_t title_id_ = 0;
|
|
|
|
void InsertEntry(Entry* entry);
|
|
|
|
std::vector<const Entry*> GetSortedEntries() const;
|
|
|
|
void ResizeEntryTable();
|
|
void ReallocateEntry(Entry* entry, uint32_t required_size);
|
|
void MarkSpaceAsFree(uint32_t offset, uint32_t size);
|
|
};
|
|
|
|
} // namespace xam
|
|
} // namespace kernel
|
|
} // namespace xe
|
|
|
|
#endif // XENIA_KERNEL_XAM_XDBF_GPD_INFO_H_
|