mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-01-30 12:32:43 +00:00
Trophy Manager: allow to lock/unlock trophies
This commit is contained in:
parent
cee6d03033
commit
47fcb9562f
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user