mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-17 08:11:51 +00:00
Implement sceNpManagerGetTicketParam
This commit is contained in:
parent
072c289f5e
commit
4479d99a9a
@ -3351,7 +3351,7 @@ error_code sceNpManagerGetTicket(vm::ptr<void> buffer, vm::ptr<u32> bufferSize)
|
||||
|
||||
error_code sceNpManagerGetTicketParam(s32 paramId, vm::ptr<SceNpTicketParam> param)
|
||||
{
|
||||
sceNp.todo("sceNpManagerGetTicketParam(paramId=%d, param=*0x%x)", paramId, param);
|
||||
sceNp.notice("sceNpManagerGetTicketParam(paramId=%d, param=*0x%x)", paramId, param);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
@ -3360,12 +3360,23 @@ error_code sceNpManagerGetTicketParam(s32 paramId, vm::ptr<SceNpTicketParam> par
|
||||
return SCE_NP_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!param)
|
||||
if (!param || paramId < SCE_NP_TICKET_PARAM_SERIAL_ID || paramId > SCE_NP_TICKET_PARAM_SUBJECT_DOB)
|
||||
{
|
||||
// TODO: check paramId
|
||||
return SCE_NP_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
const auto& ticket = nph.get_ticket();
|
||||
|
||||
if (ticket.empty())
|
||||
{
|
||||
return SCE_NP_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!ticket.get_value(paramId, param))
|
||||
{
|
||||
return SCE_NP_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -3380,7 +3391,7 @@ error_code sceNpManagerGetEntitlementIdList(vm::ptr<SceNpEntitlementId> entIdLis
|
||||
return SCE_NP_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
return not_an_error(0);
|
||||
}
|
||||
|
||||
error_code sceNpManagerGetEntitlementById(vm::cptr<char> entId, vm::ptr<SceNpEntitlement> ent)
|
||||
|
@ -243,26 +243,26 @@ enum SceNpError : u32
|
||||
SCE_NP_COMMUNITY_SERVER_ERROR_UNSPECIFIED = 0x8002a4ff,
|
||||
|
||||
// DRM
|
||||
SCE_NP_DRM_ERROR_OUT_OF_MEMORY = 0x80029501,
|
||||
SCE_NP_DRM_ERROR_INVALID_PARAM = 0x80029502,
|
||||
SCE_NP_DRM_ERROR_SERVER_RESPONSE = 0x80029509,
|
||||
SCE_NP_DRM_ERROR_NO_ENTITLEMENT = 0x80029513,
|
||||
SCE_NP_DRM_ERROR_BAD_ACT = 0x80029514,
|
||||
SCE_NP_DRM_ERROR_BAD_FORMAT = 0x80029515,
|
||||
SCE_NP_DRM_ERROR_NO_LOGIN = 0x80029516,
|
||||
SCE_NP_DRM_ERROR_INTERNAL = 0x80029517,
|
||||
SCE_NP_DRM_ERROR_BAD_PERM = 0x80029519,
|
||||
SCE_NP_DRM_ERROR_UNKNOWN_VERSION = 0x8002951a,
|
||||
SCE_NP_DRM_ERROR_TIME_LIMIT = 0x8002951b,
|
||||
SCE_NP_DRM_ERROR_DIFFERENT_ACCOUNT_ID = 0x8002951c,
|
||||
SCE_NP_DRM_ERROR_DIFFERENT_DRM_TYPE = 0x8002951d,
|
||||
SCE_NP_DRM_ERROR_SERVICE_NOT_STARTED = 0x8002951e,
|
||||
SCE_NP_DRM_ERROR_BUSY = 0x80029520,
|
||||
SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND = 0x80029521,
|
||||
SCE_NP_DRM_ERROR_IO = 0x80029525,
|
||||
SCE_NP_DRM_ERROR_FORMAT = 0x80029530,
|
||||
SCE_NP_DRM_ERROR_FILENAME = 0x80029533,
|
||||
SCE_NP_DRM_ERROR_K_LICENSEE = 0x80029534,
|
||||
SCE_NP_DRM_ERROR_OUT_OF_MEMORY = 0x80029501,
|
||||
SCE_NP_DRM_ERROR_INVALID_PARAM = 0x80029502,
|
||||
SCE_NP_DRM_ERROR_SERVER_RESPONSE = 0x80029509,
|
||||
SCE_NP_DRM_ERROR_NO_ENTITLEMENT = 0x80029513,
|
||||
SCE_NP_DRM_ERROR_BAD_ACT = 0x80029514,
|
||||
SCE_NP_DRM_ERROR_BAD_FORMAT = 0x80029515,
|
||||
SCE_NP_DRM_ERROR_NO_LOGIN = 0x80029516,
|
||||
SCE_NP_DRM_ERROR_INTERNAL = 0x80029517,
|
||||
SCE_NP_DRM_ERROR_BAD_PERM = 0x80029519,
|
||||
SCE_NP_DRM_ERROR_UNKNOWN_VERSION = 0x8002951a,
|
||||
SCE_NP_DRM_ERROR_TIME_LIMIT = 0x8002951b,
|
||||
SCE_NP_DRM_ERROR_DIFFERENT_ACCOUNT_ID = 0x8002951c,
|
||||
SCE_NP_DRM_ERROR_DIFFERENT_DRM_TYPE = 0x8002951d,
|
||||
SCE_NP_DRM_ERROR_SERVICE_NOT_STARTED = 0x8002951e,
|
||||
SCE_NP_DRM_ERROR_BUSY = 0x80029520,
|
||||
SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND = 0x80029521,
|
||||
SCE_NP_DRM_ERROR_IO = 0x80029525,
|
||||
SCE_NP_DRM_ERROR_FORMAT = 0x80029530,
|
||||
SCE_NP_DRM_ERROR_FILENAME = 0x80029533,
|
||||
SCE_NP_DRM_ERROR_K_LICENSEE = 0x80029534,
|
||||
|
||||
// DRM Server
|
||||
SCE_NP_DRM_SERVER_ERROR_SERVICE_IS_END = 0x80029700,
|
||||
@ -292,7 +292,7 @@ enum SceNpError : u32
|
||||
SCE_NP_AUTH_EBUSY = 0x8002a00a,
|
||||
SCE_NP_AUTH_EABORT = 0x8002a00c,
|
||||
SCE_NP_AUTH_EEXIST = 0x8002a014,
|
||||
SCE_NP_AUTH_EINVALID_ARGUMENT = 0x8002a015,
|
||||
SCE_NP_AUTH_EINVALID_ARGUMENT = 0x8002a015,
|
||||
|
||||
// Auth extended
|
||||
SCE_NP_AUTH_ERROR_SERVICE_END = 0x8002a200,
|
||||
@ -882,6 +882,30 @@ enum
|
||||
SCE_NP_MATCHING_GUI_EVENT_GET_ROOM_LIST_LIMIT = 0x000b
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_TICKET_PARAM_SERIAL_ID = 0,
|
||||
SCE_NP_TICKET_PARAM_ISSUER_ID = 1,
|
||||
SCE_NP_TICKET_PARAM_ISSUED_DATE = 2,
|
||||
SCE_NP_TICKET_PARAM_EXPIRE_DATE = 3,
|
||||
SCE_NP_TICKET_PARAM_SUBJECT_ACCOUNT_ID = 4,
|
||||
SCE_NP_TICKET_PARAM_SUBJECT_ONLINE_ID = 5,
|
||||
SCE_NP_TICKET_PARAM_SUBJECT_REGION = 6,
|
||||
SCE_NP_TICKET_PARAM_SUBJECT_DOMAIN = 7,
|
||||
SCE_NP_TICKET_PARAM_SERVICE_ID = 8,
|
||||
SCE_NP_TICKET_PARAM_SUBJECT_STATUS = 9,
|
||||
SCE_NP_TICKET_PARAM_STATUS_DURATION = 10,
|
||||
SCE_NP_TICKET_PARAM_SUBJECT_DOB = 11,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_TICKET_SERIAL_ID_SIZE = 20,
|
||||
SCE_NP_SUBJECT_REGION_SIZE = 4,
|
||||
SCE_NP_SUBJECT_DOMAIN_SIZE = 4,
|
||||
SCE_NP_SERVICE_ID_SIZE = 24,
|
||||
};
|
||||
|
||||
struct SceNpDrmKey
|
||||
{
|
||||
u8 keydata[16];
|
||||
|
@ -47,9 +47,294 @@ LOG_CHANNEL(sceNp);
|
||||
|
||||
LOG_CHANNEL(rpcn_log, "rpcn");
|
||||
LOG_CHANNEL(nph_log, "NPHandler");
|
||||
LOG_CHANNEL(ticket_log, "Ticket");
|
||||
|
||||
namespace np
|
||||
{
|
||||
ticket::ticket(std::vector<u8>&& raw_data)
|
||||
: raw_data(raw_data)
|
||||
{
|
||||
parse();
|
||||
}
|
||||
|
||||
std::size_t ticket::size() const
|
||||
{
|
||||
return raw_data.size();
|
||||
}
|
||||
|
||||
const u8* ticket::data() const
|
||||
{
|
||||
return raw_data.data();
|
||||
}
|
||||
|
||||
bool ticket::empty() const
|
||||
{
|
||||
return raw_data.empty();
|
||||
}
|
||||
|
||||
bool ticket::get_value(s32 param_id, vm::ptr<SceNpTicketParam> param) const
|
||||
{
|
||||
if (!parse_success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (param_id)
|
||||
{
|
||||
case SCE_NP_TICKET_PARAM_SERIAL_ID:
|
||||
{
|
||||
const auto& node = nodes[0].data.data_nodes[0];
|
||||
if (node.len != SCE_NP_TICKET_SERIAL_ID_SIZE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(param->data, node.data.data_vec.data(), SCE_NP_TICKET_SERIAL_ID_SIZE);
|
||||
break;
|
||||
}
|
||||
case SCE_NP_TICKET_PARAM_ISSUER_ID:
|
||||
{
|
||||
const auto& node = nodes[0].data.data_nodes[1];
|
||||
param->ui32 = node.data.data_u32;
|
||||
break;
|
||||
}
|
||||
case SCE_NP_TICKET_PARAM_ISSUED_DATE:
|
||||
{
|
||||
const auto& node = nodes[0].data.data_nodes[2];
|
||||
param->ui64 = node.data.data_u64;
|
||||
break;
|
||||
}
|
||||
case SCE_NP_TICKET_PARAM_EXPIRE_DATE:
|
||||
{
|
||||
const auto& node = nodes[0].data.data_nodes[3];
|
||||
param->ui64 = node.data.data_u64;
|
||||
break;
|
||||
}
|
||||
case SCE_NP_TICKET_PARAM_SUBJECT_ACCOUNT_ID:
|
||||
{
|
||||
const auto& node = nodes[0].data.data_nodes[4];
|
||||
param->ui64 = node.data.data_u64;
|
||||
break;
|
||||
}
|
||||
case SCE_NP_TICKET_PARAM_SUBJECT_ONLINE_ID:
|
||||
{
|
||||
const auto& node = nodes[0].data.data_nodes[5];
|
||||
if (node.len != 0x20)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(param->data, node.data.data_vec.data(), 0x20);
|
||||
break;
|
||||
}
|
||||
case SCE_NP_TICKET_PARAM_SUBJECT_REGION:
|
||||
{
|
||||
const auto& node = nodes[0].data.data_nodes[6];
|
||||
if (node.len != SCE_NP_SUBJECT_REGION_SIZE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(param->data, node.data.data_vec.data(), SCE_NP_SUBJECT_REGION_SIZE);
|
||||
break;
|
||||
}
|
||||
case SCE_NP_TICKET_PARAM_SUBJECT_DOMAIN:
|
||||
{
|
||||
const auto& node = nodes[0].data.data_nodes[7];
|
||||
if (node.len != SCE_NP_SUBJECT_DOMAIN_SIZE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(param->data, node.data.data_vec.data(), SCE_NP_SUBJECT_DOMAIN_SIZE);
|
||||
break;
|
||||
}
|
||||
case SCE_NP_TICKET_PARAM_SERVICE_ID:
|
||||
{
|
||||
const auto& node = nodes[0].data.data_nodes[8];
|
||||
if (node.len != SCE_NP_SERVICE_ID_SIZE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(param->data, node.data.data_vec.data(), SCE_NP_SERVICE_ID_SIZE);
|
||||
break;
|
||||
}
|
||||
case SCE_NP_TICKET_PARAM_SUBJECT_STATUS:
|
||||
{
|
||||
const auto& node = nodes[0].data.data_nodes[9];
|
||||
param->ui32 = node.data.data_u32;
|
||||
break;
|
||||
}
|
||||
case SCE_NP_TICKET_PARAM_STATUS_DURATION:
|
||||
case SCE_NP_TICKET_PARAM_SUBJECT_DOB:
|
||||
{
|
||||
param->ui64 = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sceNp.fatal("Invalid ticket param id requested!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<ticket_data> ticket::parse_node(std::size_t index) const
|
||||
{
|
||||
if ((index + MIN_TICKET_DATA_SIZE) > size())
|
||||
{
|
||||
ticket_log.error("node didn't meet minimum size requirements");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ticket_data tdata{};
|
||||
const auto* ptr = data() + index;
|
||||
tdata.id = *reinterpret_cast<const be_t<u16>*>(ptr);
|
||||
tdata.len = *reinterpret_cast<const be_t<u16>*>(ptr + 2);
|
||||
const auto* data_ptr = data() + 4;
|
||||
|
||||
auto check_size = [&](std::size_t expected) -> bool
|
||||
{
|
||||
if ((index + MIN_TICKET_DATA_SIZE + expected) > size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (tdata.id)
|
||||
{
|
||||
case 0:
|
||||
if (tdata.len != 0)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (tdata.len != 4 || !check_size(4))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
tdata.data.data_u32 = *reinterpret_cast<const be_t<u32>*>(data_ptr);
|
||||
break;
|
||||
case 2:
|
||||
case 7:
|
||||
if (tdata.len != 8 || !check_size(8))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
tdata.data.data_u64 = *reinterpret_cast<const be_t<u64>*>(data_ptr);
|
||||
break;
|
||||
case 4:
|
||||
case 8:
|
||||
if (!check_size(tdata.len))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
tdata.data.data_vec = std::vector<u8>(tdata.len);
|
||||
memcpy(tdata.data.data_vec.data(), data_ptr, tdata.len);
|
||||
break;
|
||||
default:
|
||||
if ((tdata.id & 0x3000) == 0x3000)
|
||||
{
|
||||
if (!check_size(tdata.len))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::size_t sub_index = 0;
|
||||
tdata.data.data_nodes = {};
|
||||
while (sub_index < tdata.len)
|
||||
{
|
||||
auto sub_node = parse_node(sub_index + index + 4);
|
||||
if (!sub_node)
|
||||
{
|
||||
ticket_log.error("Failed to parse subnode at %d", sub_index + index + 4);
|
||||
return std::nullopt;
|
||||
}
|
||||
sub_index += sub_node->len + MIN_TICKET_DATA_SIZE;
|
||||
tdata.data.data_nodes.push_back(std::move(*sub_node));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return tdata;
|
||||
}
|
||||
|
||||
void ticket::parse()
|
||||
{
|
||||
nodes.clear();
|
||||
parse_success = false;
|
||||
|
||||
if (size() < (sizeof(u32) * 2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
version = *reinterpret_cast<const be_t<u32>*>(data());
|
||||
if (version != 0x21010000)
|
||||
{
|
||||
ticket_log.error("Invalid version: 0x%08x", version);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 given_size = *reinterpret_cast<const be_t<u32>*>(data() + 4);
|
||||
if ((given_size + 8) != size())
|
||||
{
|
||||
ticket_log.error("Size mismatch (gs: %d vs s: %d)", given_size, size());
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t index = 8;
|
||||
while (index < size())
|
||||
{
|
||||
auto node = parse_node(index);
|
||||
if (!node)
|
||||
{
|
||||
ticket_log.error("Failed to parse node at index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
index += (node->len + MIN_TICKET_DATA_SIZE);
|
||||
nodes.push_back(std::move(*node));
|
||||
}
|
||||
|
||||
// Check that everything expected is there
|
||||
if (nodes.size() != 2)
|
||||
{
|
||||
ticket_log.error("Expected 2 blobs, found %d", nodes.size());
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodes[0].id != 0x3000 && nodes[1].id != 0x3002)
|
||||
{
|
||||
ticket_log.error("The 2 blobs ids are incorrect");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodes[0].data.data_nodes.size() < 12)
|
||||
{
|
||||
ticket_log.error("Expected at least 12 sub-nodes, found %d", nodes[0].data.data_nodes.size());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& subnodes = nodes[0].data.data_nodes;
|
||||
|
||||
if (subnodes[0].id != 8 || subnodes[1].id != 1 || subnodes[2].id != 7 || subnodes[3].id != 7 ||
|
||||
subnodes[4].id != 2 || subnodes[5].id != 4 || subnodes[6].id != 8 || subnodes[7].id != 4 ||
|
||||
subnodes[8].id != 8 || subnodes[9].id != 1)
|
||||
{
|
||||
ticket_log.error("Mismatched node");
|
||||
return;
|
||||
}
|
||||
|
||||
parse_success = true;
|
||||
return;
|
||||
}
|
||||
|
||||
np_handler::np_handler()
|
||||
{
|
||||
g_fxo->need<named_thread<signaling_handler>>();
|
||||
|
@ -17,6 +17,45 @@
|
||||
|
||||
namespace np
|
||||
{
|
||||
struct ticket_data
|
||||
{
|
||||
u16 id{}, len{};
|
||||
|
||||
struct
|
||||
{
|
||||
u32 data_u32{};
|
||||
u64 data_u64{};
|
||||
std::vector<u8> data_vec;
|
||||
std::vector<ticket_data> data_nodes;
|
||||
} data;
|
||||
};
|
||||
|
||||
class ticket
|
||||
{
|
||||
public:
|
||||
ticket() = default;
|
||||
ticket(std::vector<u8>&& raw_data);
|
||||
|
||||
std::size_t size() const;
|
||||
const u8* data() const;
|
||||
bool empty() const;
|
||||
|
||||
bool get_value(s32 param_id, vm::ptr<SceNpTicketParam> param) const;
|
||||
|
||||
private:
|
||||
std::optional<ticket_data> parse_node(std::size_t index) const;
|
||||
void parse();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t MIN_TICKET_DATA_SIZE = 4;
|
||||
|
||||
std::vector<u8> raw_data;
|
||||
|
||||
bool parse_success = false;
|
||||
u32 version{};
|
||||
std::vector<ticket_data> nodes;
|
||||
};
|
||||
|
||||
struct basic_event
|
||||
{
|
||||
s32 event = 0;
|
||||
@ -121,10 +160,7 @@ namespace np
|
||||
|
||||
// Misc stuff
|
||||
void req_ticket(u32 version, const SceNpId* npid, const char* service_id, const u8* cookie, u32 cookie_size, const char* entitlement_id, u32 consumed_count);
|
||||
const std::vector<u8>& get_ticket() const
|
||||
{
|
||||
return current_ticket;
|
||||
}
|
||||
const ticket& get_ticket() const;
|
||||
u32 add_players_to_history(vm::cptr<SceNpId> npids, u32 count);
|
||||
|
||||
// For signaling
|
||||
@ -207,7 +243,7 @@ namespace np
|
||||
bool is_connected = false;
|
||||
bool is_psn_active = false;
|
||||
|
||||
std::vector<u8> current_ticket;
|
||||
ticket current_ticket;
|
||||
|
||||
// IP & DNS info
|
||||
std::string hostname = "localhost";
|
||||
|
@ -213,7 +213,7 @@ namespace np
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -675,7 +675,12 @@ namespace np
|
||||
return;
|
||||
}
|
||||
|
||||
bool np_handler::reply_req_ticket(u32 /*req_id*/, std::vector<u8>& reply_data)
|
||||
const ticket& np_handler::get_ticket() const
|
||||
{
|
||||
return current_ticket;
|
||||
}
|
||||
|
||||
bool np_handler::reply_req_ticket([[maybe_unused]] u32 req_id, std::vector<u8>& reply_data)
|
||||
{
|
||||
vec_stream reply(reply_data, 1);
|
||||
auto ticket_raw = reply.get_rawdata();
|
||||
@ -683,7 +688,7 @@ namespace np
|
||||
if (reply.is_error())
|
||||
return error_and_disconnect("Malformed reply to RequestTicket command");
|
||||
|
||||
current_ticket = std::move(ticket_raw);
|
||||
current_ticket = ticket(std::move(ticket_raw));
|
||||
auto ticket_size = static_cast<s32>(current_ticket.size());
|
||||
|
||||
if (manager_cb)
|
||||
|
Loading…
Reference in New Issue
Block a user