rpcsx/rpcs3/Loader/TROPUSR.cpp

333 lines
7.4 KiB
C++
Raw Normal View History

2020-12-05 13:08:24 +01:00
#include "stdafx.h"
2014-07-11 13:59:13 +02:00
#include "Utilities/rXml.h"
#include "Emu/VFS.h"
#include "TROPUSR.h"
LOG_CHANNEL(trp_log, "Trophy");
2020-02-01 05:36:53 +01:00
2014-03-21 15:07:05 +01:00
bool TROPUSRLoader::Load(const std::string& filepath, const std::string& configpath)
{
2016-02-01 22:47:09 +01:00
const std::string& path = vfs::get(filepath);
if (!m_file.open(path, fs::read))
2015-07-26 10:14:56 +02:00
{
if (!Generate(filepath, configpath))
{
return false;
}
2015-07-26 10:14:56 +02:00
}
2016-02-01 22:47:09 +01:00
if (!LoadHeader() || !LoadTableHeaders() || !LoadTables())
{
return false;
}
2016-02-01 22:47:09 +01:00
m_file.release();
return true;
}
bool TROPUSRLoader::LoadHeader()
{
2016-02-01 22:47:09 +01:00
if (!m_file)
2015-07-26 10:14:56 +02:00
{
return false;
2015-07-26 10:14:56 +02:00
}
2016-02-01 22:47:09 +01:00
m_file.seek(0);
2015-07-26 10:14:56 +02:00
2016-02-01 22:47:09 +01:00
if (!m_file.read(m_header))
2015-07-26 10:14:56 +02:00
{
return false;
2015-07-26 10:14:56 +02:00
}
return true;
}
bool TROPUSRLoader::LoadTableHeaders()
{
2016-02-01 22:47:09 +01:00
if (!m_file)
2015-07-26 10:14:56 +02:00
{
return false;
2015-07-26 10:14:56 +02:00
}
2016-02-01 22:47:09 +01:00
m_file.seek(0x30);
m_tableHeaders.clear();
2015-07-26 10:14:56 +02:00
if (!m_file.read<true>(m_tableHeaders, m_header.tables_count))
2015-07-26 10:14:56 +02:00
{
return false;
}
2015-07-26 10:14:56 +02:00
return true;
}
bool TROPUSRLoader::LoadTables()
{
2016-02-01 22:47:09 +01:00
if (!m_file)
2015-07-26 10:14:56 +02:00
{
return false;
2015-07-26 10:14:56 +02:00
}
for (const TROPUSRTableHeader& tableHeader : m_tableHeaders)
{
2016-02-01 22:47:09 +01:00
m_file.seek(tableHeader.offset);
if (tableHeader.type == 4u)
{
m_table4.clear();
2015-07-26 10:14:56 +02:00
if (!m_file.read<true>(m_table4, tableHeader.entries_count))
return false;
}
if (tableHeader.type == 6u)
{
m_table6.clear();
2015-07-26 10:14:56 +02:00
if (!m_file.read<true>(m_table6, tableHeader.entries_count))
return false;
}
// TODO: Other tables
}
return true;
}
// TODO: TROPUSRLoader::Save deletes the TROPUSR and creates it again. This is probably very slow.
2014-03-21 15:07:05 +01:00
bool TROPUSRLoader::Save(const std::string& filepath)
{
2021-03-02 16:13:15 +01:00
fs::pending_file temp(vfs::get(filepath));
if (!temp.file)
2015-07-26 10:14:56 +02:00
{
2016-02-01 22:47:09 +01:00
return false;
2015-07-26 10:14:56 +02:00
}
2021-03-02 16:13:15 +01:00
temp.file.write(m_header);
for (const TROPUSRTableHeader& tableHeader : m_tableHeaders)
2015-07-26 10:14:56 +02:00
{
2021-03-02 16:13:15 +01:00
temp.file.write(tableHeader);
2015-07-26 10:14:56 +02:00
}
for (const auto& entry : m_table4)
2015-07-26 10:14:56 +02:00
{
2021-03-02 16:13:15 +01:00
temp.file.write(entry);
2015-07-26 10:14:56 +02:00
}
for (const auto& entry : m_table6)
2015-07-26 10:14:56 +02:00
{
2021-03-02 16:13:15 +01:00
temp.file.write(entry);
2015-07-26 10:14:56 +02:00
}
2021-03-02 16:13:15 +01:00
return temp.commit();
}
2014-03-21 15:07:05 +01:00
bool TROPUSRLoader::Generate(const std::string& filepath, const std::string& configpath)
{
2017-09-03 21:29:20 +02:00
fs::file config(vfs::get(configpath));
if (!config)
{
return false;
}
2016-02-01 22:47:09 +01:00
rXmlDocument doc;
2017-09-03 21:29:20 +02:00
doc.Read(config.to_string());
m_table4.clear();
m_table6.clear();
2015-07-26 10:14:56 +02:00
auto trophy_base = doc.GetRoot();
if (trophy_base->GetChildren()->GetName() == "trophyconf")
{
trophy_base = trophy_base->GetChildren();
}
for (std::shared_ptr<rXmlNode> n = trophy_base->GetChildren(); n; n = n->GetNext())
{
if (n->GetName() == "trophy")
{
2019-12-19 02:57:40 +01:00
const u32 trophy_id = std::atoi(n->GetAttribute("id").c_str());
2019-12-21 11:46:43 +01:00
const u32 trophy_pid = std::atoi(n->GetAttribute("pid").c_str());
2019-12-19 02:57:40 +01:00
u32 trophy_grade;
2019-11-29 23:28:06 +01:00
switch (n->GetAttribute("ttype")[0])
{
2019-12-21 11:46:43 +01:00
case 'B': trophy_grade = trophy_grade::bronze; break;
case 'S': trophy_grade = trophy_grade::silver; break;
case 'G': trophy_grade = trophy_grade::gold; break;
case 'P': trophy_grade = trophy_grade::platinum; break;
default: trophy_grade = trophy_grade::unknown; break;
}
2019-12-21 11:46:43 +01:00
TROPUSREntry4 entry4 = { 4, u32{sizeof(TROPUSREntry4)} - 0x10, ::size32(m_table4), 0, trophy_id, trophy_grade, trophy_pid };
2019-11-29 23:28:06 +01:00
TROPUSREntry6 entry6 = { 6, u32{sizeof(TROPUSREntry6)} - 0x10, ::size32(m_table6), 0, trophy_id };
m_table4.push_back(entry4);
m_table6.push_back(entry6);
}
}
u64 offset = sizeof(TROPUSRHeader) + 2 * sizeof(TROPUSRTableHeader);
2019-11-29 23:28:06 +01:00
TROPUSRTableHeader table4header = { 4, u32{sizeof(TROPUSREntry4)} - 0x10, 1, ::size32(m_table4), offset };
offset += m_table4.size() * sizeof(TROPUSREntry4);
2019-11-29 23:28:06 +01:00
TROPUSRTableHeader table6header = { 6, u32{sizeof(TROPUSREntry6)} - 0x10, 1, ::size32(m_table6), offset };
offset += m_table6.size() * sizeof(TROPUSREntry6);
m_tableHeaders.clear();
m_tableHeaders.push_back(table4header);
m_tableHeaders.push_back(table6header);
m_header.magic = 0x818F54AD;
m_header.unk1 = 0x00010000;
2019-11-29 23:28:06 +01:00
m_header.tables_count = ::size32(m_tableHeaders);
m_header.unk2 = 0;
Save(filepath);
2015-07-26 10:14:56 +02:00
return true;
}
u32 TROPUSRLoader::GetTrophiesCount()
{
2019-11-29 23:28:06 +01:00
return ::size32(m_table6);
}
u32 TROPUSRLoader::GetUnlockedTrophiesCount()
{
u32 count = 0;
for (const auto& trophy : m_table6)
{
if (trophy.trophy_state)
{
count++;
}
}
return count;
}
2019-12-21 11:46:43 +01:00
u32 TROPUSRLoader::GetUnlockedPlatinumID(u32 trophy_id, const std::string& config_path)
{
constexpr u32 invalid_trophy_id = -1; // SCE_NP_TROPHY_INVALID_TROPHY_ID;
if (trophy_id >= m_table6.size() || trophy_id >= m_table4.size())
{
2020-02-01 05:36:53 +01:00
trp_log.warning("TROPUSRLoader::GetUnlockedPlatinumID: Invalid id=%d", trophy_id);
2019-12-21 11:46:43 +01:00
return invalid_trophy_id;
}
if (m_table6.size() != m_table4.size())
{
2020-02-01 05:36:53 +01:00
trp_log.warning("TROPUSRLoader::GetUnlockedPlatinumID: Table size mismatch: %d vs. %d", m_table6.size(), m_table4.size());
2019-12-21 11:46:43 +01:00
return invalid_trophy_id;
}
// We need to read the trophy info from file here and update it for backwards compatibility.
// TROPUSRLoader::Generate will currently not be called on existing trophy data which might lack the pid.
fs::file config(config_path);
if (!config)
{
return invalid_trophy_id;
}
rXmlDocument doc;
doc.Read(config.to_string());
auto trophy_base = doc.GetRoot();
if (trophy_base->GetChildren()->GetName() == "trophyconf")
{
trophy_base = trophy_base->GetChildren();
}
2020-12-18 08:39:54 +01:00
const usz trophy_count = m_table4.size();
2019-12-21 11:46:43 +01:00
for (std::shared_ptr<rXmlNode> n = trophy_base->GetChildren(); n; n = n->GetNext())
{
if (n->GetName() == "trophy")
{
const u32 trophy_id = std::atoi(n->GetAttribute("id").c_str());
const u32 trophy_pid = std::atoi(n->GetAttribute("pid").c_str());
// We currently assume that trophies are ordered
if (trophy_id < trophy_count && m_table4[trophy_id].trophy_id == trophy_id)
{
// Update the pid for backwards compatibility
m_table4[trophy_id].trophy_pid = trophy_pid;
}
}
}
// Get this trophy's platinum link id
const u32 pid = m_table4[trophy_id].trophy_pid;
// The platinum trophy has to have a valid id and must still be locked
if (pid == invalid_trophy_id || GetTrophyUnlockState(pid)) // the first check is redundant but I'll keep it to prevent regressions
{
return invalid_trophy_id;
}
// The platinum trophy stays locked if any relevant trophy is still locked
2020-12-18 08:39:54 +01:00
for (usz i = 0; i < trophy_count; i++)
2019-12-21 11:46:43 +01:00
{
if (m_table4[i].trophy_pid == pid && !m_table6[i].trophy_state)
{
return invalid_trophy_id;
}
}
// All relevant trophies for this platinum link id were unlocked
return pid;
}
u32 TROPUSRLoader::GetTrophyGrade(u32 id)
{
if (id >= m_table4.size())
{
2020-02-01 05:36:53 +01:00
trp_log.warning("TROPUSRLoader::GetTrophyGrade: Invalid id=%d", id);
return trophy_grade::unknown;
}
return m_table4[id].trophy_grade; // Let's assume the trophies are stored ordered
}
u32 TROPUSRLoader::GetTrophyUnlockState(u32 id)
{
2015-07-26 10:14:56 +02:00
if (id >= m_table6.size())
{
2020-02-01 05:36:53 +01:00
trp_log.warning("TROPUSRLoader::GetTrophyUnlockState: Invalid id=%d", id);
return 0;
2015-07-26 10:14:56 +02:00
}
return m_table6[id].trophy_state; // Let's assume the trophies are stored ordered
}
2014-08-30 22:41:01 +02:00
u64 TROPUSRLoader::GetTrophyTimestamp(u32 id)
{
2014-08-30 22:41:01 +02:00
if (id >= m_table6.size())
2015-07-26 10:14:56 +02:00
{
2020-02-01 05:36:53 +01:00
trp_log.warning("TROPUSRLoader::GetTrophyTimestamp: Invalid id=%d", id);
return 0;
2015-07-26 10:14:56 +02:00
}
2018-09-03 17:46:14 +02:00
// TODO: What timestamp does sceNpTrophyGetTrophyInfo want, timestamp1 or timestamp2?
return m_table6[id].timestamp2; // Let's assume the trophies are stored ordered
}
bool TROPUSRLoader::UnlockTrophy(u32 id, u64 timestamp1, u64 timestamp2)
{
2015-07-26 10:14:56 +02:00
if (id >= m_table6.size())
{
2020-02-01 05:36:53 +01:00
trp_log.warning("TROPUSRLoader::UnlockTrophy: Invalid id=%d", id);
return false;
2015-07-26 10:14:56 +02:00
}
m_table6[id].trophy_state = 1;
m_table6[id].timestamp1 = timestamp1;
m_table6[id].timestamp2 = timestamp2;
2015-07-26 10:14:56 +02:00
return true;
}