mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-06-06 07:55:05 -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;
|
current_content_install_result.type = InstallResult::Type::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool complete =
|
bool complete;
|
||||||
from_cdn ? is_done
|
|
||||||
: (install_state >= CIAInstallState::TMDLoaded &&
|
if (is_cancel) {
|
||||||
content_written.size() == container.GetTitleMetadata().GetContentCount() &&
|
complete = false;
|
||||||
std::all_of(content_written.begin(), content_written.end(),
|
} else {
|
||||||
[this, i = 0](auto& bytes_written) mutable {
|
complete =
|
||||||
return bytes_written >=
|
from_cdn ? is_done
|
||||||
container.GetContentSize(static_cast<u16>(i++));
|
: (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
|
// Install aborted
|
||||||
if (!complete) {
|
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) {
|
if (!is_additional_content) {
|
||||||
// Only delete the content folder as there may be user save data in the title folder.
|
// Only delete the content folder as there may be user save data in the title folder.
|
||||||
const std::string title_content_path =
|
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);
|
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.
|
/// Wraps all File operations to allow adding an offset to them.
|
||||||
class AMFileWrapper : public FileSys::FileBackend {
|
class AMFileWrapper : public FileSys::FileBackend {
|
||||||
public:
|
public:
|
||||||
@ -3420,6 +3335,123 @@ ResultVal<T*> GetFileBackendFromSession(std::shared_ptr<Kernel::ClientSession> f
|
|||||||
return Kernel::ResultNotImplemented;
|
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) {
|
void Module::Interface::GetProgramInfoFromCia(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
[[maybe_unused]] const auto media_type = static_cast<FS::MediaType>(rp.Pop<u8>());
|
[[maybe_unused]] const auto media_type = static_cast<FS::MediaType>(rp.Pop<u8>());
|
||||||
|
|||||||
@ -224,6 +224,11 @@ public:
|
|||||||
is_done = true;
|
is_done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Cancel() {
|
||||||
|
is_cancel = true;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<InstallResult>& GetInstallResults() const {
|
const std::vector<InstallResult>& GetInstallResults() const {
|
||||||
return install_results;
|
return install_results;
|
||||||
}
|
}
|
||||||
@ -237,6 +242,7 @@ private:
|
|||||||
bool decryption_authorized;
|
bool decryption_authorized;
|
||||||
bool is_done = false;
|
bool is_done = false;
|
||||||
bool is_closed = false;
|
bool is_closed = false;
|
||||||
|
bool is_cancel = false;
|
||||||
bool is_additional_content = false;
|
bool is_additional_content = false;
|
||||||
|
|
||||||
// Whether it's installing an update, and what step of installation it is at
|
// Whether it's installing an update, and what step of installation it is at
|
||||||
@ -815,6 +821,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
void BeginImportProgramTemporarily(Kernel::HLERequestContext& ctx);
|
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
|
* AM::EndImportProgram service function
|
||||||
* Finish importing from a CTR Installable Archive
|
* 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"},
|
{0x0401, nullptr, "UpdateFirmwareTo"},
|
||||||
{0x0402, &AM_NET::BeginImportProgram, "BeginImportProgram"},
|
{0x0402, &AM_NET::BeginImportProgram, "BeginImportProgram"},
|
||||||
{0x0403, &AM_NET::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"},
|
{0x0403, &AM_NET::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"},
|
||||||
{0x0404, nullptr, "CancelImportProgram"},
|
{0x0404, &AM_NET::CancelImportProgram, "CancelImportProgram"},
|
||||||
{0x0405, &AM_NET::EndImportProgram, "EndImportProgram"},
|
{0x0405, &AM_NET::EndImportProgram, "EndImportProgram"},
|
||||||
{0x0406, &AM_NET::EndImportProgramWithoutCommit, "EndImportProgramWithoutCommit"},
|
{0x0406, &AM_NET::EndImportProgramWithoutCommit, "EndImportProgramWithoutCommit"},
|
||||||
{0x0407, &AM_NET::CommitImportPrograms, "CommitImportPrograms"},
|
{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"},
|
{0x0401, nullptr, "UpdateFirmwareTo"},
|
||||||
{0x0402, &AM_U::BeginImportProgram, "BeginImportProgram"},
|
{0x0402, &AM_U::BeginImportProgram, "BeginImportProgram"},
|
||||||
{0x0403, &AM_U::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"},
|
{0x0403, &AM_U::BeginImportProgramTemporarily, "BeginImportProgramTemporarily"},
|
||||||
{0x0404, nullptr, "CancelImportProgram"},
|
{0x0404, &AM_U::CancelImportProgram, "CancelImportProgram"},
|
||||||
{0x0405, &AM_U::EndImportProgram, "EndImportProgram"},
|
{0x0405, &AM_U::EndImportProgram, "EndImportProgram"},
|
||||||
{0x0406, &AM_U::EndImportProgramWithoutCommit, "EndImportProgramWithoutCommit"},
|
{0x0406, &AM_U::EndImportProgramWithoutCommit, "EndImportProgramWithoutCommit"},
|
||||||
{0x0407, &AM_U::CommitImportPrograms, "CommitImportPrograms"},
|
{0x0407, &AM_U::CommitImportPrograms, "CommitImportPrograms"},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user