mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-06 00:40:11 +00:00
sceNpTrophy: unlock platinum trophies
This commit is contained in:
parent
4450ae0c7a
commit
e7845357e2
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user