Trophy Manager: allow to lock/unlock trophies

This commit is contained in:
Megamouse 2023-11-17 21:33:30 +01:00
parent cee6d03033
commit 47fcb9562f
7 changed files with 177 additions and 22 deletions

View File

@ -807,10 +807,10 @@ CellRtcDateTime tick_to_date_time(u64 tick)
bool exit_while = false;
do
{
bool leap = is_leap_year(years);
for (u32 m = 0; m <= 11; m++)
const bool leap = is_leap_year(years);
for (u32 m = 0; m < 12; m++)
{
u8 daysinmonth = DAYS_IN_MONTH[m + (leap * 12)];
const u8 daysinmonth = DAYS_IN_MONTH[m + (leap * 12)];
if (days_tmp >= daysinmonth)
{
months++;
@ -842,6 +842,52 @@ CellRtcDateTime tick_to_date_time(u64 tick)
return date_time;
}
u64 date_time_to_tick(CellRtcDateTime date_time)
{
const auto get_days_in_year = [](u16 year, u16 months) -> u64
{
const bool leap = is_leap_year(year);
u64 days = 0;
for (u16 m = 0; m < months; m++)
{
days += DAYS_IN_MONTH[m + (leap * 12)];
}
return days;
};
u64 days = 0;
if (date_time.day > 1)
{
// We only need the whole days before "this" day
days += date_time.day - 1ULL;
}
if (date_time.month > 1)
{
// We only need the whole months before "this" month
days += get_days_in_year(date_time.year, date_time.month - 1ULL);
}
if (date_time.year > 1)
{
// We only need the whole years before "this" year
// NOTE: tick_to_date_time starts counting with year 1, so count [1,n[ instead of [0,n-1[
for (u16 year = 1; year < date_time.year; year++)
{
days += get_days_in_year(year, 12);
}
}
u64 tick = date_time.microsecond
+ u64{date_time.second} * 1000000ULL
+ u64{date_time.minute} * 60ULL * 1000000ULL
+ u64{date_time.hour} * 60ULL * 60ULL * 1000000ULL
+ days * 24ULL * 60ULL * 60ULL * 1000000ULL;
return tick;
}
error_code cellRtcSetTick(vm::ptr<CellRtcDateTime> pTime, vm::cptr<CellRtcTick> pTick)
{
cellRtc.todo("cellRtcSetTick(pTime=*0x%x, pTick=*0x%x)", pTime, pTick);

View File

@ -53,3 +53,4 @@ error_code cellRtcGetTick(vm::cptr<CellRtcDateTime> pTime, vm::ptr<CellRtcTick>
error_code cellRtcGetCurrentTick(vm::ptr<CellRtcTick> pTick);
CellRtcDateTime tick_to_date_time(u64 tick);
u64 date_time_to_tick(CellRtcDateTime date_time);

View File

@ -1033,7 +1033,10 @@ error_code sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, vm::pt
sceNpTrophy.error("sceNpTrophyUnlockTrophy: Failed to get timestamp: 0x%x", +error);
}
ctxt->tropusr->UnlockTrophy(trophyId, tick->tick, tick->tick);
if (ctxt->tropusr->UnlockTrophy(trophyId, tick->tick, tick->tick))
{
sceNpTrophy.notice("Trophy %d unlocked", trophyId);
}
// 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");
@ -1056,7 +1059,10 @@ error_code sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, vm::pt
}
const std::string trophyPath = "/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + "/TROPUSR.DAT";
ctxt->tropusr->Save(trophyPath);
if (!ctxt->tropusr->Save(trophyPath))
{
sceNpTrophy.error("sceNpTrophyUnlockTrophy: failed to save '%s'", trophyPath);
}
if (g_cfg.misc.show_trophy_popups)
{

View File

@ -31,9 +31,9 @@ std::shared_ptr<rXmlNode> trophy_xml_document::GetRoot()
return trophy_base;
}
TROPUSRLoader::load_result TROPUSRLoader::Load(const std::string& filepath, const std::string& configpath)
TROPUSRLoader::load_result TROPUSRLoader::Load(std::string_view filepath, std::string_view configpath)
{
const std::string& path = vfs::get(filepath);
const std::string path = vfs::get(filepath);
load_result res{};
@ -143,7 +143,7 @@ bool TROPUSRLoader::LoadTables()
}
// TODO: TROPUSRLoader::Save deletes the TROPUSR and creates it again. This is probably very slow.
bool TROPUSRLoader::Save(const std::string& filepath)
bool TROPUSRLoader::Save(std::string_view filepath)
{
fs::pending_file temp(vfs::get(filepath));
@ -160,7 +160,7 @@ bool TROPUSRLoader::Save(const std::string& filepath)
return temp.commit();
}
bool TROPUSRLoader::Generate(const std::string& filepath, const std::string& configpath)
bool TROPUSRLoader::Generate(std::string_view filepath, std::string_view configpath)
{
fs::file config(vfs::get(configpath));
@ -377,3 +377,18 @@ bool TROPUSRLoader::UnlockTrophy(u32 id, u64 timestamp1, u64 timestamp2)
return true;
}
bool TROPUSRLoader::LockTrophy(u32 id)
{
if (id >= m_table6.size())
{
trp_log.warning("TROPUSRLoader::LockTrophy: Invalid id=%d", id);
return false;
}
m_table6[id].trophy_state = 0;
m_table6[id].timestamp1 = 0;
m_table6[id].timestamp2 = 0;
return true;
}

View File

@ -81,10 +81,10 @@ class TROPUSRLoader
std::vector<TROPUSREntry4> m_table4;
std::vector<TROPUSREntry6> m_table6;
virtual bool Generate(const std::string& filepath, const std::string& configpath);
virtual bool LoadHeader();
virtual bool LoadTableHeaders();
virtual bool LoadTables();
[[nodiscard]] bool Generate(std::string_view filepath, std::string_view configpath);
[[nodiscard]] bool LoadHeader();
[[nodiscard]] bool LoadTableHeaders();
[[nodiscard]] bool LoadTables();
public:
virtual ~TROPUSRLoader() = default;
@ -95,17 +95,18 @@ public:
bool success;
};
virtual load_result Load(const std::string& filepath, const std::string& configpath);
virtual bool Save(const std::string& filepath);
[[nodiscard]] load_result Load(std::string_view filepath, std::string_view configpath);
[[nodiscard]] bool Save(std::string_view filepath);
virtual u32 GetTrophiesCount() const;
virtual u32 GetUnlockedTrophiesCount() const;
[[nodiscard]] u32 GetTrophiesCount() const;
[[nodiscard]] u32 GetUnlockedTrophiesCount() const;
virtual u32 GetUnlockedPlatinumID(u32 trophy_id, const std::string& config_path);
[[nodiscard]] u32 GetUnlockedPlatinumID(u32 trophy_id, const std::string& config_path);
virtual u32 GetTrophyGrade(u32 id) const;
virtual u32 GetTrophyUnlockState(u32 id) const;
virtual u64 GetTrophyTimestamp(u32 id) const;
[[nodiscard]] u32 GetTrophyGrade(u32 id) const;
[[nodiscard]] u32 GetTrophyUnlockState(u32 id) const;
[[nodiscard]] u64 GetTrophyTimestamp(u32 id) const;
virtual bool UnlockTrophy(u32 id, u64 timestamp1, u64 timestamp2);
[[nodiscard]] bool UnlockTrophy(u32 id, u64 timestamp1, u64 timestamp2);
[[nodiscard]] bool LockTrophy(u32 id);
};

