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:
Alexandro Sánchez Bach 2014-03-20 19:23:14 +01:00
parent 834700eb24
commit f86ac251a8
8 changed files with 457 additions and 36 deletions

View file

@ -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);
}

View file

@ -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,

View file

@ -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;
}

View file

@ -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
View 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
View 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);
};

View file

@ -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">

View file

@ -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" />