Services/AM: Implement GetPatchTitleInfos, correct error codes/returns, misc fixes
This commit is contained in:
parent
26065fd872
commit
2e38ea7a33
@ -24,6 +24,9 @@
|
|||||||
namespace Service {
|
namespace Service {
|
||||||
namespace AM {
|
namespace AM {
|
||||||
|
|
||||||
|
constexpr u32 TID_HIGH_UPDATE = 0x0004000E;
|
||||||
|
constexpr u32 TID_HIGH_DLC = 0x0004008C;
|
||||||
|
|
||||||
static bool lists_initialized = false;
|
static bool lists_initialized = false;
|
||||||
static std::array<std::vector<u64_le>, 3> am_title_list;
|
static std::array<std::vector<u64_le>, 3> am_title_list;
|
||||||
|
|
||||||
@ -182,7 +185,7 @@ void GetNumPrograms(Service::Interface* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FindContentInfos(Service::Interface* self) {
|
void FindContentInfos(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1002, 4, 2); // 0x10020104
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1002, 4, 4); // 0x10020104
|
||||||
|
|
||||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
u64 title_id = rp.Pop<u64>();
|
u64 title_id = rp.Pop<u64>();
|
||||||
@ -225,7 +228,7 @@ void FindContentInfos(Service::Interface* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ListContentInfos(Service::Interface* self) {
|
void ListContentInfos(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1003, 5, 1); // 0x10030142
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1003, 5, 2); // 0x10030142
|
||||||
u32 content_count = rp.Pop<u32>();
|
u32 content_count = rp.Pop<u32>();
|
||||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
u64 title_id = rp.Pop<u64>();
|
u64 title_id = rp.Pop<u64>();
|
||||||
@ -264,7 +267,7 @@ void ListContentInfos(Service::Interface* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DeleteContents(Service::Interface* self) {
|
void DeleteContents(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1004, 4, 1); // 0x10040102
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1004, 4, 2); // 0x10040102
|
||||||
u8 media_type = rp.Pop<u8>();
|
u8 media_type = rp.Pop<u8>();
|
||||||
u64 title_id = rp.Pop<u64>();
|
u64 title_id = rp.Pop<u64>();
|
||||||
u32 content_count = rp.Pop<u32>();
|
u32 content_count = rp.Pop<u32>();
|
||||||
@ -278,7 +281,7 @@ void DeleteContents(Service::Interface* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetProgramList(Service::Interface* self) {
|
void GetProgramList(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 2, 2, 1); // 0x00020082
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 2, 2, 2); // 0x00020082
|
||||||
|
|
||||||
u32 count = rp.Pop<u32>();
|
u32 count = rp.Pop<u32>();
|
||||||
u8 media_type = rp.Pop<u8>();
|
u8 media_type = rp.Pop<u8>();
|
||||||
@ -302,18 +305,9 @@ void GetProgramList(Service::Interface* self) {
|
|||||||
rb.Push(copied);
|
rb.Push(copied);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetProgramInfos(Service::Interface* self) {
|
ResultCode GetTitleInfoFromList(const std::vector<u64>& title_id_list,
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 3, 2, 2); // 0x00030084
|
Service::FS::MediaType media_type, VAddr title_info_out) {
|
||||||
|
for (u32 i = 0; i < title_id_list.size(); i++) {
|
||||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
|
||||||
u32 title_count = rp.Pop<u32>();
|
|
||||||
VAddr title_id_list_pointer = rp.PopMappedBuffer();
|
|
||||||
VAddr title_info_out = rp.PopMappedBuffer();
|
|
||||||
|
|
||||||
std::vector<u64> title_id_list(title_count);
|
|
||||||
Memory::ReadBlock(title_id_list_pointer, title_id_list.data(), title_count * sizeof(u64));
|
|
||||||
|
|
||||||
for (u32 i = 0; i < title_count; i++) {
|
|
||||||
std::string tmd_path = GetTitleMetadataPath(media_type, title_id_list[i]);
|
std::string tmd_path = GetTitleMetadataPath(media_type, title_id_list[i]);
|
||||||
|
|
||||||
TitleInfo title_info = {};
|
TitleInfo title_info = {};
|
||||||
@ -326,23 +320,113 @@ void GetProgramInfos(Service::Interface* self) {
|
|||||||
title_info.size = tmd.GetContentSizeByIndex(FileSys::TMDContentIndex::Main);
|
title_info.size = tmd.GetContentSizeByIndex(FileSys::TMDContentIndex::Main);
|
||||||
title_info.version = tmd.GetTitleVersion();
|
title_info.version = tmd.GetTitleVersion();
|
||||||
title_info.type = tmd.GetTitleType();
|
title_info.type = tmd.GetTitleType();
|
||||||
|
} else {
|
||||||
|
return ResultCode(ErrorDescription::NotFound, ErrorModule::AM,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||||
}
|
}
|
||||||
Memory::WriteBlock(title_info_out, &title_info, sizeof(TitleInfo));
|
Memory::WriteBlock(title_info_out, &title_info, sizeof(TitleInfo));
|
||||||
title_info_out += sizeof(TitleInfo);
|
title_info_out += sizeof(TitleInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
return RESULT_SUCCESS;
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetDataTitleInfos(Service::Interface* self) {
|
void GetProgramInfos(Service::Interface* self) {
|
||||||
GetProgramInfos(self);
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 3, 2, 4); // 0x00030084
|
||||||
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
|
u32 title_count = rp.Pop<u32>();
|
||||||
|
|
||||||
|
size_t title_id_list_size, title_info_size;
|
||||||
|
IPC::MappedBufferPermissions title_id_list_perms, title_info_perms;
|
||||||
|
VAddr title_id_list_pointer = rp.PopMappedBuffer(&title_id_list_size, &title_id_list_perms);
|
||||||
|
VAddr title_info_out = rp.PopMappedBuffer(&title_info_size, &title_info_perms);
|
||||||
|
|
||||||
|
std::vector<u64> title_id_list(title_count);
|
||||||
|
Memory::ReadBlock(title_id_list_pointer, title_id_list.data(), title_count * sizeof(u64));
|
||||||
|
|
||||||
|
ResultCode result = GetTitleInfoFromList(title_id_list, media_type, title_info_out);
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushMappedBuffer(title_id_list_pointer, title_id_list_size, title_id_list_perms);
|
||||||
|
rb.PushMappedBuffer(title_info_out, title_info_size, title_info_perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetDLCTitleInfos(Service::Interface* self) {
|
||||||
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1005, 2, 4); // 0x10050084
|
||||||
|
|
||||||
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
|
u32 title_count = rp.Pop<u32>();
|
||||||
|
|
||||||
|
size_t title_id_list_size, title_info_size;
|
||||||
|
IPC::MappedBufferPermissions title_id_list_perms, title_info_perms;
|
||||||
|
VAddr title_id_list_pointer = rp.PopMappedBuffer(&title_id_list_size, &title_id_list_perms);
|
||||||
|
VAddr title_info_out = rp.PopMappedBuffer(&title_info_size, &title_info_perms);
|
||||||
|
|
||||||
|
std::vector<u64> title_id_list(title_count);
|
||||||
|
Memory::ReadBlock(title_id_list_pointer, title_id_list.data(), title_count * sizeof(u64));
|
||||||
|
|
||||||
|
ResultCode result = RESULT_SUCCESS;
|
||||||
|
|
||||||
|
// Validate that DLC TIDs were passed in
|
||||||
|
for (u32 i = 0; i < title_count; i++) {
|
||||||
|
u32 tid_high = static_cast<u32>(title_id_list[i] >> 32);
|
||||||
|
if (tid_high != TID_HIGH_DLC) {
|
||||||
|
result = ResultCode(ErrCodes::InvalidTIDInList, ErrorModule::AM,
|
||||||
|
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
result = GetTitleInfoFromList(title_id_list, media_type, title_info_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushMappedBuffer(title_id_list_pointer, title_id_list_size, title_id_list_perms);
|
||||||
|
rb.PushMappedBuffer(title_info_out, title_info_size, title_info_perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetPatchTitleInfos(Service::Interface* self) {
|
||||||
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x100D, 2, 4); // 0x100D0084
|
||||||
|
|
||||||
|
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||||
|
u32 title_count = rp.Pop<u32>();
|
||||||
|
|
||||||
|
size_t title_id_list_size, title_info_size;
|
||||||
|
IPC::MappedBufferPermissions title_id_list_perms, title_info_perms;
|
||||||
|
VAddr title_id_list_pointer = rp.PopMappedBuffer(&title_id_list_size, &title_id_list_perms);
|
||||||
|
VAddr title_info_out = rp.PopMappedBuffer(&title_info_size, &title_info_perms);
|
||||||
|
|
||||||
|
std::vector<u64> title_id_list(title_count);
|
||||||
|
Memory::ReadBlock(title_id_list_pointer, title_id_list.data(), title_count * sizeof(u64));
|
||||||
|
|
||||||
|
ResultCode result = RESULT_SUCCESS;
|
||||||
|
|
||||||
|
// Validate that update TIDs were passed in
|
||||||
|
for (u32 i = 0; i < title_count; i++) {
|
||||||
|
u32 tid_high = static_cast<u32>(title_id_list[i] >> 32);
|
||||||
|
if (tid_high != TID_HIGH_UPDATE) {
|
||||||
|
result = ResultCode(ErrCodes::InvalidTIDInList, ErrorModule::AM,
|
||||||
|
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
result = GetTitleInfoFromList(title_id_list, media_type, title_info_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushMappedBuffer(title_id_list_pointer, title_id_list_size, title_id_list_perms);
|
||||||
|
rb.PushMappedBuffer(title_info_out, title_info_size, title_info_perms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListDataTitleTicketInfos(Service::Interface* self) {
|
void ListDataTitleTicketInfos(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1007, 4, 1); // 0x10070102
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1007, 4, 4); // 0x10070102
|
||||||
u32 ticket_count = rp.Pop<u32>();
|
u32 ticket_count = rp.Pop<u32>();
|
||||||
u64 title_id = rp.Pop<u64>();
|
u64 title_id = rp.Pop<u64>();
|
||||||
u32 start_index = rp.Pop<u32>();
|
u32 start_index = rp.Pop<u32>();
|
||||||
@ -408,7 +492,7 @@ void GetNumTickets(Service::Interface* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetTicketList(Service::Interface* self) {
|
void GetTicketList(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 9, 2, 1); // 0x00090082
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 9, 2, 2); // 0x00090082
|
||||||
u32 ticket_list_count = rp.Pop<u32>();
|
u32 ticket_list_count = rp.Pop<u32>();
|
||||||
u32 ticket_index = rp.Pop<u32>();
|
u32 ticket_index = rp.Pop<u32>();
|
||||||
VAddr ticket_tids_out = rp.PopMappedBuffer();
|
VAddr ticket_tids_out = rp.PopMappedBuffer();
|
||||||
|
@ -19,6 +19,12 @@ class Interface;
|
|||||||
|
|
||||||
namespace AM {
|
namespace AM {
|
||||||
|
|
||||||
|
namespace ErrCodes {
|
||||||
|
enum {
|
||||||
|
InvalidTIDInList = 60,
|
||||||
|
};
|
||||||
|
} // namespace ErrCodes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the .tmd path for a title
|
* Get the .tmd path for a title
|
||||||
* @param media_type the media the title exists on
|
* @param media_type the media the title exists on
|
||||||
@ -139,8 +145,8 @@ void GetProgramList(Service::Interface* self);
|
|||||||
void GetProgramInfos(Service::Interface* self);
|
void GetProgramInfos(Service::Interface* self);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AM::GetDataTitleInfos service function
|
* AM::GetDLCTitleInfos service function
|
||||||
* Wrapper for AM::GetProgramInfos
|
* Wrapper for AM::GetProgramInfos, explicitly checks that TID high value is 0004008C.
|
||||||
* Inputs:
|
* Inputs:
|
||||||
* 1 : u8 Mediatype
|
* 1 : u8 Mediatype
|
||||||
* 2 : Total titles
|
* 2 : Total titles
|
||||||
@ -149,7 +155,22 @@ void GetProgramInfos(Service::Interface* self);
|
|||||||
* Outputs:
|
* Outputs:
|
||||||
* 1 : Result, 0 on success, otherwise error code
|
* 1 : Result, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
void GetDataTitleInfos(Service::Interface* self);
|
void GetDLCTitleInfos(Service::Interface* self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AM::GetPatchTitleInfos service function
|
||||||
|
* Wrapper for AM::GetProgramInfos, explicitly checks that TID high value is 0004000E.
|
||||||
|
* Inputs:
|
||||||
|
* 1 : u8 Mediatype
|
||||||
|
* 2 : Total titles
|
||||||
|
* 4 : TitleIDList input pointer
|
||||||
|
* 6 : TitleList output pointer
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result, 0 on success, otherwise error code
|
||||||
|
* 2 : TitleIDList input pointer
|
||||||
|
* 4 : TitleList output pointer
|
||||||
|
*/
|
||||||
|
void GetPatchTitleInfos(Service::Interface* self);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AM::ListDataTitleTicketInfos service function
|
* AM::ListDataTitleTicketInfos service function
|
||||||
|
@ -13,7 +13,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
|||||||
{0x10020104, FindContentInfos, "FindContentInfos"},
|
{0x10020104, FindContentInfos, "FindContentInfos"},
|
||||||
{0x10030142, ListContentInfos, "ListContentInfos"},
|
{0x10030142, ListContentInfos, "ListContentInfos"},
|
||||||
{0x10040102, DeleteContents, "DeleteContents"},
|
{0x10040102, DeleteContents, "DeleteContents"},
|
||||||
{0x10050084, GetDataTitleInfos, "GetDataTitleInfos"},
|
{0x10050084, GetDLCTitleInfos, "GetDLCTitleInfos"},
|
||||||
{0x10060080, nullptr, "GetNumDataTitleTickets"},
|
{0x10060080, nullptr, "GetNumDataTitleTickets"},
|
||||||
{0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
|
{0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
|
||||||
{0x100801C2, nullptr, "GetItemRights"},
|
{0x100801C2, nullptr, "GetItemRights"},
|
||||||
@ -21,7 +21,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
|||||||
{0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
|
{0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
|
||||||
{0x100B00C0, nullptr, "GetNumExistingContentInfos"},
|
{0x100B00C0, nullptr, "GetNumExistingContentInfos"},
|
||||||
{0x100C0142, nullptr, "ListExistingContentInfos"},
|
{0x100C0142, nullptr, "ListExistingContentInfos"},
|
||||||
{0x100D0084, nullptr, "GetPatchTitleInfos"},
|
{0x100D0084, GetPatchTitleInfos, "GetPatchTitleInfos"},
|
||||||
};
|
};
|
||||||
|
|
||||||
AM_APP_Interface::AM_APP_Interface() {
|
AM_APP_Interface::AM_APP_Interface() {
|
||||||
|
@ -58,7 +58,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
|||||||
{0x10020104, FindContentInfos, "FindContentInfos"},
|
{0x10020104, FindContentInfos, "FindContentInfos"},
|
||||||
{0x10030142, ListContentInfos, "ListContentInfos"},
|
{0x10030142, ListContentInfos, "ListContentInfos"},
|
||||||
{0x10040102, DeleteContents, "DeleteContents"},
|
{0x10040102, DeleteContents, "DeleteContents"},
|
||||||
{0x10050084, GetDataTitleInfos, "GetDataTitleInfos"},
|
{0x10050084, GetDLCTitleInfos, "GetDLCTitleInfos"},
|
||||||
{0x10060080, nullptr, "GetNumDataTitleTickets"},
|
{0x10060080, nullptr, "GetNumDataTitleTickets"},
|
||||||
{0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
|
{0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
|
||||||
{0x100801C2, nullptr, "GetItemRights"},
|
{0x100801C2, nullptr, "GetItemRights"},
|
||||||
@ -66,7 +66,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
|||||||
{0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
|
{0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
|
||||||
{0x100B00C0, nullptr, "GetNumExistingContentInfos"},
|
{0x100B00C0, nullptr, "GetNumExistingContentInfos"},
|
||||||
{0x100C0142, nullptr, "ListExistingContentInfos"},
|
{0x100C0142, nullptr, "ListExistingContentInfos"},
|
||||||
{0x100D0084, nullptr, "GetPatchTitleInfos"},
|
{0x100D0084, GetPatchTitleInfos, "GetPatchTitleInfos"},
|
||||||
};
|
};
|
||||||
|
|
||||||
AM_SYS_Interface::AM_SYS_Interface() {
|
AM_SYS_Interface::AM_SYS_Interface() {
|
||||||
|
Loading…
Reference in New Issue
Block a user