diff --git a/src/core/file_sys/ticket.cpp b/src/core/file_sys/ticket.cpp index 49f55a84e..1979755a7 100644 --- a/src/core/file_sys/ticket.cpp +++ b/src/core/file_sys/ticket.cpp @@ -167,4 +167,19 @@ std::optional> Ticket::GetTitleKey() const { return title_key; } +bool Ticket::IsPersonal() { + if (ticket_body.console_id == 0u) { + // Common ticket + return false; + } + + auto& otp = HW::UniqueData::GetOTP(); + if (!otp.Valid()) { + LOG_ERROR(HW_AES, "Invalid OTP"); + return false; + } + + return ticket_body.console_id == otp.GetDeviceID(); +} + } // namespace FileSys diff --git a/src/core/file_sys/ticket.h b/src/core/file_sys/ticket.h index ac93ced3e..ed6d379c0 100644 --- a/src/core/file_sys/ticket.h +++ b/src/core/file_sys/ticket.h @@ -1,4 +1,4 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -67,6 +67,8 @@ public: return serialized_size; } + bool IsPersonal(); + private: Body ticket_body; u32_be signature_type; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index e16edca13..50b5a6b25 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -2777,37 +2777,61 @@ void Module::Interface::QueryAvailableTitleDatabase(Kernel::HLERequestContext& c void Module::Interface::GetPersonalizedTicketInfoList(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u32 ticket_count = rp.Pop(); - auto& out_buffer = rp.PopMappedBuffer(); - LOG_DEBUG(Service_AM, "(STUBBED) called, ticket_count={}", ticket_count); + struct AsyncData { + u32 ticket_count; - u32 written = 0; - std::scoped_lock lock(am->am_lists_mutex); - for (auto it = am->am_ticket_list.begin(); - it != am->am_ticket_list.end() && written < ticket_count; it++) { - u64 title_id = it->first; - u32 tid_high = static_cast(title_id << 32); - if ((tid_high & 0x00048010) == 0x00040010 || (tid_high & 0x00048001) == 0x00048001) - continue; + std::vector out; + Kernel::MappedBuffer* out_buffer; + }; + std::shared_ptr async_data = std::make_shared(); + async_data->ticket_count = rp.Pop(); + async_data->out_buffer = &rp.PopMappedBuffer(); - FileSys::Ticket ticket; - if (ticket.Load(title_id, it->second) != Loader::ResultStatus::Success) - continue; + LOG_DEBUG(Service_AM, "called, ticket_count={}", async_data->ticket_count); - TicketInfo info = {}; - info.title_id = ticket.GetTitleID(); - info.ticket_id = ticket.GetTicketID(); - info.version = ticket.GetVersion(); - info.size = static_cast(ticket.GetSerializedSize()); + // TODO(PabloMK7): Properly figure out how to detect personalized tickets. - out_buffer.Write(&info, written * sizeof(TicketInfo), sizeof(TicketInfo)); - written++; - } + ctx.RunAsync( + [this, async_data](Kernel::HLERequestContext& ctx) { + u32 written = 0; + std::scoped_lock lock(am->am_lists_mutex); + for (auto it = am->am_ticket_list.begin(); + it != am->am_ticket_list.end() && written < async_data->ticket_count; it++) { + u64 title_id = it->first; + u32 tid_high = static_cast(title_id << 32); + if ((tid_high & 0x00048010) == 0x00040010 || (tid_high & 0x00048001) == 0x00048001) + continue; - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - rb.Push(ResultSuccess); // No error - rb.Push(written); + FileSys::Ticket ticket; + if (ticket.Load(title_id, it->second) != Loader::ResultStatus::Success || + !ticket.IsPersonal()) + continue; + + TicketInfo info = {}; + info.title_id = ticket.GetTitleID(); + info.ticket_id = ticket.GetTicketID(); + info.version = ticket.GetVersion(); + info.size = static_cast(ticket.GetSerializedSize()); + + async_data->out.push_back(info); + written++; + } + return 0; + }, + [async_data](Kernel::HLERequestContext& ctx) { + u32 written = 0; + for (auto& info : async_data->out) { + async_data->out_buffer->Write(&info, written * sizeof(TicketInfo), + sizeof(TicketInfo)); + written++; + } + + IPC::RequestBuilder rb(ctx, 2, 0); + rb.Push(ResultSuccess); // No error + rb.Push(written); + }, + true); } void Module::Interface::GetNumImportTitleContextsFiltered(Kernel::HLERequestContext& ctx) {