[Kernel] Add XdbfTitlePlayed support to GpdFile, used by dash GPD
Eventually user_profile would read dash GPD, loop through XdbfTitlePlayed entries, load GPD for each one... Also eventually settings will be stored in dash GPD (eg. the ones currently defined in user_profile.cc)
This commit is contained in:
parent
00f3627715
commit
734bbc7515
|
@ -249,36 +249,6 @@ std::string SpaFile::GetTitle() const {
|
|||
static_cast<uint16_t>(XdbfSpaID::Title));
|
||||
}
|
||||
|
||||
std::wstring ReadNullTermString(const wchar_t* ptr) {
|
||||
std::wstring retval;
|
||||
wchar_t data = xe::byte_swap(*ptr);
|
||||
while (data != 0) {
|
||||
retval += data;
|
||||
ptr++;
|
||||
data = xe::byte_swap(*ptr);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void ConvertGPDToXdbfAchievement(const X_XDBF_GPD_ACHIEVEMENT* src,
|
||||
XdbfAchievement* dest) {
|
||||
dest->id = src->id;
|
||||
dest->image_id = src->image_id;
|
||||
dest->gamerscore = src->gamerscore;
|
||||
dest->flags = src->flags;
|
||||
dest->unlock_time = src->unlock_time;
|
||||
|
||||
auto* txt_ptr = reinterpret_cast<const uint8_t*>(src + 1);
|
||||
|
||||
dest->label = ReadNullTermString((const wchar_t*)txt_ptr);
|
||||
|
||||
txt_ptr += (dest->label.length() * 2) + 2;
|
||||
dest->description = ReadNullTermString((const wchar_t*)txt_ptr);
|
||||
|
||||
txt_ptr += (dest->description.length() * 2) + 2;
|
||||
dest->unachieved_desc = ReadNullTermString((const wchar_t*)txt_ptr);
|
||||
}
|
||||
|
||||
bool GpdFile::GetAchievement(uint16_t id, XdbfAchievement* dest) {
|
||||
for (size_t i = 0; i < entries.size(); i++) {
|
||||
auto* entry = (XdbfEntry*)&entries[i];
|
||||
|
@ -291,7 +261,7 @@ bool GpdFile::GetAchievement(uint16_t id, XdbfAchievement* dest) {
|
|||
auto* ach_data =
|
||||
reinterpret_cast<const X_XDBF_GPD_ACHIEVEMENT*>(entry->data.data());
|
||||
|
||||
ConvertGPDToXdbfAchievement(ach_data, dest);
|
||||
dest->ReadGPD(ach_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -308,6 +278,9 @@ uint32_t GpdFile::GetAchievements(
|
|||
static_cast<uint16_t>(XdbfGpdSection::kAchievement)) {
|
||||
continue;
|
||||
}
|
||||
if (entry->info.id == 0x100000000 || entry->info.id == 0x200000000) {
|
||||
continue; // achievement sync data, ignore it
|
||||
}
|
||||
|
||||
ach_count++;
|
||||
|
||||
|
@ -316,7 +289,7 @@ uint32_t GpdFile::GetAchievements(
|
|||
reinterpret_cast<const X_XDBF_GPD_ACHIEVEMENT*>(entry->data.data());
|
||||
|
||||
XdbfAchievement ach;
|
||||
ConvertGPDToXdbfAchievement(ach_data, &ach);
|
||||
ach.ReadGPD(ach_data);
|
||||
|
||||
achievements->push_back(ach);
|
||||
}
|
||||
|
@ -325,6 +298,52 @@ uint32_t GpdFile::GetAchievements(
|
|||
return ach_count;
|
||||
}
|
||||
|
||||
bool GpdFile::GetTitle(uint32_t title_id, XdbfTitlePlayed* dest) {
|
||||
for (size_t i = 0; i < entries.size(); i++) {
|
||||
auto* entry = (XdbfEntry*)&entries[i];
|
||||
if (entry->info.section != static_cast<uint16_t>(XdbfGpdSection::kTitle) ||
|
||||
entry->info.id != title_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* title_data =
|
||||
reinterpret_cast<const X_XDBF_GPD_TITLEPLAYED*>(entry->data.data());
|
||||
|
||||
dest->ReadGPD(title_data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t GpdFile::GetTitles(std::vector<XdbfTitlePlayed>* titles) const {
|
||||
uint32_t title_count = 0;
|
||||
|
||||
for (size_t i = 0; i < entries.size(); i++) {
|
||||
auto* entry = (XdbfEntry*)&entries[i];
|
||||
if (entry->info.section != static_cast<uint16_t>(XdbfGpdSection::kTitle)) {
|
||||
continue;
|
||||
}
|
||||
if (entry->info.id == 0x100000000 || entry->info.id == 0x200000000) {
|
||||
continue; // achievement sync data, ignore it
|
||||
}
|
||||
|
||||
title_count++;
|
||||
|
||||
if (titles) {
|
||||
auto* title_data =
|
||||
reinterpret_cast<const X_XDBF_GPD_TITLEPLAYED*>(entry->data.data());
|
||||
|
||||
XdbfTitlePlayed title;
|
||||
title.ReadGPD(title_data);
|
||||
titles->push_back(title);
|
||||
}
|
||||
}
|
||||
|
||||
return title_count;
|
||||
}
|
||||
|
||||
bool GpdFile::UpdateAchievement(XdbfAchievement ach) {
|
||||
XdbfEntry ent;
|
||||
ent.info.section = static_cast<uint16_t>(XdbfGpdSection::kAchievement);
|
||||
|
@ -363,8 +382,28 @@ bool GpdFile::UpdateAchievement(XdbfAchievement ach) {
|
|||
xe::copy_and_swap<wchar_t>((wchar_t*)unach_ptr, ach.unachieved_desc.c_str(),
|
||||
ach.unachieved_desc.size());
|
||||
|
||||
UpdateEntry(ent);
|
||||
return true;
|
||||
return UpdateEntry(ent);
|
||||
}
|
||||
|
||||
bool GpdFile::UpdateTitle(XdbfTitlePlayed title) {
|
||||
XdbfEntry ent;
|
||||
ent.info.section = static_cast<uint16_t>(XdbfGpdSection::kTitle);
|
||||
ent.info.id = title.title_id;
|
||||
|
||||
// calculate entry size...
|
||||
size_t name_len = (title.title_name.length() * 2) + 2;
|
||||
|
||||
size_t est_size = sizeof(X_XDBF_GPD_TITLEPLAYED);
|
||||
est_size += name_len;
|
||||
|
||||
ent.data.resize(est_size);
|
||||
memset(ent.data.data(), 0, est_size);
|
||||
|
||||
// convert XdbfTitlePlayed to GPD title
|
||||
auto* title_data = reinterpret_cast<X_XDBF_GPD_TITLEPLAYED*>(ent.data.data());
|
||||
title.WriteGPD(title_data);
|
||||
|
||||
return UpdateEntry(ent);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
|
|
@ -132,8 +132,87 @@ struct X_XDBF_GPD_ACHIEVEMENT {
|
|||
// wchar_t* unlocked_description;
|
||||
};
|
||||
|
||||
// from https://github.com/xemio/testdev/blob/master/xkelib/xam/_xamext.h
|
||||
struct X_XDBF_GPD_TITLEPLAYED {
|
||||
xe::be<uint32_t> title_id;
|
||||
xe::be<uint32_t> achievements_possible;
|
||||
xe::be<uint32_t> achievements_earned;
|
||||
xe::be<uint32_t> gamerscore_total;
|
||||
xe::be<uint32_t> gamerscore_earned;
|
||||
xe::be<uint16_t> reserved_achievement_count;
|
||||
|
||||
// the following are meant to be split into possible/earned, 1 byte each
|
||||
// but who cares
|
||||
xe::be<uint16_t> all_avatar_awards;
|
||||
xe::be<uint16_t> male_avatar_awards;
|
||||
xe::be<uint16_t> female_avatar_awards;
|
||||
xe::be<uint32_t> reserved_flags;
|
||||
xe::be<uint64_t> last_loaded;
|
||||
// wchar_t* title_name;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
inline std::wstring ReadNullTermString(const wchar_t* ptr) {
|
||||
std::wstring retval;
|
||||
wchar_t data = xe::byte_swap(*ptr);
|
||||
while (data != 0) {
|
||||
retval += data;
|
||||
ptr++;
|
||||
data = xe::byte_swap(*ptr);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct XdbfTitlePlayed {
|
||||
uint32_t title_id = 0;
|
||||
std::wstring title_name;
|
||||
uint32_t achievements_possible = 0;
|
||||
uint32_t achievements_earned = 0;
|
||||
uint32_t gamerscore_total = 0;
|
||||
uint32_t gamerscore_earned = 0;
|
||||
uint16_t reserved_achievement_count = 0;
|
||||
uint16_t all_avatar_awards = 0;
|
||||
uint16_t male_avatar_awards = 0;
|
||||
uint16_t female_avatar_awards = 0;
|
||||
uint32_t reserved_flags = 0;
|
||||
uint64_t last_loaded = 0;
|
||||
|
||||
void ReadGPD(const X_XDBF_GPD_TITLEPLAYED* src) {
|
||||
title_id = src->title_id;
|
||||
achievements_possible = src->achievements_possible;
|
||||
achievements_earned = src->achievements_earned;
|
||||
gamerscore_total = src->gamerscore_total;
|
||||
gamerscore_earned = src->gamerscore_earned;
|
||||
reserved_achievement_count = src->reserved_achievement_count;
|
||||
all_avatar_awards = src->all_avatar_awards;
|
||||
male_avatar_awards = src->male_avatar_awards;
|
||||
female_avatar_awards = src->female_avatar_awards;
|
||||
reserved_flags = src->reserved_flags;
|
||||
last_loaded = src->last_loaded;
|
||||
|
||||
auto* txt_ptr = reinterpret_cast<const uint8_t*>(src + 1);
|
||||
title_name = ReadNullTermString((const wchar_t*)txt_ptr);
|
||||
}
|
||||
|
||||
void WriteGPD(X_XDBF_GPD_TITLEPLAYED* dest) {
|
||||
dest->title_id = title_id;
|
||||
dest->achievements_possible = achievements_possible;
|
||||
dest->achievements_earned = achievements_earned;
|
||||
dest->gamerscore_total = gamerscore_total;
|
||||
dest->gamerscore_earned = gamerscore_earned;
|
||||
dest->reserved_achievement_count = reserved_achievement_count;
|
||||
dest->all_avatar_awards = all_avatar_awards;
|
||||
dest->male_avatar_awards = male_avatar_awards;
|
||||
dest->female_avatar_awards = female_avatar_awards;
|
||||
dest->reserved_flags = reserved_flags;
|
||||
dest->last_loaded = last_loaded;
|
||||
|
||||
auto* txt_ptr = reinterpret_cast<const uint8_t*>(dest + 1);
|
||||
xe::copy_and_swap<wchar_t>((wchar_t*)txt_ptr, title_name.c_str(),
|
||||
title_name.size());
|
||||
}
|
||||
};
|
||||
|
||||
enum class XdbfAchievementType : uint32_t {
|
||||
kCompletion = 1,
|
||||
kLeveling = 2,
|
||||
|
@ -188,6 +267,24 @@ struct XdbfAchievement {
|
|||
flags & ~(static_cast<uint32_t>(XdbfAchievementFlags::kAchievedOnline));
|
||||
unlock_time = 0;
|
||||
}
|
||||
|
||||
void ReadGPD(const X_XDBF_GPD_ACHIEVEMENT* src) {
|
||||
id = src->id;
|
||||
image_id = src->image_id;
|
||||
gamerscore = src->gamerscore;
|
||||
flags = src->flags;
|
||||
unlock_time = src->unlock_time;
|
||||
|
||||
auto* txt_ptr = reinterpret_cast<const uint8_t*>(src + 1);
|
||||
|
||||
label = ReadNullTermString((const wchar_t*)txt_ptr);
|
||||
|
||||
txt_ptr += (label.length() * 2) + 2;
|
||||
description = ReadNullTermString((const wchar_t*)txt_ptr);
|
||||
|
||||
txt_ptr += (description.length() * 2) + 2;
|
||||
unachieved_desc = ReadNullTermString((const wchar_t*)txt_ptr);
|
||||
}
|
||||
};
|
||||
|
||||
struct XdbfEntry {
|
||||
|
@ -235,8 +332,14 @@ class GpdFile : public XdbfFile {
|
|||
bool GetAchievement(uint16_t id, XdbfAchievement* dest);
|
||||
uint32_t GetAchievements(std::vector<XdbfAchievement>* achievements) const;
|
||||
|
||||
bool GetTitle(uint32_t title_id, XdbfTitlePlayed* title);
|
||||
uint32_t GetTitles(std::vector<XdbfTitlePlayed>* titles) const;
|
||||
|
||||
// Updates (or adds) an achievement
|
||||
bool UpdateAchievement(XdbfAchievement ach);
|
||||
|
||||
// Updates (or adds) a title
|
||||
bool UpdateTitle(XdbfTitlePlayed title);
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
|
Loading…
Reference in New Issue