sceNpTrophy: unlock platinum trophies

This commit is contained in:
Megamouse 2019-12-21 11:46:43 +01:00
parent 4450ae0c7a
commit e7845357e2
4 changed files with 151 additions and 33 deletions

View File

@ -154,7 +154,35 @@ void fmt_class_string<SceNpTrophyError>::format(std::string& out, u64 arg)
});
}
// Helpers
void show_trophy_notification(u32 context, u32 handle, u32 trophyId, const std::string& context_name)
{
// Get icon for the notification.
const std::string padded_trophy_id = fmt::format("%03u", trophyId);
const std::string trophy_icon_path = "/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + context_name + "/TROP" + padded_trophy_id + ".PNG";
fs::file trophy_icon_file = fs::file(vfs::get(trophy_icon_path));
std::vector<uchar> trophy_icon_data;
trophy_icon_file.read(trophy_icon_data, trophy_icon_file.size());
vm::var<SceNpTrophyDetails> details({ 0 });
vm::var<SceNpTrophyData> _({ 0 });
const s32 ret = sceNpTrophyGetTrophyInfo(context, handle, trophyId, details, _);
if (ret != CELL_OK)
{
sceNpTrophy.error("Failed to get info for trophy dialog. Error code %x", ret);
*details = SceNpTrophyDetails();
}
if (auto trophy_notification_dialog = Emu.GetCallbacks().get_trophy_notification_dialog())
{
trophy_notification_dialog->ShowTrophyNotification(*details, trophy_icon_data);
}
}
// Functions
error_code sceNpTrophyInit(vm::ptr<void> pool, u32 poolSize, u32 containerId, u64 options)
{
sceNpTrophy.warning("sceNpTrophyInit(pool=*0x%x, poolSize=0x%x, containerId=0x%x, options=0x%llx)", pool, poolSize, containerId, options);
@ -795,37 +823,40 @@ error_code sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, vm::pt
if (ctxt->tropusr->GetTrophyUnlockState(trophyId))
return SCE_NP_TROPHY_ERROR_ALREADY_UNLOCKED;
ctxt->tropusr->UnlockTrophy(trophyId, 0, 0); // TODO
std::string trophyPath = "/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + "/TROPUSR.DAT";
ctxt->tropusr->Save(trophyPath);
ctxt->tropusr->UnlockTrophy(trophyId, 0, 0); // TODO: add timestamps
// TODO: Make sure that unlocking platinum trophies is properly implemented and improve upon it
const std::string& config_path = vfs::get("/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + "/TROPCONF.SFM");
const u32 unlocked_platinum_id = ctxt->tropusr->GetUnlockedPlatinumID(trophyId, config_path);
if (unlocked_platinum_id != SCE_NP_TROPHY_INVALID_TROPHY_ID)
{
sceNpTrophy.warning("sceNpTrophyUnlockTrophy: All requirements for unlocking the platinum trophy (ID = %d) were met.)", unlocked_platinum_id);
if (ctxt->tropusr->UnlockTrophy(unlocked_platinum_id, 0, 0)) // TODO: add timestamps
{
sceNpTrophy.success("You unlocked a platinum trophy! Hooray!!!");
}
}
if (platinumId)
{
*platinumId = SCE_NP_TROPHY_INVALID_TROPHY_ID; // TODO
*platinumId = unlocked_platinum_id;
sceNpTrophy.warning("sceNpTrophyUnlockTrophy: platinumId was set to %d)", unlocked_platinum_id);
}
const std::string trophyPath = "/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + "/TROPUSR.DAT";
ctxt->tropusr->Save(trophyPath);
if (g_cfg.misc.show_trophy_popups)
{
// Get icon for the notification.
const std::string padded_trophy_id = fmt::format("%03u", trophyId);
const std::string trophy_icon_path = "/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + "/TROP" + padded_trophy_id + ".PNG";
fs::file trophy_icon_file = fs::file(vfs::get(trophy_icon_path));
std::vector<uchar> trophy_icon_data;
trophy_icon_file.read(trophy_icon_data, trophy_icon_file.size());
// Enqueue popup for the regular trophy
show_trophy_notification(context, handle, trophyId, ctxt->trp_name);
vm::var<SceNpTrophyDetails> details({0});
vm::var<SceNpTrophyData> _({0});
const s32 ret = sceNpTrophyGetTrophyInfo(context, handle, trophyId, details, _);
if (ret != CELL_OK)
if (unlocked_platinum_id != SCE_NP_TROPHY_INVALID_TROPHY_ID)
{
sceNpTrophy.error("Failed to get info for trophy dialog. Error code %x", ret);
*details = SceNpTrophyDetails();
}
if (auto trophy_notification_dialog = Emu.GetCallbacks().get_trophy_notification_dialog())
{
trophy_notification_dialog->ShowTrophyNotification(*details, trophy_icon_data);
// Enqueue popup for the holy platinum trophy
show_trophy_notification(context, handle, unlocked_platinum_id, ctxt->trp_name);
}
}

View File

@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include "restore_new.h"
#include "Utilities/rXml.h"
#include "define_new_memleakdetect.h"
@ -159,18 +159,19 @@ bool TROPUSRLoader::Generate(const std::string& filepath, const std::string& con
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());
u32 trophy_grade;
switch (n->GetAttribute("ttype")[0])
{
case 'B': trophy_grade = 4; break;
case 'S': trophy_grade = 3; break;
case 'G': trophy_grade = 2; break;
case 'P': trophy_grade = 1; break;
default: trophy_grade = 0;
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;
}
TROPUSREntry4 entry4 = { 4, u32{sizeof(TROPUSREntry4)} - 0x10, ::size32(m_table4), 0, trophy_id, trophy_grade, 0xFFFFFFFF };
TROPUSREntry4 entry4 = { 4, u32{sizeof(TROPUSREntry4)} - 0x10, ::size32(m_table4), 0, trophy_id, trophy_grade, trophy_pid };
TROPUSREntry6 entry6 = { 6, u32{sizeof(TROPUSREntry6)} - 0x10, ::size32(m_table6), 0, trophy_id };
m_table4.push_back(entry4);
@ -216,6 +217,80 @@ u32 TROPUSRLoader::GetUnlockedTrophiesCount()
return count;
}
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())
{
LOG_WARNING(LOADER, "TROPUSRLoader::GetUnlockedPlatinumID: Invalid id=%d", trophy_id);
return invalid_trophy_id;
}
if (m_table6.size() != m_table4.size())
{
LOG_WARNING(LOADER, "TROPUSRLoader::GetUnlockedPlatinumID: Table size mismatch: %d vs. %d", m_table6.size(), m_table4.size());
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();
}
const size_t trophy_count = m_table4.size();
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
for (size_t i = 0; i < trophy_count; i++)
{
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::GetTrophyUnlockState(u32 id)
{
if (id >= m_table6.size())
@ -243,6 +318,7 @@ bool TROPUSRLoader::UnlockTrophy(u32 id, u64 timestamp1, u64 timestamp2)
{
if (id >= m_table6.size())
{
LOG_WARNING(LOADER, "TROPUSRLoader::UnlockTrophy: Invalid id=%d", id);
return false;
}

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
struct TROPUSRHeader
{
@ -30,7 +30,7 @@ struct TROPUSREntry4
// Entry Contents
be_t<u32> trophy_id; // Trophy ID
be_t<u32> trophy_grade; // This seems interesting
be_t<u32> unk5; // Seems to be FF FF FF FF
be_t<u32> trophy_pid; // (Assuming that this is the platinum link id) FF FF FF FF (-1) = SCE_NP_TROPHY_INVALID_TROPHY_ID
char unk6[68]; // Just zeroes?
};
@ -51,11 +51,20 @@ struct TROPUSREntry6
be_t<u64> timestamp2;
char unk6[64]; // Just zeroes?
//Note: One of the fields should hold a flag showing whether the trophy is hidden or not
// Note: One of the fields should hold a flag showing whether the trophy is hidden or not
};
class TROPUSRLoader
{
enum trophy_grade : u32
{
unknown = 0, // SCE_NP_TROPHY_GRADE_UNKNOWN
platinum = 1, // SCE_NP_TROPHY_GRADE_PLATINUM
gold = 2, // SCE_NP_TROPHY_GRADE_GOLD
silver = 3, // SCE_NP_TROPHY_GRADE_SILVER
bronze = 4 // SCE_NP_TROPHY_GRADE_BRONZE
};
fs::file m_file;
TROPUSRHeader m_header{};
std::vector<TROPUSRTableHeader> m_tableHeaders;
@ -75,6 +84,8 @@ public:
virtual u32 GetTrophiesCount();
virtual u32 GetUnlockedTrophiesCount();
virtual u32 GetUnlockedPlatinumID(u32 trophy_id, const std::string& config_path);
virtual u32 GetTrophyUnlockState(u32 id);
virtual u64 GetTrophyTimestamp(u32 id);

View File

@ -392,7 +392,7 @@ bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name)
const QString path = qstr(game_trophy_data->path) + "TROP" + padding + QString::number(trophy_id) + ".PNG";
if (!trophy_icon.load(path))
{
LOG_ERROR(GENERAL, "Failed to load trophy icon for trophy %n %s", trophy_id, game_trophy_data->path);
LOG_ERROR(GENERAL, "Failed to load trophy icon for trophy %d %s", trophy_id, game_trophy_data->path);
}
game_trophy_data->trophy_images.emplace_back(std::move(trophy_icon));
}