diff --git a/src/core/file_sys/ticket.h b/src/core/file_sys/ticket.h index 170493c64c..aec608cf5b 100644 --- a/src/core/file_sys/ticket.h +++ b/src/core/file_sys/ticket.h @@ -50,6 +50,9 @@ public: Loader::ResultStatus Load(std::span file_data, std::size_t offset = 0); std::optional> GetTitleKey() const; + u64 GetTitleID() const { + return ticket_body.title_id; + } private: Body ticket_body; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index b0cc00b117..cf68e34e96 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -374,6 +374,45 @@ bool CIAFile::Close() const { void CIAFile::Flush() const {} +TicketFile::TicketFile() {} + +TicketFile::~TicketFile() { + Close(); +} + +ResultVal TicketFile::Read(u64 offset, std::size_t length, u8* buffer) const { + UNIMPLEMENTED(); + return length; +} + +ResultVal TicketFile::Write(u64 offset, std::size_t length, bool flush, + const u8* buffer) { + written += length; + data.resize(written); + std::memcpy(data.data() + offset, buffer, length); + return length; +} + +u64 TicketFile::GetSize() const { + return written; +} + +bool TicketFile::SetSize(u64 size) const { + return false; +} + +bool TicketFile::Close() const { + FileSys::Ticket ticket; + if (ticket.Load(data, 0) == Loader::ResultStatus::Success) { + LOG_WARNING(Service_AM, "Discarding ticket for {:#016X}.", ticket.GetTitleID()); + } else { + LOG_ERROR(Service_AM, "Invalid ticket provided to TicketFile."); + } + return true; +} + +void TicketFile::Flush() const {} + InstallStatus InstallCIA(const std::string& path, std::function&& update_callback) { LOG_INFO(Service_AM, "Installing {}...", path); @@ -942,6 +981,10 @@ void Module::Interface::GetProgramInfos(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(title_info_out); } +void Module::Interface::GetProgramInfosIgnorePlatform(Kernel::HLERequestContext& ctx) { + GetProgramInfos(ctx); +} + void Module::Interface::DeleteUserProgram(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); auto media_type = rp.PopEnum(); @@ -1177,6 +1220,16 @@ void Module::Interface::NeedsCleanup(Kernel::HLERequestContext& ctx) { rb.Push(false); } +void Module::Interface::DoCleanup(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto media_type = rp.Pop(); + + LOG_DEBUG(Service_AM, "(STUBBED) called, media_type={:#02x}", media_type); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + void Module::Interface::QueryAvailableTitleDatabase(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); u8 media_type = rp.Pop(); @@ -1188,6 +1241,45 @@ void Module::Interface::QueryAvailableTitleDatabase(Kernel::HLERequestContext& c LOG_WARNING(Service_AM, "(STUBBED) media_type={}", media_type); } +void Module::Interface::GetPersonalizedTicketInfoList(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + [[maybe_unused]] u32 ticket_count = rp.Pop(); + [[maybe_unused]] auto& buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); // No error + rb.Push(0); + + LOG_WARNING(Service_AM, "(STUBBED) called, ticket_count={}", ticket_count); +} + +void Module::Interface::GetNumImportTitleContextsFiltered(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + u8 media_type = rp.Pop(); + u8 filter = rp.Pop(); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); // No error + rb.Push(0); + + LOG_WARNING(Service_AM, "(STUBBED) called, media_type={}, filter={}", media_type, filter); +} + +void Module::Interface::GetImportTitleContextListFiltered(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + [[maybe_unused]] const u32 count = rp.Pop(); + const u8 media_type = rp.Pop(); + const u8 filter = rp.Pop(); + auto& buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + rb.Push(RESULT_SUCCESS); // No error + rb.Push(0); + rb.PushMappedBuffer(buffer); + + LOG_WARNING(Service_AM, "(STUBBED) called, media_type={}, filter={}", media_type, filter); +} + void Module::Interface::CheckContentRights(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); u64 tid = rp.Pop(); @@ -1674,6 +1766,30 @@ void Module::Interface::GetMetaDataFromCia(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(output_buffer); } +void Module::Interface::BeginImportTicket(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + + // Create our TicketFile handle for the app to write to + auto file = std::make_shared(am->kernel, std::make_unique(), + FileSys::Path{}); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); // No error + rb.PushCopyObjects(file->Connect()); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void Module::Interface::EndImportTicket(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + [[maybe_unused]] const auto ticket = rp.PopObject(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + Module::Module(Core::System& system) : kernel(system.Kernel()) { ScanForAllTitles(); system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex"); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 7682e48a78..a5409e3198 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -109,6 +109,25 @@ private: std::unique_ptr decryption_state; }; +// A file handled returned for Tickets to be written into and subsequently installed. +class TicketFile final : public FileSys::FileBackend { +public: + explicit TicketFile(); + ~TicketFile(); + + ResultVal Read(u64 offset, std::size_t length, u8* buffer) const override; + ResultVal Write(u64 offset, std::size_t length, bool flush, + const u8* buffer) override; + u64 GetSize() const override; + bool SetSize(u64 size) const override; + bool Close() const override; + void Flush() const override; + +private: + u64 written = 0; + std::vector data; +}; + /** * Installs a CIA file from a specified file path. * @param path file path of the CIA file to install @@ -272,6 +291,18 @@ public: */ void GetProgramInfos(Kernel::HLERequestContext& ctx); + /** + * AM::GetProgramInfosIgnorePlatform service function + * Inputs: + * 1 : u8 Mediatype + * 2 : Total titles + * 4 : TitleIDList pointer + * 6 : TitleList pointer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ + void GetProgramInfosIgnorePlatform(Kernel::HLERequestContext& ctx); + /** * AM::DeleteUserProgram service function * Deletes a user program @@ -389,6 +420,15 @@ public: */ void NeedsCleanup(Kernel::HLERequestContext& ctx); + /** + * AM::DoCleanup service function + * Inputs: + * 1 : Media Type + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ + void DoCleanup(Kernel::HLERequestContext& ctx); + /** * AM::QueryAvailableTitleDatabase service function * Inputs: @@ -399,6 +439,42 @@ public: */ void QueryAvailableTitleDatabase(Kernel::HLERequestContext& ctx); + /** + * AM::GetPersonalizedTicketInfoList service function + * Inputs: + * 1 : Count + * 2-3 : Buffer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Out count + */ + void GetPersonalizedTicketInfoList(Kernel::HLERequestContext& ctx); + + /** + * AM::GetNumImportTitleContextsFiltered service function + * Inputs: + * 1 : Count + * 2 : Filter + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Num import titles + */ + void GetNumImportTitleContextsFiltered(Kernel::HLERequestContext& ctx); + + /** + * AM::GetImportTitleContextListFiltered service function + * Inputs: + * 1 : Count + * 2 : Media type + * 3 : filter + * 4-5 : Buffer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2 : Out count + * 3-4 : Out buffer + */ + void GetImportTitleContextListFiltered(Kernel::HLERequestContext& ctx); + /** * AM::CheckContentRights service function * Inputs: @@ -602,6 +678,25 @@ public: */ void GetMetaDataFromCia(Kernel::HLERequestContext& ctx); + /** + * AM::BeginImportTicket service function + * Inputs: + * 1 : Media type to install title to + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2-3 : TicketHandle handle for application to write to + */ + void BeginImportTicket(Kernel::HLERequestContext& ctx); + + /** + * AM::EndImportTicket service function + * Inputs: + * 1-2 : TicketHandle handle application wrote to + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ + void EndImportTicket(Kernel::HLERequestContext& ctx); + protected: std::shared_ptr am; }; diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp index 506579dc33..fed0ab912c 100644 --- a/src/core/hle/service/am/am_net.cpp +++ b/src/core/hle/service/am/am_net.cpp @@ -42,19 +42,19 @@ AM_NET::AM_NET(std::shared_ptr am) : Module::Interface(std::move(am), "a {0x001E, nullptr, "ReadTwlBackupInfo"}, {0x001F, nullptr, "DeleteAllExpiredUserPrograms"}, {0x0020, nullptr, "GetTwlArchiveResourceInfo"}, - {0x0021, nullptr, "GetPersonalizedTicketInfoList"}, + {0x0021, &AM_NET::GetPersonalizedTicketInfoList, "GetPersonalizedTicketInfoList"}, {0x0022, nullptr, "DeleteAllImportContextsFiltered"}, - {0x0023, nullptr, "GetNumImportTitleContextsFiltered"}, - {0x0024, nullptr, "GetImportTitleContextListFiltered"}, - {0x0025, nullptr, "CheckContentRights"}, + {0x0023, &AM_NET::GetNumImportTitleContextsFiltered, "GetNumImportTitleContextsFiltered"}, + {0x0024, &AM_NET::GetImportTitleContextListFiltered, "GetImportTitleContextListFiltered"}, + {0x0025, &AM_NET::CheckContentRights, "CheckContentRights"}, {0x0026, nullptr, "GetTicketLimitInfos"}, {0x0027, nullptr, "GetDemoLaunchInfos"}, {0x0028, nullptr, "ReadTwlBackupInfoEx"}, {0x0029, nullptr, "DeleteUserProgramsAtomically"}, {0x002A, nullptr, "GetNumExistingContentInfosSystem"}, {0x002B, nullptr, "ListExistingContentInfosSystem"}, - {0x002C, nullptr, "GetProgramInfosIgnorePlatform"}, - {0x002D, nullptr, "CheckContentRightsIgnorePlatform"}, + {0x002C, &AM_NET::GetProgramInfosIgnorePlatform, "GetProgramInfosIgnorePlatform"}, + {0x002D, &AM_NET::CheckContentRightsIgnorePlatform, "CheckContentRightsIgnorePlatform"}, {0x0401, nullptr, "UpdateFirmwareTo"}, {0x0402, &AM_NET::BeginImportProgram, "BeginImportProgram"}, {0x0403, nullptr, "BeginImportProgramTemporarily"}, @@ -72,7 +72,7 @@ AM_NET::AM_NET(std::shared_ptr am) : Module::Interface(std::move(am), "a {0x040F, nullptr, "UpdateFirmwareAuto"}, {0x0410, &AM_NET::DeleteProgram, "DeleteProgram"}, {0x0411, nullptr, "GetTwlProgramListForReboot"}, - {0x0412, nullptr, "GetSystemUpdaterMutex"}, + {0x0412, &AM_NET::GetSystemUpdaterMutex, "GetSystemUpdaterMutex"}, {0x0413, &AM_NET::GetMetaSizeFromCia, "GetMetaSizeFromCia"}, {0x0414, &AM_NET::GetMetaDataFromCia, "GetMetaDataFromCia"}, {0x0415, nullptr, "CheckDemoLaunchRights"}, @@ -80,9 +80,9 @@ AM_NET::AM_NET(std::shared_ptr am) : Module::Interface(std::move(am), "a {0x0417, nullptr, "PerpetuateAgbSaveData"}, {0x0418, nullptr, "BeginImportProgramForOverWrite"}, {0x0419, nullptr, "BeginImportSystemProgram"}, - {0x0801, nullptr, "BeginImportTicket"}, + {0x0801, &AM_NET::BeginImportTicket, "BeginImportTicket"}, {0x0802, nullptr, "CancelImportTicket"}, - {0x0803, nullptr, "EndImportTicket"}, + {0x0803, &AM_NET::EndImportTicket, "EndImportTicket"}, {0x0804, nullptr, "BeginImportTitle"}, {0x0805, nullptr, "StopImportTitle"}, {0x0806, nullptr, "ResumeImportTitle"}, diff --git a/src/core/hle/service/am/am_sys.cpp b/src/core/hle/service/am/am_sys.cpp index c0b394f0bf..48bc39cbb5 100644 --- a/src/core/hle/service/am/am_sys.cpp +++ b/src/core/hle/service/am/am_sys.cpp @@ -29,7 +29,7 @@ AM_SYS::AM_SYS(std::shared_ptr am) : Module::Interface(std::move(am), "a {0x0011, nullptr, "GetImportContentContexts"}, {0x0012, nullptr, "DeleteImportContentContexts"}, {0x0013, &AM_SYS::NeedsCleanup, "NeedsCleanup"}, - {0x0014, nullptr, "DoCleanup"}, + {0x0014, &AM_SYS::DoCleanup, "DoCleanup"}, {0x0015, nullptr, "DeleteAllImportContexts"}, {0x0016, nullptr, "DeleteAllTemporaryPrograms"}, {0x0017, nullptr, "ImportTwlBackupLegacy"},