From 56e906f1e3a63525dceffe41717b566674325f34 Mon Sep 17 00:00:00 2001
From: shinyquagsire23 <mtinc2@gmail.com>
Date: Wed, 15 Nov 2017 12:01:49 -0700
Subject: [PATCH] Services/AM: Add InstallCIA function for frontends

---
 src/core/hle/service/am/am.cpp | 43 ++++++++++++++++++++++++++++++++++
 src/core/hle/service/am/am.h   | 13 ++++++++++
 2 files changed, 56 insertions(+)

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<ProgressCallback>&& 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<u8, 0x10000> 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<u64>(total_bytes_read), bytes_read, true,
+                                            static_cast<u8*>(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<u16>(titleId >> 48);
     u16 category = static_cast<u16>((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 <functional>
 #include <string>
 #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<ProgressCallback>&& update_callback = nullptr);
+
 /**
  * Get the mediatype for an installed title
  * @param titleId the installed title ID