From 59575d5cae573b57eb9e1e611e2c25ceb935ec0a Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Thu, 18 Jan 2018 16:54:34 -0300
Subject: [PATCH] Stub PopLaunchParameter and implement Buffer C Descriptors
 reading on hle_ipc (#96)

* Stub PopLaunchParameter and implement Buffer C Descriptors reading

* Address PR feedback

* Ensure we push a u64 not a size_t

* Fix formatting
---
 src/core/hle/ipc.h                    |  4 ++
 src/core/hle/ipc_helpers.h            |  4 ++
 src/core/hle/kernel/hle_ipc.cpp       | 34 ++++++++---
 src/core/hle/kernel/hle_ipc.h         |  6 ++
 src/core/hle/service/am/applet_oe.cpp | 86 +++++++++++++++++++++++++++
 5 files changed, 127 insertions(+), 7 deletions(-)

diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 1840fac12c..0dcaede678 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -133,6 +133,10 @@ struct BufferDescriptorC {
         address |= static_cast<VAddr>(address_bits_32_47) << 32;
         return address;
     }
+
+    u64 Size() const {
+        return static_cast<u64>(size);
+    }
 };
 static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorrect");
 
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 25530a3c82..4c9b0de288 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -54,6 +54,10 @@ public:
     unsigned GetCurrentOffset() const {
         return static_cast<unsigned>(index);
     }
+
+    void SetCurrentOffset(unsigned offset) {
+        index = static_cast<ptrdiff_t>(offset);
+    }
 };
 
 class RequestBuilder : public RequestHelperBase {
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index ac62a0d5af..73bb6a8be6 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -81,13 +81,8 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
     for (unsigned i = 0; i < command_header->num_buf_w_descriptors; ++i) {
         buffer_w_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
     }
-    if (command_header->buf_c_descriptor_flags !=
-        IPC::CommandHeader::BufferDescriptorCFlag::Disabled) {
-        if (command_header->buf_c_descriptor_flags !=
-            IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) {
-            UNIMPLEMENTED();
-        }
-    }
+
+    buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size;
 
     // Padding to align to 16 bytes
     rp.AlignWithPadding();
@@ -117,6 +112,31 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
         ASSERT(data_payload_header->magic == Common::MakeMagic('S', 'F', 'C', 'O'));
     }
 
+    rp.SetCurrentOffset(buffer_c_offset);
+
+    // For Inline buffers, the response data is written directly to buffer_c_offset
+    // and in this case we don't have any BufferDescriptorC on the request.
+    if (command_header->buf_c_descriptor_flags >
+        IPC::CommandHeader::BufferDescriptorCFlag::InlineDescriptor) {
+        if (command_header->buf_c_descriptor_flags ==
+            IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) {
+            buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
+        } else {
+            unsigned num_buf_c_descriptors =
+                static_cast<unsigned>(command_header->buf_c_descriptor_flags.Value()) - 2;
+
+            // This is used to detect possible underflows, in case something is broken
+            // with the two ifs above and the flags value is == 0 || == 1.
+            ASSERT(num_buf_c_descriptors < 14);
+
+            for (unsigned i = 0; i < num_buf_c_descriptors; ++i) {
+                buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
+            }
+        }
+    }
+
+    rp.SetCurrentOffset(data_payload_offset);
+
     command = rp.Pop<u32_le>();
     rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
 }
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 6dceb766db..80fa48d7fa 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -143,6 +143,10 @@ public:
         return buffer_b_desciptors;
     }
 
+    const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
+        return buffer_c_desciptors;
+    }
+
     const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
         return domain_message_header;
     }
@@ -200,8 +204,10 @@ private:
     std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
     std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
     std::vector<IPC::BufferDescriptorABW> buffer_w_desciptors;
+    std::vector<IPC::BufferDescriptorC> buffer_c_desciptors;
 
     unsigned data_payload_offset{};
+    unsigned buffer_c_offset{};
     u32_le command{};
 };
 
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index b360e7e5fe..0d7f9c03dc 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -201,10 +201,76 @@ private:
     Kernel::SharedPtr<Kernel::Event> event;
 };
 
+class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
+public:
+    explicit IStorageAccessor(std::vector<u8> buffer)
+        : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
+        static const FunctionInfo functions[] = {
+            {0, &IStorageAccessor::GetSize, "GetSize"},
+            {11, &IStorageAccessor::Read, "Read"},
+        };
+        RegisterHandlers(functions);
+    }
+
+private:
+    std::vector<u8> buffer;
+
+    void GetSize(Kernel::HLERequestContext& ctx) {
+        IPC::RequestBuilder rb{ctx, 4};
+
+        rb.Push(RESULT_SUCCESS);
+        rb.Push(static_cast<u64>(buffer.size()));
+
+        LOG_DEBUG(Service, "called");
+    }
+
+    void Read(Kernel::HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+
+        u64 offset = rp.Pop<u64>();
+
+        const auto& output_buffer = ctx.BufferDescriptorC()[0];
+
+        ASSERT(offset + output_buffer.Size() <= buffer.size());
+
+        Memory::WriteBlock(output_buffer.Address(), buffer.data() + offset, output_buffer.Size());
+
+        IPC::RequestBuilder rb{ctx, 2};
+
+        rb.Push(RESULT_SUCCESS);
+
+        LOG_DEBUG(Service, "called");
+    }
+};
+
+class IStorage final : public ServiceFramework<IStorage> {
+public:
+    explicit IStorage(std::vector<u8> buffer)
+        : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
+        static const FunctionInfo functions[] = {
+            {0, &IStorage::Open, "Open"},
+        };
+        RegisterHandlers(functions);
+    }
+
+private:
+    std::vector<u8> buffer;
+
+    void Open(Kernel::HLERequestContext& ctx) {
+        IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
+
+        rb.Push(RESULT_SUCCESS);
+        rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
+
+        LOG_DEBUG(Service, "called");
+    }
+};
+
 class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
 public:
     IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
         static const FunctionInfo functions[] = {
+            {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
             {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
             {66, &IApplicationFunctions::InitializeGamePlayRecording,
              "InitializeGamePlayRecording"},
@@ -215,6 +281,26 @@ public:
     }
 
 private:
+    void PopLaunchParameter(Kernel::HLERequestContext& ctx) {
+        constexpr u8 data[0x88] = {
+            0xca, 0x97, 0x94, 0xc7, // Magic
+            1,    0,    0,    0,    // IsAccountSelected (bool)
+            1,    0,    0,    0,    // User Id (word 0)
+            0,    0,    0,    0,    // User Id (word 1)
+            0,    0,    0,    0,    // User Id (word 2)
+            0,    0,    0,    0     // User Id (word 3)
+        };
+
+        std::vector<u8> buffer(data, data + sizeof(data));
+
+        IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
+
+        rb.Push(RESULT_SUCCESS);
+        rb.PushIpcInterface<AM::IStorage>(buffer);
+
+        LOG_DEBUG(Service, "called");
+    }
+
     void SetTerminateResult(Kernel::HLERequestContext& ctx) {
         // Takes an input u32 Result, no output.
         // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak.