diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index db3576ce4..b03e06421 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -283,6 +283,49 @@ bool CIAFile::Close() const { void CIAFile::Flush() const {} +bool InstallCIA(const std::string& path, std::function&& update_callback) { + LOG_INFO(Service_AM, "Installing %s...", path.c_str()); + + if (!FileUtil::Exists(path)) { + LOG_ERROR(Service_AM, "File %s does not exist!", path.c_str()); + return false; + } + + FileSys::CIAContainer container; + if (container.Load(path) == Loader::ResultStatus::Success) { + Service::AM::CIAFile installFile( + Service::AM::GetTitleMediaType(container.GetTitleMetadata().GetTitleID())); + + FileUtil::IOFile file(path, "rb"); + if (!file.IsOpen()) + return false; + + std::array buffer; + size_t total_bytes_read = 0; + while (total_bytes_read != file.GetSize()) { + size_t bytes_read = file.ReadBytes(buffer.data(), buffer.size()); + auto result = installFile.Write(static_cast(total_bytes_read), bytes_read, true, + static_cast(buffer.data())); + + if (update_callback) + update_callback(total_bytes_read, file.GetSize()); + if (result.Failed()) { + LOG_ERROR(Service_AM, "CIA file installation aborted with error code %08x", + result.Code()); + return false; + } + total_bytes_read += bytes_read; + } + installFile.Close(); + + LOG_INFO(Service_AM, "Installed %s successfully.", path.c_str()); + return true; + } + + LOG_ERROR(Service_AM, "CIA file %s is invalid!", path.c_str()); + return false; +} + Service::FS::MediaType GetTitleMediaType(u64 titleId) { u16 platform = static_cast(titleId >> 48); u16 category = static_cast((titleId >> 32) & 0xFFFF); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 8199046a7..7deb09113 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "common/common_types.h" #include "core/file_sys/cia_container.h" @@ -41,6 +42,9 @@ enum class CIAInstallState : u32 { ContentWritten, }; +// Progress callback for InstallCIA, recieves bytes written and total bytes +using ProgressCallback = void(size_t, size_t); + // A file handled returned for CIAs to be written into and subsequently installed. class CIAFile final : public FileSys::FileBackend { public: @@ -73,6 +77,15 @@ private: Service::FS::MediaType media_type; }; +/** + * Installs a CIA file from a specified file path. + * @param path file path of the CIA file to install + * @param update_callback callback function called during filesystem write + * @returns bool whether the install was successful + */ +bool InstallCIA(const std::string& path, + std::function&& update_callback = nullptr); + /** * Get the mediatype for an installed title * @param titleId the installed title ID