mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-04-04 20:27:44 -06:00
core: Implement AM:CancelImportProgram (#1535)
This commit is contained in:
parent
9ed8cdccd4
commit
f60f3eed1f
@ -850,19 +850,25 @@ bool CIAFile::Close() {
|
||||
current_content_install_result.type = InstallResult::Type::NONE;
|
||||
}
|
||||
|
||||
bool complete =
|
||||
from_cdn ? is_done
|
||||
: (install_state >= CIAInstallState::TMDLoaded &&
|
||||
content_written.size() == container.GetTitleMetadata().GetContentCount() &&
|
||||
std::all_of(content_written.begin(), content_written.end(),
|
||||
[this, i = 0](auto& bytes_written) mutable {
|
||||
return bytes_written >=
|
||||
container.GetContentSize(static_cast<u16>(i++));
|
||||
}));
|
||||
bool complete;
|
||||
|
||||
if (is_cancel) {
|
||||
complete = false;
|
||||
} else {
|
||||
complete =
|
||||
from_cdn ? is_done
|
||||
: (install_state >= CIAInstallState::TMDLoaded &&
|
||||
content_written.size() == container.GetTitleMetadata().GetContentCount() &&
|
||||
std::all_of(content_written.begin(), content_written.end(),
|
||||
[this, i = 0](auto& bytes_written) mutable {
|
||||
return bytes_written >=
|
||||
container.GetContentSize(static_cast<u16>(i++));
|
||||
}));
|
||||
}
|
||||
|
||||
// Install aborted
|
||||
if (!complete) {
|
||||
LOG_ERROR(Service_AM, "CIAFile closed prematurely, aborting install...");
|
||||
LOG_ERROR(Service_AM, "CIAFile closed prematurely or cancelled, aborting install...");
|
||||
if (!is_additional_content) {
|
||||
// Only delete the content folder as there may be user save data in the title folder.
|
||||
const std::string title_content_path =
|
||||
@ -3214,97 +3220,6 @@ void Module::Interface::CheckContentRightsIgnorePlatform(Kernel::HLERequestConte
|
||||
LOG_DEBUG(Service_AM, "tid={:016x}, content_index={}", tid, content_index);
|
||||
}
|
||||
|
||||
void Module::Interface::BeginImportProgram(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||
|
||||
if (am->cia_installing) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(Result(ErrCodes::InvalidImportState, ErrorModule::AM, ErrorSummary::InvalidState,
|
||||
ErrorLevel::Permanent));
|
||||
return;
|
||||
}
|
||||
|
||||
// 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<Service::FS::File>(
|
||||
am->system.Kernel(), std::make_unique<CIAFile>(am->system, media_type), cia_path);
|
||||
|
||||
am->cia_installing = true;
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
rb.Push(ResultSuccess); // No error
|
||||
rb.PushCopyObjects(file->Connect());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) media_type={}", media_type);
|
||||
}
|
||||
|
||||
void Module::Interface::BeginImportProgramTemporarily(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
if (am->cia_installing) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(Result(ErrCodes::InvalidImportState, 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 = {};
|
||||
std::shared_ptr<Service::FS::File> file;
|
||||
{
|
||||
auto cia_file = std::make_unique<CIAFile>(am->system, FS::MediaType::NAND);
|
||||
|
||||
AuthorizeCIAFileDecryption(cia_file.get(), ctx);
|
||||
|
||||
file =
|
||||
std::make_shared<Service::FS::File>(am->system.Kernel(), std::move(cia_file), cia_path);
|
||||
}
|
||||
am->cia_installing = true;
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
rb.Push(ResultSuccess); // No error
|
||||
rb.PushCopyObjects(file->Connect());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED)");
|
||||
}
|
||||
|
||||
void Module::Interface::EndImportProgram(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
[[maybe_unused]] const auto cia = rp.PopObject<Kernel::ClientSession>();
|
||||
|
||||
LOG_DEBUG(Service_AM, "");
|
||||
|
||||
am->ScanForAllTitles();
|
||||
|
||||
am->cia_installing = false;
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Module::Interface::EndImportProgramWithoutCommit(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
[[maybe_unused]] const auto cia = rp.PopObject<Kernel::ClientSession>();
|
||||
|
||||
// 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(ResultSuccess);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED)");
|
||||
}
|
||||
|
||||
void Module::Interface::CommitImportPrograms(Kernel::HLERequestContext& ctx) {
|
||||
CommitImportTitlesImpl(ctx, false, false);
|
||||
}
|
||||
|
||||
/// Wraps all File operations to allow adding an offset to them.
|
||||
class AMFileWrapper : public FileSys::FileBackend {
|
||||
public:
|
||||
@ -3420,6 +3335,123 @@ ResultVal<T*> GetFileBackendFromSession(std::shared_ptr<Kernel::ClientSession> f
|
||||
return Kernel::ResultNotImplemented;
|
||||
}
|
||||
|
||||
void Module::Interface::BeginImportProgram(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
auto media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
|
||||
|
||||
if (am->cia_installing) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(Result(ErrCodes::InvalidImportState, ErrorModule::AM, ErrorSummary::InvalidState,
|
||||
ErrorLevel::Permanent));
|
||||
return;
|
||||
}
|
||||
|
||||
// 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<Service::FS::File>(
|
||||
am->system.Kernel(), std::make_unique<CIAFile>(am->system, media_type), cia_path);
|
||||
|
||||
am->cia_installing = true;
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
rb.Push(ResultSuccess); // No error
|
||||
rb.PushCopyObjects(file->Connect());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) media_type={}", media_type);
|
||||
}
|
||||
|
||||
void Module::Interface::BeginImportProgramTemporarily(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
if (am->cia_installing) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(Result(ErrCodes::InvalidImportState, 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 = {};
|
||||
std::shared_ptr<Service::FS::File> file;
|
||||
{
|
||||
auto cia_file = std::make_unique<CIAFile>(am->system, FS::MediaType::NAND);
|
||||
|
||||
AuthorizeCIAFileDecryption(cia_file.get(), ctx);
|
||||
|
||||
file =
|
||||
std::make_shared<Service::FS::File>(am->system.Kernel(), std::move(cia_file), cia_path);
|
||||
}
|
||||
am->cia_installing = true;
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
rb.Push(ResultSuccess); // No error
|
||||
rb.PushCopyObjects(file->Connect());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED)");
|
||||
}
|
||||
|
||||
void Module::Interface::CancelImportProgram(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto cia = rp.PopObject<Kernel::ClientSession>();
|
||||
|
||||
LOG_DEBUG(Service_AM, "");
|
||||
|
||||
auto cia_file = GetFileBackendFromSession<CIAFile>(cia);
|
||||
if (cia_file.Succeeded()) {
|
||||
cia_file.Unwrap()->Cancel();
|
||||
}
|
||||
|
||||
am->cia_installing = false;
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Module::Interface::EndImportProgram(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto cia = rp.PopObject<Kernel::ClientSession>();
|
||||
|
||||
LOG_DEBUG(Service_AM, "");
|
||||
|
||||
auto cia_file = GetFileBackendFromSession<CIAFile>(cia);
|
||||
if (cia_file.Succeeded()) {
|
||||
cia_file.Unwrap()->Close();
|
||||
}
|
||||
|
||||
am->ScanForAllTitles();
|
||||
|
||||
am->cia_installing = false;
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Module::Interface::EndImportProgramWithoutCommit(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto cia = rp.PopObject<Kernel::ClientSession>();
|
||||
|
||||
auto cia_file = GetFileBackendFromSession<CIAFile>(cia);
|
||||
if (cia_file.Succeeded()) {
|
||||
cia_file.Unwrap()->Close();
|
||||
}
|
||||
|
||||
// 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(ResultSuccess);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED)");
|
||||
}
|
||||
|
||||
void Module::Interface::CommitImportPrograms(Kernel::HLERequestContext& ctx) {
|
||||
CommitImportTitlesImpl(ctx, false, false);
|
||||
}
|
||||
|
||||
void Module::Interface::GetProgramInfoFromCia(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
[[maybe_unused]] const auto media_type = static_cast<FS::MediaType>(rp.Pop<u8>());
|
||||
|
||||
@ -224,6 +224,11 @@ public:
|
||||
is_done = true;
|
||||
}
|
||||
|
||||
void Cancel() {
|
||||
is_cancel = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
const std::vector<InstallResult>& GetInstallResults() const {
|
||||
return install_results;
|
||||
}
|
||||
@ -237,6 +242,7 @@ private:
|
||||
bool decryption_authorized;
|
||||
bool is_done = false;
|
||||
bool is_closed = false;
|
||||
bool is_cancel = false;
|
||||
bool is_additional_content = false;
|
||||
|
||||
// Whether it's installing an update, and what step of installation it is at
|
||||
@ -815,6 +821,17 @@ public:
|
||||
*/
|
||||
void BeginImportProgramTemporarily(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* AM::CancelImportProgram service function
|
||||
* Cancel importing a CTR Installable Archive
|
||||
* Inputs:
|
||||
* 0 : Command header (0x04040002)
|
||||
* 1-2 : CIAFile handle application wrote to
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
*/
|
||||
void CancelImportProgram(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* AM::EndImportProgram service function
|
||||
* Finish importing from a CTR Installable Archive
|
||||
|
||||
@ -58,7 +58,7 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
||||
{0x0401, nullptr, "UpdateFirmwareTo"},
|
||||
{0x0402, &AM_NET::BeginImportProgram, "BeginImportProgram"},
|
||||
{0x0403, &AM_NET::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"},
|
||||
{0x0404, nullptr, "CancelImportProgram"},
|
||||
{0x0404, &AM_NET::CancelImportProgram, "CancelImportProgram"},
|
||||
{0x0405, &AM_NET::EndImportProgram, "EndImportProgram"},
|
||||
{0x0406, &AM_NET::EndImportProgramWithoutCommit, "EndImportProgramWithoutCommit"},
|
||||
{0x0407, &AM_NET::CommitImportPrograms, "CommitImportPrograms"},
|
||||
|
||||
@ -58,7 +58,7 @@ AM_U::AM_U(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "am:u"
|
||||
{0x0401, nullptr, "UpdateFirmwareTo"},
|
||||
{0x0402, &AM_U::BeginImportProgram, "BeginImportProgram"},
|
||||
{0x0403, &AM_U::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"},
|
||||
{0x0404, nullptr, "CancelImportProgram"},
|
||||
{0x0404, &AM_U::CancelImportProgram, "CancelImportProgram"},
|
||||
{0x0405, &AM_U::EndImportProgram, "EndImportProgram"},
|
||||
{0x0406, &AM_U::EndImportProgramWithoutCommit, "EndImportProgramWithoutCommit"},
|
||||
{0x0407, &AM_U::CommitImportPrograms, "CommitImportPrograms"},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user