diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 286a40711..05be6ef32 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1038,6 +1038,33 @@ void Module::Interface::BeginImportProgram(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) media_type={}", static_cast(media_type)); } +void Module::Interface::BeginImportProgramTemporarily(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0403, 0, 0); // 0x04030000 + + if (am->cia_installing) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrCodes::CIACurrentlyInstalling, ErrorModule::AM, + ErrorSummary::InvalidState, ErrorLevel::Permanent)); + return; + } + + // Note: This function should register the title in the temp_i.db database, but we can get away + // with not doing that because we traverse the file system to detect installed titles. + // Create our CIAFile handle for the app to write to, and while the app writes Citra will store + // contents out to sdmc/nand + const FileSys::Path cia_path = {}; + auto file = std::make_shared(std::make_unique(FS::MediaType::NAND), + cia_path); + + am->cia_installing = true; + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); // No error + rb.PushCopyObjects(file->Connect()); + + LOG_WARNING(Service_AM, "(STUBBED)"); +} + void Module::Interface::EndImportProgram(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0405, 0, 2); // 0x04050002 auto cia = rp.PopObject(); @@ -1049,6 +1076,35 @@ void Module::Interface::EndImportProgram(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void Module::Interface::EndImportProgramWithoutCommit(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0406, 0, 2); // 0x04060002 + auto cia = rp.PopObject(); + + // Note: This function is basically a no-op for us since we don't use title.db or ticket.db + // files to keep track of installed titles. + am->ScanForAllTitles(); + + am->cia_installing = false; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void Module::Interface::CommitImportPrograms(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0407, 3, 2); // 0x040700C2 + auto media_type = static_cast(rp.Pop()); + u32 title_count = rp.Pop(); + u8 database = rp.Pop(); + auto buffer = rp.PopMappedBuffer(); + + // Note: This function is basically a no-op for us since we don't use title.db or ticket.db + // files to keep track of installed titles. + am->ScanForAllTitles(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(buffer); +} + /// Wraps all File operations to allow adding an offset to them. class AMFileWrapper : public FileSys::FileBackend { public: diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index cb7dff745..dc10af0db 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -384,6 +384,17 @@ public: */ void BeginImportProgram(Kernel::HLERequestContext& ctx); + /** + * AM::BeginImportProgramTemporarily service function + * Begin importing from a CTR Installable Archive into the temporary title database + * Inputs: + * 0 : Command header (0x04030000) + * Outputs: + * 1 : Result, 0 on success, otherwise error code + * 2-3 : CIAFile handle for application to write to + */ + void BeginImportProgramTemporarily(Kernel::HLERequestContext& ctx); + /** * AM::EndImportProgram service function * Finish importing from a CTR Installable Archive @@ -395,6 +406,32 @@ public: */ void EndImportProgram(Kernel::HLERequestContext& ctx); + /** + * AM::EndImportProgramWithoutCommit service function + * Finish importing from a CTR Installable Archive + * Inputs: + * 0 : Command header (0x04060002) + * 1-2 : CIAFile handle application wrote to + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ + void EndImportProgramWithoutCommit(Kernel::HLERequestContext& ctx); + + /** + * AM::CommitImportPrograms service function + * Commits changes from the temporary title database to the real title database (title.db). + * This is a no-op for us, we don't use title.db + * Inputs: + * 0 : Command header (0x040700C2) + * 1 : Media type + * 2 : Title count + * 3 : Database type + * 4-5 : Title list buffer + * Outputs: + * 1 : Result, 0 on success, otherwise error code + */ + void CommitImportPrograms(Kernel::HLERequestContext& ctx); + /** * AM::GetProgramInfoFromCia service function * Get TitleInfo from a CIA file handle diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp index 7fa4a38c0..840860ec0 100644 --- a/src/core/hle/service/am/am_u.cpp +++ b/src/core/hle/service/am/am_u.cpp @@ -55,11 +55,11 @@ AM_U::AM_U(std::shared_ptr am) : Module::Interface(std::move(am), "am:u" {0x002D00C0, nullptr, "CheckContentRightsIgnorePlatform"}, {0x04010080, nullptr, "UpdateFirmwareTo"}, {0x04020040, &AM_U::BeginImportProgram, "BeginImportProgram"}, - {0x04030000, nullptr, "BeginImportProgramTemporarily"}, + {0x04030000, &AM_U::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"}, {0x04040002, nullptr, "CancelImportProgram"}, {0x04050002, &AM_U::EndImportProgram, "EndImportProgram"}, - {0x04060002, nullptr, "EndImportProgramWithoutCommit"}, - {0x040700C2, nullptr, "CommitImportPrograms"}, + {0x04060002, &AM_U::EndImportProgramWithoutCommit, "EndImportProgramWithoutCommit"}, + {0x040700C2, &AM_U::CommitImportPrograms, "CommitImportPrograms"}, {0x04080042, &AM_U::GetProgramInfoFromCia, "GetProgramInfoFromCia"}, {0x04090004, &AM_U::GetSystemMenuDataFromCia, "GetSystemMenuDataFromCia"}, {0x040A0002, &AM_U::GetDependencyListFromCia, "GetDependencyListFromCia"},