mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 11:36:13 +00:00
TROPUSR Loader & sceNpTrophy stuff
* Added a loader for the TROPUSR.DAT files. * Added a few structs/enums to sceNpTrophy.h * Added more sceNpTrophy functions and updated other ones. * Updated cellHddGame error codes in cellSysutil.h * Added addresses of cellHddGame to cellSysutil_init. NOTE: There is a known problem in the `if (!Emu.GetVFS().ExistsFile(filepath))` in `TROPUSRLoader::Load` which causes the games to overwrite their TROPUSR.DAT file every time they boot and they "forget" the unlocked trophies. However, as long as the game is running the unlocked trophies should be still there.
This commit is contained in:
parent
834700eb24
commit
f86ac251a8
8 changed files with 457 additions and 36 deletions
|
@ -943,5 +943,8 @@ void cellSysutil_init()
|
|||
cellSysutil.AddFunc(0x744c1544, cellSysCacheClear);
|
||||
|
||||
cellSysutil.AddFunc(0x9117df20, cellHddGameCheck);
|
||||
|
||||
//cellSysutil.AddFunc(0x4bdec82a, cellHddGameCheck2);
|
||||
//cellSysutil.AddFunc(0xf82e2ef7, cellHddGameGetSizeKB);
|
||||
//cellSysutil.AddFunc(0x9ca9ffa7, cellHddGameSetSystemVer);
|
||||
//cellSysutil.AddFunc(0xafd605b3, cellHddGameExitBroken);
|
||||
}
|
||||
|
|
|
@ -146,13 +146,14 @@ enum CellMsgDialogType
|
|||
enum
|
||||
{
|
||||
// Return Codes
|
||||
CELL_HDDGAME_RET_CANCEL = 0,
|
||||
CELL_HDDGAME_ERROR_CBRESULT = 0,
|
||||
CELL_HDDGAME_ERROR_ACCESS_ERROR = 0,
|
||||
CELL_HDDGAME_ERROR_INTERNAL = 0,
|
||||
CELL_HDDGAME_ERROR_PARAM = 0,
|
||||
CELL_HDDGAME_ERROR_BROKEN = 0,
|
||||
CELL_HDDGAME_ERROR_FAILURE = 0,
|
||||
CELL_HDDGAME_RET_CANCEL = 1,
|
||||
CELL_HDDGAME_ERROR_CBRESULT = 0x8002ba01,
|
||||
CELL_HDDGAME_ERROR_ACCESS_ERROR = 0x8002ba02,
|
||||
CELL_HDDGAME_ERROR_INTERNAL = 0x8002ba03,
|
||||
CELL_HDDGAME_ERROR_PARAM = 0x8002ba04,
|
||||
CELL_HDDGAME_ERROR_NOSPACE = 0x8002ba05,
|
||||
CELL_HDDGAME_ERROR_BROKEN = 0x8002ba06,
|
||||
CELL_HDDGAME_ERROR_FAILURE = 0x8002ba07,
|
||||
|
||||
// Callback Result
|
||||
CELL_HDDGAME_CBRESULT_OK_CANCEL = 1,
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
#include "stdafx.h"
|
||||
#include "Emu/SysCalls/SysCalls.h"
|
||||
#include "Emu/SysCalls/SC_FUNC.h"
|
||||
#include "wx/xml/xml.h"
|
||||
|
||||
#include "sceNp.h"
|
||||
#include "sceNpTrophy.h"
|
||||
|
||||
#include "Loader/TRP.h"
|
||||
#include "Loader/TROPUSR.h"
|
||||
#include "Emu/SysCalls/lv2/SC_Time.h"
|
||||
|
||||
void sceNpTrophy_unload();
|
||||
void sceNpTrophy_init();
|
||||
|
@ -17,6 +20,8 @@ struct sceNpTrophyInternalContext
|
|||
// TODO
|
||||
std::string trp_name;
|
||||
vfsStream* trp_stream;
|
||||
|
||||
TROPUSRLoader* tropusr;
|
||||
};
|
||||
|
||||
struct sceNpTrophyInternal
|
||||
|
@ -149,9 +154,14 @@ int sceNpTrophyRegisterContext(u32 context, u32 handle, u32 statusCb_addr, u32 a
|
|||
}
|
||||
|
||||
// TODO: Get the path of the current user
|
||||
if (!trp.Install("/dev_hdd0/home/00000001/trophy/" + ctxt.trp_name))
|
||||
std::string trophyPath = "/dev_hdd0/home/00000001/trophy/" + ctxt.trp_name;
|
||||
if (!trp.Install(trophyPath))
|
||||
return SCE_NP_TROPHY_ERROR_ILLEGAL_UPDATE;
|
||||
|
||||
TROPUSRLoader* tropusr = new TROPUSRLoader();
|
||||
tropusr->Load(trophyPath + "/TROPUSR.DAT", trophyPath + "/TROPCONF.SFM");
|
||||
ctxt.tropusr = tropusr;
|
||||
|
||||
// TODO: Callbacks
|
||||
|
||||
return CELL_OK;
|
||||
|
@ -183,7 +193,6 @@ int sceNpTrophyGetRequiredDiskSpace(u32 context, u32 handle, mem64_t reqspace, u
|
|||
// TODO: There are other possible errors
|
||||
|
||||
sceNpTrophyInternalContext& ctxt = s_npTrophyInstance.contexts[context];
|
||||
|
||||
if (!ctxt.trp_stream)
|
||||
return SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST;
|
||||
|
||||
|
@ -214,21 +223,46 @@ int sceNpTrophyGetGameInfo(u32 context, u32 handle, mem_ptr_t<SceNpTrophyGameDet
|
|||
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
// TODO: There are other possible errors
|
||||
|
||||
// sceNpTrophyInternalContext& ctxt = s_npTrophyInstance.contexts[context];
|
||||
wxString path;
|
||||
wxXmlDocument doc;
|
||||
sceNpTrophyInternalContext& ctxt = s_npTrophyInstance.contexts[context];
|
||||
Emu.GetVFS().GetDevice("/dev_hdd0/home/00000001/trophy/" + ctxt.trp_name + "/TROPCONF.SFM", path); // TODO: Get the path of the current user
|
||||
doc.Load(path);
|
||||
|
||||
// TODO: This data is faked, implement a XML reader and get it from TROP.SFM
|
||||
memcpy(details->title, "Trophy Set", SCE_NP_TROPHY_NAME_MAX_SIZE);
|
||||
memcpy(details->description, "Hey! Implement a XML reader, and load the description from TROP.SFM", SCE_NP_TROPHY_DESCR_MAX_SIZE);
|
||||
details->numTrophies = 0;
|
||||
details->numPlatinum = 0;
|
||||
details->numGold = 0;
|
||||
details->numSilver = 0;
|
||||
details->numBronze = 0;
|
||||
data->unlockedTrophies = 0;
|
||||
data->unlockedPlatinum = 0;
|
||||
data->unlockedGold = 0;
|
||||
data->unlockedSilver = 0;
|
||||
data->unlockedBronze = 0;
|
||||
std::string titleName;
|
||||
std::string titleDetail;
|
||||
for (wxXmlNode *n = doc.GetRoot()->GetChildren(); n; n = n->GetNext()) {
|
||||
if (n->GetName() == "title-name")
|
||||
titleName = n->GetNodeContent().mb_str();
|
||||
if (n->GetName() == "title-detail")
|
||||
titleDetail = n->GetNodeContent().mb_str();
|
||||
if (n->GetName() == "trophy")
|
||||
{
|
||||
u32 trophy_id = atoi(n->GetAttribute("id").mb_str());
|
||||
|
||||
details->numTrophies++;
|
||||
switch (n->GetAttribute("ttype").mb_str()[0]) {
|
||||
case 'B': details->numBronze++; break;
|
||||
case 'S': details->numSilver++; break;
|
||||
case 'G': details->numGold++; break;
|
||||
case 'P': details->numPlatinum++; break;
|
||||
}
|
||||
|
||||
if (ctxt.tropusr->GetTrophyUnlockState(trophy_id))
|
||||
{
|
||||
data->unlockedTrophies++;
|
||||
switch (n->GetAttribute("ttype").mb_str()[0]) {
|
||||
case 'B': data->unlockedBronze++; break;
|
||||
case 'S': data->unlockedSilver++; break;
|
||||
case 'G': data->unlockedGold++; break;
|
||||
case 'P': data->unlockedPlatinum++; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(details->title, titleName.c_str(), SCE_NP_TROPHY_NAME_MAX_SIZE);
|
||||
memcpy(details->description, titleDetail.c_str(), SCE_NP_TROPHY_DESCR_MAX_SIZE);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -238,9 +272,29 @@ int sceNpTrophyDestroyHandle()
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
int sceNpTrophyUnlockTrophy()
|
||||
int sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, mem32_t platinumId)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(sceNpTrophy);
|
||||
sceNpTrophy.Warning("sceNpTrophyUnlockTrophy(context=%d, handle=%d, trophyId=%d, platinumId_addr=0x%x)",
|
||||
context, handle, trophyId, platinumId.GetAddr());
|
||||
|
||||
if (!s_npTrophyInstance.m_bInitialized)
|
||||
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED;
|
||||
if (!platinumId.IsGood())
|
||||
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
// TODO: There are other possible errors
|
||||
|
||||
sceNpTrophyInternalContext& ctxt = s_npTrophyInstance.contexts[context];
|
||||
if (trophyId >= ctxt.tropusr->GetTrophiesCount())
|
||||
return SCE_NP_TROPHY_ERROR_INVALID_TROPHY_ID;
|
||||
if (ctxt.tropusr->GetTrophyUnlockState(trophyId))
|
||||
return SCE_NP_TROPHY_ERROR_ALREADY_UNLOCKED;
|
||||
|
||||
u64 timestamp1 = get_system_time(); // TODO: Either timestamp1 or timestamp2 is wrong
|
||||
u64 timestamp2 = get_system_time(); // TODO: Either timestamp1 or timestamp2 is wrong
|
||||
ctxt.tropusr->UnlockTrophy(trophyId, timestamp1, timestamp2);
|
||||
ctxt.tropusr->Save("/dev_hdd0/home/00000001/trophy/" + ctxt.trp_name + "/TROPUSR.DAT");
|
||||
|
||||
platinumId = SCE_NP_TROPHY_INVALID_TROPHY_ID; // TODO
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -250,9 +304,31 @@ int sceNpTrophyTerm()
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
int sceNpTrophyGetTrophyUnlockState()
|
||||
int sceNpTrophyGetTrophyUnlockState(u32 context, u32 handle, mem_ptr_t<SceNpTrophyFlagArray> flags, mem32_t count)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(sceNpTrophy);
|
||||
sceNpTrophy.Warning("sceNpTrophyGetTrophyUnlockState(context=%d, handle=%d, flags_addr=0x%x, count_addr=0x%x)",
|
||||
context, handle, flags.GetAddr(), count.GetAddr());
|
||||
|
||||
if (!s_npTrophyInstance.m_bInitialized)
|
||||
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED;
|
||||
if (!flags.IsGood() || !count.IsGood())
|
||||
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
// TODO: There are other possible errors
|
||||
|
||||
sceNpTrophyInternalContext& ctxt = s_npTrophyInstance.contexts[context];
|
||||
count = ctxt.tropusr->GetTrophiesCount();
|
||||
if (count.GetValue() > 128)
|
||||
ConLog.Warning("sceNpTrophyGetTrophyUnlockState: More than 128 trophies detected!");
|
||||
|
||||
// Pack up to 128 bools in u32 flag_bits[4]
|
||||
for (u32 id=0; id<count.GetValue(); id++)
|
||||
{
|
||||
if (ctxt.tropusr->GetTrophyUnlockState(id))
|
||||
flags->flag_bits[id/32] |= 1<<(id%32);
|
||||
else
|
||||
flags->flag_bits[id/32] &= ~(1<<(id%32));
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -273,14 +349,43 @@ int sceNpTrophyGetTrophyInfo(u32 context, u32 handle, s32 trophyId, mem_ptr_t<Sc
|
|||
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
|
||||
// TODO: There are other possible errors
|
||||
|
||||
// sceNpTrophyInternalContext& ctxt = s_npTrophyInstance.contexts[context];
|
||||
wxString path;
|
||||
wxXmlDocument doc;
|
||||
sceNpTrophyInternalContext& ctxt = s_npTrophyInstance.contexts[context];
|
||||
Emu.GetVFS().GetDevice("/dev_hdd0/home/00000001/trophy/" + ctxt.trp_name + "/TROPCONF.SFM", path); // TODO: Get the path of the current user
|
||||
doc.Load(path);
|
||||
|
||||
// TODO: This data is faked, implement a XML reader and get it from TROP.SFM
|
||||
memcpy(details->name, "Some Trophy", SCE_NP_TROPHY_NAME_MAX_SIZE);
|
||||
memcpy(details->description, "Hey! Implement a XML reader, and load the description from TROP.SFM", SCE_NP_TROPHY_DESCR_MAX_SIZE);
|
||||
details->hidden = false;
|
||||
details->trophyId = trophyId;
|
||||
details->trophyGrade = SCE_NP_TROPHY_GRADE_GOLD;
|
||||
std::string name;
|
||||
std::string detail;
|
||||
for (wxXmlNode *n = doc.GetRoot()->GetChildren(); n; n = n->GetNext()) {
|
||||
if (n->GetName() == "trophy" && (trophyId == atoi(n->GetAttribute("id").mb_str())))
|
||||
{
|
||||
details->trophyId = trophyId;
|
||||
switch (n->GetAttribute("ttype").mb_str()[0]) {
|
||||
case 'B': details->trophyGrade = SCE_NP_TROPHY_GRADE_BRONZE; break;
|
||||
case 'S': details->trophyGrade = SCE_NP_TROPHY_GRADE_SILVER; break;
|
||||
case 'G': details->trophyGrade = SCE_NP_TROPHY_GRADE_GOLD; break;
|
||||
case 'P': details->trophyGrade = SCE_NP_TROPHY_GRADE_PLATINUM; break;
|
||||
}
|
||||
|
||||
switch (n->GetAttribute("hidden").mb_str()[0]) {
|
||||
case 'y': details->hidden = true; break;
|
||||
case 'n': details->hidden = false; break;
|
||||
}
|
||||
|
||||
for (wxXmlNode *n2 = n->GetChildren(); n2; n2 = n2->GetNext()) {
|
||||
if (n2->GetName() == "name") name = n2->GetNodeContent().mb_str();
|
||||
if (n2->GetName() == "detail") detail = n2->GetNodeContent().mb_str();
|
||||
}
|
||||
|
||||
data->trophyId = trophyId;
|
||||
data->unlocked = ctxt.tropusr->GetTrophyUnlockState(trophyId);
|
||||
data->timestamp.tick = ctxt.tropusr->GetTrophyTimestamp(trophyId);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(details->name, name.c_str(), SCE_NP_TROPHY_NAME_MAX_SIZE);
|
||||
memcpy(details->description, detail.c_str(), SCE_NP_TROPHY_DESCR_MAX_SIZE);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,13 @@ enum
|
|||
SCE_NP_TROPHY_GAME_DESCR_MAX_SIZE = 1024,
|
||||
SCE_NP_TROPHY_NAME_MAX_SIZE = 128,
|
||||
SCE_NP_TROPHY_DESCR_MAX_SIZE = 1024,
|
||||
|
||||
SCE_NP_TROPHY_FLAG_SETSIZE = 128,
|
||||
SCE_NP_TROPHY_FLAG_BITS_SHIFT = 5,
|
||||
|
||||
SCE_NP_TROPHY_INVALID_CONTEXT = 0,
|
||||
SCE_NP_TROPHY_INVALID_HANDLE = 0,
|
||||
SCE_NP_TROPHY_INVALID_TROPHY_ID = -1,
|
||||
};
|
||||
|
||||
enum SceNpTrophyGrade
|
||||
|
@ -97,9 +104,15 @@ struct SceNpTrophyDetails
|
|||
u8 reserved[3];
|
||||
};
|
||||
|
||||
struct SceNpTrophyData {
|
||||
struct SceNpTrophyData
|
||||
{
|
||||
CellRtcTick timestamp;
|
||||
be_t<s32> trophyId; // SceNpTrophyId
|
||||
bool unlocked;
|
||||
u8 reserved[3];
|
||||
};
|
||||
|
||||
struct SceNpTrophyFlagArray
|
||||
{
|
||||
u32 flag_bits[SCE_NP_TROPHY_FLAG_SETSIZE >> SCE_NP_TROPHY_FLAG_BITS_SHIFT];
|
||||
};
|
||||
|
|
212
rpcs3/Loader/TROPUSR.cpp
Normal file
212
rpcs3/Loader/TROPUSR.cpp
Normal file
|
@ -0,0 +1,212 @@
|
|||
#include "stdafx.h"
|
||||
#include "TROPUSR.h"
|
||||
|
||||
#include "wx/xml/xml.h"
|
||||
|
||||
TROPUSRLoader::TROPUSRLoader()
|
||||
{
|
||||
m_file = NULL;
|
||||
memset(&m_header, 0, sizeof(m_header));
|
||||
}
|
||||
|
||||
TROPUSRLoader::~TROPUSRLoader()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool TROPUSRLoader::Load(std::string& filepath, std::string& configpath)
|
||||
{
|
||||
if (m_file)
|
||||
Close();
|
||||
|
||||
if (!Emu.GetVFS().ExistsFile(filepath))
|
||||
Generate(filepath, configpath);
|
||||
|
||||
m_file = Emu.GetVFS().OpenFile(filepath, vfsRead);
|
||||
LoadHeader();
|
||||
LoadTableHeaders();
|
||||
LoadTables();
|
||||
|
||||
m_file->Close();
|
||||
m_file = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TROPUSRLoader::LoadHeader()
|
||||
{
|
||||
if(!m_file->IsOpened())
|
||||
return false;
|
||||
|
||||
m_file->Seek(0);
|
||||
if (m_file->Read(&m_header, sizeof(TROPUSRHeader)) != sizeof(TROPUSRHeader))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TROPUSRLoader::LoadTableHeaders()
|
||||
{
|
||||
if(!m_file->IsOpened())
|
||||
return false;
|
||||
|
||||
m_file->Seek(0x30);
|
||||
m_tableHeaders.clear();
|
||||
m_tableHeaders.resize(m_header.tables_count);
|
||||
for (TROPUSRTableHeader& tableHeader : m_tableHeaders) {
|
||||
if (m_file->Read(&tableHeader, sizeof(TROPUSRTableHeader)) != sizeof(TROPUSRTableHeader))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TROPUSRLoader::LoadTables()
|
||||
{
|
||||
if(!m_file->IsOpened())
|
||||
return false;
|
||||
|
||||
for (const TROPUSRTableHeader& tableHeader : m_tableHeaders)
|
||||
{
|
||||
m_file->Seek(tableHeader.offset);
|
||||
|
||||
if (tableHeader.type == 4)
|
||||
{
|
||||
m_table4.clear();
|
||||
m_table4.resize(tableHeader.entries_count);
|
||||
for (auto& entry : m_table4) {
|
||||
if (m_file->Read(&entry, sizeof(TROPUSREntry4)) != sizeof(TROPUSREntry4))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (tableHeader.type == 6)
|
||||
{
|
||||
m_table6.clear();
|
||||
m_table6.resize(tableHeader.entries_count);
|
||||
for (auto& entry : m_table6) {
|
||||
if (m_file->Read(&entry, sizeof(TROPUSREntry6)) != sizeof(TROPUSREntry6))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Other tables
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: TROPUSRLoader::Save deletes the TROPUSR and creates it again. This is probably very slow.
|
||||
bool TROPUSRLoader::Save(std::string& filepath)
|
||||
{
|
||||
if (m_file)
|
||||
Close();
|
||||
|
||||
if (!Emu.GetVFS().ExistsFile(filepath))
|
||||
Emu.GetVFS().CreateFile(filepath);
|
||||
|
||||
m_file = Emu.GetVFS().OpenFile(filepath, vfsWrite);
|
||||
m_file->Write(&m_header, sizeof(TROPUSRHeader));
|
||||
|
||||
for (const TROPUSRTableHeader& tableHeader : m_tableHeaders)
|
||||
m_file->Write(&tableHeader, sizeof(TROPUSRTableHeader));
|
||||
|
||||
for (const auto& entry : m_table4)
|
||||
m_file->Write(&entry, sizeof(TROPUSREntry4));
|
||||
for (const auto& entry : m_table6)
|
||||
m_file->Write(&entry, sizeof(TROPUSREntry6));
|
||||
|
||||
m_file->Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TROPUSRLoader::Generate(std::string& filepath, std::string& configpath)
|
||||
{
|
||||
wxString path;
|
||||
wxXmlDocument doc;
|
||||
Emu.GetVFS().GetDevice(configpath.c_str(), path);
|
||||
doc.Load(path);
|
||||
|
||||
m_table4.clear();
|
||||
m_table6.clear();
|
||||
for (wxXmlNode *n = doc.GetRoot()->GetChildren(); n; n = n->GetNext())
|
||||
{
|
||||
if (n->GetName() == "trophy")
|
||||
{
|
||||
u32 trophy_id = atoi(n->GetAttribute("id").mb_str());
|
||||
u32 trophy_grade;
|
||||
switch (n->GetAttribute("ttype").mb_str()[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;
|
||||
}
|
||||
|
||||
TROPUSREntry4 entry4 = {4, sizeof(TROPUSREntry4)-0x10, m_table4.size(), 0, trophy_id, trophy_grade, 0xFFFFFFFF};
|
||||
TROPUSREntry6 entry6 = {6, sizeof(TROPUSREntry6)-0x10, m_table6.size(), 0, trophy_id, 0, 0, 0, 0, 0};
|
||||
|
||||
m_table4.push_back(entry4);
|
||||
m_table6.push_back(entry6);
|
||||
}
|
||||
}
|
||||
|
||||
u64 offset = sizeof(TROPUSRHeader) + 2 * sizeof(TROPUSRTableHeader);
|
||||
TROPUSRTableHeader table4header = {4, sizeof(TROPUSREntry4)-0x10, 1, m_table4.size(), offset, 0};
|
||||
offset += m_table4.size() * sizeof(TROPUSREntry4);
|
||||
TROPUSRTableHeader table6header = {6, sizeof(TROPUSREntry6)-0x10, 1, m_table6.size(), offset, 0};
|
||||
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;
|
||||
m_header.tables_count = m_tableHeaders.size();
|
||||
m_header.unk2 = 0;
|
||||
|
||||
Save(filepath);
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 TROPUSRLoader::GetTrophiesCount()
|
||||
{
|
||||
return m_table6.size();
|
||||
}
|
||||
|
||||
u32 TROPUSRLoader::GetTrophyUnlockState(u32 id)
|
||||
{
|
||||
if (id >= m_table6.size())
|
||||
ConLog.Warning("TROPUSRLoader::GetUnlockState: Invalid id=%d", id);
|
||||
|
||||
return m_table6[id].trophy_state; // Let's assume the trophies are stored ordered
|
||||
}
|
||||
|
||||
u32 TROPUSRLoader::GetTrophyTimestamp(u32 id)
|
||||
{
|
||||
if (id >= m_table6.size())
|
||||
ConLog.Warning("TROPUSRLoader::GetTrophyTimestamp: Invalid id=%d", id);
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (id >= m_table6.size())
|
||||
return false;
|
||||
|
||||
m_table6[id].trophy_state = 1;
|
||||
m_table6[id].timestamp1 = timestamp1;
|
||||
m_table6[id].timestamp2 = timestamp2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TROPUSRLoader::Close()
|
||||
{
|
||||
if (m_file && m_file->Close())
|
||||
{
|
||||
m_file = NULL;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
83
rpcs3/Loader/TROPUSR.h
Normal file
83
rpcs3/Loader/TROPUSR.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
struct TROPUSRHeader
|
||||
{
|
||||
be_t<u32> magic; // 81 8F 54 AD
|
||||
be_t<u32> unk1;
|
||||
be_t<u32> tables_count;
|
||||
be_t<u32> unk2;
|
||||
char reserved[32];
|
||||
};
|
||||
|
||||
struct TROPUSRTableHeader
|
||||
{
|
||||
be_t<u32> type;
|
||||
be_t<u32> entries_size;
|
||||
be_t<u32> unk1; // Seems to be 1
|
||||
be_t<u32> entries_count;
|
||||
be_t<u64> offset;
|
||||
be_t<u64> reserved;
|
||||
};
|
||||
|
||||
struct TROPUSREntry4
|
||||
{
|
||||
// Entry Header
|
||||
be_t<u32> entry_type; // Always 0x4
|
||||
be_t<u32> entry_size; // Always 0x50
|
||||
be_t<u32> entry_id; // Entry ID
|
||||
be_t<u32> entry_unk1; // Just zeroes?
|
||||
|
||||
// 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
|
||||
char unk6[68]; // Just zeroes?
|
||||
};
|
||||
|
||||
struct TROPUSREntry6
|
||||
{
|
||||
// Entry Header
|
||||
be_t<u32> entry_type; // Always 6
|
||||
be_t<u32> entry_size; // Always 0x60
|
||||
be_t<u32> entry_id; // Entry ID
|
||||
be_t<u32> entry_unk1; // Just zeroes?
|
||||
|
||||
// Entry Contents
|
||||
be_t<u32> trophy_id; // Trophy ID
|
||||
be_t<u32> trophy_state; // Wild guess: 00 00 00 00 = Locked, 00 00 00 01 = Unlocked
|
||||
be_t<u32> unk4; // This seems interesting
|
||||
be_t<u32> unk5; // Just zeroes?
|
||||
be_t<u64> timestamp1;
|
||||
be_t<u64> timestamp2;
|
||||
char unk6[64]; // Just zeroes?
|
||||
};
|
||||
|
||||
class TROPUSRLoader
|
||||
{
|
||||
vfsStream* m_file;
|
||||
TROPUSRHeader m_header;
|
||||
std::vector<TROPUSRTableHeader> m_tableHeaders;
|
||||
|
||||
std::vector<TROPUSREntry4> m_table4;
|
||||
std::vector<TROPUSREntry6> m_table6;
|
||||
|
||||
virtual bool Generate(std::string& filepath, std::string& configpath);
|
||||
virtual bool LoadHeader();
|
||||
virtual bool LoadTableHeaders();
|
||||
virtual bool LoadTables();
|
||||
|
||||
public:
|
||||
TROPUSRLoader();
|
||||
~TROPUSRLoader();
|
||||
|
||||
virtual bool Load(std::string& filepath, std::string& configpath);
|
||||
virtual bool Save(std::string& filepath);
|
||||
virtual bool Close();
|
||||
|
||||
virtual u32 GetTrophiesCount();
|
||||
|
||||
virtual u32 GetTrophyUnlockState(u32 id);
|
||||
virtual u32 GetTrophyTimestamp(u32 id);
|
||||
|
||||
virtual bool UnlockTrophy(u32 id, u64 timestamp1, u64 timestamp2);
|
||||
};
|
|
@ -338,6 +338,7 @@
|
|||
<ClCompile Include="Loader\PKG.cpp" />
|
||||
<ClCompile Include="Loader\PSF.cpp" />
|
||||
<ClCompile Include="Loader\SELF.cpp" />
|
||||
<ClCompile Include="Loader\TROPUSR.cpp" />
|
||||
<ClCompile Include="Loader\TRP.cpp" />
|
||||
<ClCompile Include="rpcs3.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
|
|
@ -469,6 +469,9 @@
|
|||
<ClCompile Include="Emu\SysCalls\lv2\SC_Spinlock.cpp">
|
||||
<Filter>Emu\SysCalls\lv2</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Loader\TROPUSR.cpp">
|
||||
<Filter>Loader</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="rpcs3.rc" />
|
||||
|
|
Loading…
Add table
Reference in a new issue