diff --git a/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp b/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp index 9f9b0aa3af..7ce3935dd4 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp @@ -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); } diff --git a/rpcs3/Emu/SysCalls/Modules/cellSysutil.h b/rpcs3/Emu/SysCalls/Modules/cellSysutil.h index 3084fc537a..eb53f7ea34 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSysutil.h +++ b/rpcs3/Emu/SysCalls/Modules/cellSysutil.h @@ -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, diff --git a/rpcs3/Emu/SysCalls/Modules/sceNpTrophy.cpp b/rpcs3/Emu/SysCalls/Modules/sceNpTrophy.cpp index 7f02dd1e6f..41d34dd6e9 100644 --- a/rpcs3/Emu/SysCalls/Modules/sceNpTrophy.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sceNpTrophy.cpp @@ -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_ttitle, "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 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; idGetTrophyUnlockState(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_tname, "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; } diff --git a/rpcs3/Emu/SysCalls/Modules/sceNpTrophy.h b/rpcs3/Emu/SysCalls/Modules/sceNpTrophy.h index eae2c74ed7..6245c0688c 100644 --- a/rpcs3/Emu/SysCalls/Modules/sceNpTrophy.h +++ b/rpcs3/Emu/SysCalls/Modules/sceNpTrophy.h @@ -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 trophyId; // SceNpTrophyId bool unlocked; u8 reserved[3]; }; + +struct SceNpTrophyFlagArray +{ + u32 flag_bits[SCE_NP_TROPHY_FLAG_SETSIZE >> SCE_NP_TROPHY_FLAG_BITS_SHIFT]; +}; diff --git a/rpcs3/Loader/TROPUSR.cpp b/rpcs3/Loader/TROPUSR.cpp new file mode 100644 index 0000000000..7c0e1fac6b --- /dev/null +++ b/rpcs3/Loader/TROPUSR.cpp @@ -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; +} diff --git a/rpcs3/Loader/TROPUSR.h b/rpcs3/Loader/TROPUSR.h new file mode 100644 index 0000000000..a01fd75230 --- /dev/null +++ b/rpcs3/Loader/TROPUSR.h @@ -0,0 +1,83 @@ +#pragma once + +struct TROPUSRHeader +{ + be_t magic; // 81 8F 54 AD + be_t unk1; + be_t tables_count; + be_t unk2; + char reserved[32]; +}; + +struct TROPUSRTableHeader +{ + be_t type; + be_t entries_size; + be_t unk1; // Seems to be 1 + be_t entries_count; + be_t offset; + be_t reserved; +}; + +struct TROPUSREntry4 +{ + // Entry Header + be_t entry_type; // Always 0x4 + be_t entry_size; // Always 0x50 + be_t entry_id; // Entry ID + be_t entry_unk1; // Just zeroes? + + // Entry Contents + be_t trophy_id; // Trophy ID + be_t trophy_grade; // This seems interesting + be_t unk5; // Seems to be FF FF FF FF + char unk6[68]; // Just zeroes? +}; + +struct TROPUSREntry6 +{ + // Entry Header + be_t entry_type; // Always 6 + be_t entry_size; // Always 0x60 + be_t entry_id; // Entry ID + be_t entry_unk1; // Just zeroes? + + // Entry Contents + be_t trophy_id; // Trophy ID + be_t trophy_state; // Wild guess: 00 00 00 00 = Locked, 00 00 00 01 = Unlocked + be_t unk4; // This seems interesting + be_t unk5; // Just zeroes? + be_t timestamp1; + be_t timestamp2; + char unk6[64]; // Just zeroes? +}; + +class TROPUSRLoader +{ + vfsStream* m_file; + TROPUSRHeader m_header; + std::vector m_tableHeaders; + + std::vector m_table4; + std::vector 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); +}; diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index a02f6bf916..bf0d5f4eaf 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -338,6 +338,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 7e4015ba55..b70898668a 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -469,6 +469,9 @@ Emu\SysCalls\lv2 + + Loader +