View File

@ -860,6 +860,74 @@ void trophy_manager_dialog::ShowTrophyTableContextMenu(const QPoint& pos)
menu->addMenu(copy_menu);
}
const QTableWidgetItem* id_item = m_trophy_table->item(row, static_cast<int>(gui::trophy_list_columns::id));
const QTableWidgetItem* type_item = m_trophy_table->item(row, static_cast<int>(gui::trophy_list_columns::type));
if (id_item && type_item && !Emu.IsRunning())
{
const int type = type_item->data(Qt::UserRole).toInt();
const int trophy_id = id_item->text().toInt();
const bool is_unlocked = m_trophies_db[db_ind]->trop_usr->GetTrophyUnlockState(trophy_id);
QAction* lock_unlock_trophy = new QAction(is_unlocked ? tr("&Lock Trophy") : tr("&Unlock Trophy"), menu);
connect(lock_unlock_trophy, &QAction::triggered, this, [this, db_ind, trophy_id, is_unlocked, row, type]()
{
if (type == SCE_NP_TROPHY_GRADE_PLATINUM && !is_unlocked)
{
QMessageBox::information(this, tr("Action not permitted."), tr("Platinum trophies can only be unlocked ingame."), QMessageBox::Ok);
return;
}
auto& db = m_trophies_db[db_ind];
const std::string path = vfs::retrieve(db->path);
const std::string tropusr_path = path + "/TROPUSR.DAT";
const std::string tropconf_path = path + "/TROPCONF.SFM";
// Reload trophy file just make sure it hasn't changed
if (!db->trop_usr->Load(tropusr_path, tropconf_path).success)
{
gui_log.error("Failed to load trophy file");
return;
}
u64 tick = 0;
if (is_unlocked)
{
if (!db->trop_usr->LockTrophy(trophy_id))
{
gui_log.error("Failed to lock trophy %d", trophy_id);
return;
}
}
else
{
tick = DateTimeToTick(QDateTime::currentDateTime());
if (!db->trop_usr->UnlockTrophy(trophy_id, tick, tick))
{
gui_log.error("Failed to unlock trophy %d", trophy_id);
return;
}
}
if (!db->trop_usr->Save(tropusr_path))
{
gui_log.error("Failed to save '%s': error=%s", path, fs::g_tls_error);
return;
}
if (QTableWidgetItem* lock_item = m_trophy_table->item(row, static_cast<int>(gui::trophy_list_columns::is_unlocked)))
{
lock_item->setText(db->trop_usr->GetTrophyUnlockState(trophy_id) ? tr("Earned") : tr("Not Earned"));
}
if (QTableWidgetItem* date_item = m_trophy_table->item(row, static_cast<int>(gui::trophy_list_columns::time_unlocked)))
{
date_item->setText(tick ? QLocale().toString(TickToDateTime(tick), gui::persistent::last_played_date_with_time_of_day_format) : tr("Unknown"));
date_item->setData(Qt::UserRole, QVariant::fromValue<qulonglong>(tick));
}
});
menu->addSeparator();
menu->addAction(lock_unlock_trophy);
}
menu->exec(m_trophy_table->viewport()->mapToGlobal(pos));
}
@ -1261,3 +1329,20 @@ QDateTime trophy_manager_dialog::TickToDateTime(u64 tick)
Qt::TimeSpec::UTC);
return datetime.toLocalTime();
}
u64 trophy_manager_dialog::DateTimeToTick(QDateTime date_time)
{
const QDateTime utc = date_time.toUTC();
const QDate date = utc.date();
const QTime time = utc.time();
const CellRtcDateTime rtc_date = {
.year = static_cast<u16>(date.year()),
.month = static_cast<u16>(date.month()),
.day = static_cast<u16>(date.day()),
.hour = static_cast<u16>(time.hour()),
.minute = static_cast<u16>(time.minute()),
.second = static_cast<u16>(time.second()),
.microsecond = static_cast<u32>(time.msec() * 1000),
};
return date_time_to_tick(rtc_date);
}

View File

@ -81,6 +81,7 @@ private:
bool eventFilter(QObject *object, QEvent *event) override;
static QDateTime TickToDateTime(u64 tick);
static u64 DateTimeToTick(QDateTime date_time);
std::shared_ptr<gui_settings> m_gui_settings;