/*
 * Copyright (c) Atmosphère-NX
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
/* Serialization classes. */
namespace ams::sf {
    namespace impl {
        struct ProcessIdHolder {
            os::ProcessId process_id;
            constexpr explicit operator os::ProcessId() const { return this->process_id; }
            constexpr os::ProcessId GetValue() const { return this->process_id; }
            constexpr void SetValue(const os::ProcessId &p) { this->process_id = p; }
        };
    }
    struct ClientProcessId : public impl::ProcessIdHolder {};
    static_assert(std::is_trivial::value && sizeof(ClientProcessId) == sizeof(os::ProcessId), "ClientProcessId");
    struct ClientAppletResourceUserId : public impl::ProcessIdHolder {};
    static_assert(std::is_trivial::value && sizeof(ClientAppletResourceUserId ) == sizeof(os::ProcessId), "ClientAppletResourceUserId");
    namespace impl {
        constexpr inline Result MarshalProcessId(ClientProcessId &client, const os::ProcessId &client_process_id) {
            client.SetValue(client_process_id);
            return ResultSuccess();
        }
        constexpr inline Result MarshalProcessId(ClientAppletResourceUserId &client, const os::ProcessId &client_process_id) {
            if (client.GetValue() != client_process_id && client.GetValue() != os::ProcessId{}) {
                return sf::ResultPreconditionViolation();
            }
            return ResultSuccess();
        }
    }
    namespace impl {
        struct OutObjectTag{};
        template
        class InOutObjectHolder;
    }
    template requires std::derived_from
    class Out> : public impl::OutObjectTag {
        template
        friend class Out;
        public:
            using Interface = ServiceImpl;
        private:
            impl::SharedPointerBase *m_out;
            cmif::DomainObjectId *m_object_id;
        private:
            Out(impl::SharedPointerBase *p, cmif::DomainObjectId *o = nullptr) : m_out(p), m_object_id(o) { /* ... */ }
        public:
            Out(const Out &rhs) : m_out(rhs.m_out), m_object_id(rhs.m_object_id) { /* ... */ }
            Out(SharedPointer *out, cmif::DomainObjectId *o = nullptr) : m_out(std::addressof(out->m_base)), m_object_id(o) { /* .... */ }
            template requires std::derived_from
            Out(Out> out) : m_out(out.m_out), m_object_id(out.m_object_id) { /* ... */ }
            template requires std::derived_from
            Out(SharedPointer *out, cmif::DomainObjectId *o = nullptr) : m_out(std::addressof(out->m_base)), m_object_id(o)  { /* .... */ }
            void SetValue(SharedPointer s, cmif::DomainObjectId new_object_id = cmif::InvalidDomainObjectId) {
                *m_out = std::move(s.m_base);
                if (new_object_id != cmif::InvalidDomainObjectId) {
                    AMS_ABORT_UNLESS(m_object_id != nullptr);
                    *m_object_id = new_object_id;
                }
            }
            template requires std::derived_from
            void SetValue(SharedPointer s, cmif::DomainObjectId new_object_id = cmif::InvalidDomainObjectId) {
                *m_out = std::move(s.m_base);
                if (new_object_id != cmif::InvalidDomainObjectId) {
                    AMS_ABORT_UNLESS(m_object_id != nullptr);
                    *m_object_id = new_object_id;
                }
            }
            class DerefProxy {
                template
                friend class Out;
                private:
                    Out &m_target;
                private:
                    explicit DerefProxy(Out &t) : m_target(t) { /* ... */ }
                public:
                    DerefProxy &operator=(SharedPointer p) {
                        m_target.SetValue(std::move(p));
                        return *this;
                    }
                    template requires std::derived_from
                    DerefProxy &operator=(SharedPointer p) {
                        m_target.SetValue(std::move(p));
                        return *this;
                    }
            };
            DerefProxy operator *() {
                return DerefProxy(*this);
            }
            template
            Out> DownCast() {
                return Out>(m_out, m_object_id);
            }
    };
}
namespace ams::sf::impl {
    /* Machinery for filtering type lists. */
    template
    struct TupleCat;
    template
    struct TupleCat, std::tuple> {
        using type = std::tuple;
    };
    template typename Cond>
    struct TupleFilter {
        private:
            template
            struct ImplType;
            template
            struct ImplType> {
                using type = Res;
            };
            template
            struct ImplType> {
                using type = typename std::conditional::value,
                                                       typename ImplType>::type, std::tuple>::type,
                                                       typename ImplType>::type
                                                      >::type;
            };
        public:
            template
            using FilteredType = typename ImplType, T>::type;
    };
    enum class ArgumentType {
        InData,
        OutData,
        Buffer,
        InHandle,
        OutHandle,
        InObject,
        OutObject,
    };
    template
    struct IsInObject : public std::false_type{};
    template
    struct IsInObject> : public std::true_type {
        static_assert(std::is_base_of::value, "Invalid IsInObject>");
    };
    template
    constexpr inline ArgumentType GetArgumentType = [] {
        static_assert(!std::same_as);
        static_assert(!std::same_as>);
        if constexpr (sf::IsBuffer) {
            return ArgumentType::Buffer;
        } else if constexpr (IsInObject::value) {
            return ArgumentType::InObject;
        } else if constexpr (std::is_base_of::value) {
            return ArgumentType::OutObject;
        } else if constexpr (std::same_as || std::same_as) {
            return ArgumentType::InHandle;
        } else if constexpr (std::same_as || std::same_as) {
            return ArgumentType::OutHandle;
        } else if constexpr (std::is_base_of::value) {
            return ArgumentType::OutData;
        } else if constexpr (std::is_trivial::value && !std::is_pointer::value) {
            return ArgumentType::InData;
        } else if constexpr (std::is_same::value) {
            return ArgumentType::InData;
        } else {
            static_assert(!std::is_same::value, "Invalid ArgumentType");
        }
    }();
    template
    struct ArgumentTypeFilter : public std::bool_constant == ArgT>{};
    template
    struct TypeEqualityFilter : public std::bool_constant::value>{};
    /* Argument type filters. */
    template
    using InDataFilter    = ArgumentTypeFilter;
    template
    using OutDataFilter   = ArgumentTypeFilter;
    template
    using BufferFilter    = ArgumentTypeFilter;
    template
    using InHandleFilter  = ArgumentTypeFilter;
    template
    using OutHandleFilter = ArgumentTypeFilter;
    template
    using InObjectFilter  = ArgumentTypeFilter;
    template
    using OutObjectFilter = ArgumentTypeFilter;
    template
    struct ProcessIdHolderFilter : public std::bool_constant::value>{};
    /* Handle kind filters. */
    template
    using InMoveHandleFilter = TypeEqualityFilter;
    template
    using InCopyHandleFilter = TypeEqualityFilter;
    template
    using OutMoveHandleFilter = TypeEqualityFilter>;
    template
    using OutCopyHandleFilter = TypeEqualityFilter>;
    template
    struct BufferAttributeArrayGetter;
    template
    struct BufferAttributeArrayGetter> {
        static constexpr std::array value = { BufferAttributes..., };
    };
    template<>
    struct BufferAttributeArrayGetter> {
        static constexpr std::array value{};
    };
    template
    struct BufferAttributeCounter {
        template
        static constexpr size_t GetCount(const std::array &attributes_array) {
            size_t count = 0;
            for (size_t i = 0; i < Size; i++) {
                if (Predicate(attributes_array[i])) {
                    count++;
                }
            }
            return count;
        }
    };
    NX_CONSTEXPR size_t InHipcMapAliasBufferPredicate(const u32 attribute) {
        if ((attribute & SfBufferAttr_In) && !(attribute & SfBufferAttr_Out)) {
            return (attribute & SfBufferAttr_HipcMapAlias) || (attribute & SfBufferAttr_HipcAutoSelect);
        } else {
            return false;
        }
    }
    NX_CONSTEXPR size_t OutHipcMapAliasBufferPredicate(const u32 attribute) {
        if (!(attribute & SfBufferAttr_In) && (attribute & SfBufferAttr_Out)) {
            return (attribute & SfBufferAttr_HipcMapAlias) || (attribute & SfBufferAttr_HipcAutoSelect);
        } else {
            return false;
        }
    }
    NX_CONSTEXPR size_t InHipcPointerBufferPredicate(const u32 attribute) {
        if ((attribute & SfBufferAttr_In) && !(attribute & SfBufferAttr_Out)) {
            return (attribute & SfBufferAttr_HipcPointer) || (attribute & SfBufferAttr_HipcAutoSelect);
        } else {
            return false;
        }
    }
    NX_CONSTEXPR size_t OutHipcPointerBufferPredicate(const u32 attribute) {
        if (!(attribute & SfBufferAttr_In) && (attribute & SfBufferAttr_Out)) {
            return (attribute & SfBufferAttr_HipcPointer) || (attribute & SfBufferAttr_HipcAutoSelect);
        } else {
            return false;
        }
    }
    NX_CONSTEXPR size_t FixedSizeOutHipcPointerBufferPredicate(const u32 attribute) {
        return OutHipcPointerBufferPredicate(attribute) && (attribute & SfBufferAttr_FixedSize);
    }
    template
    struct RawDataOffsetCalculator;
    template
    struct RawDataOffsetCalculator> {
        private:
            template
            struct LayoutHelper {
                static_assert(GetArgumentType == ArgumentType::InData, "LayoutHelper InData");
                static constexpr size_t Alignment = alignof(T);
                static constexpr size_t Size      = sizeof(T);
            };
            template
            struct LayoutHelper> {
                static_assert(GetArgumentType == ArgumentType::InData, "LayoutHelper OutData");
                static constexpr size_t Alignment = alignof(T);
                static constexpr size_t Size      = sizeof(T);
            };
            static constexpr void StableSort(std::array &map, const std::array &values) {
                /* Use insertion sort, which is stable and optimal for small numbers of parameters. */
                for (size_t i = 1; i < sizeof...(Ts); i++) {
                    for (size_t j = i; j > 0 && values[map[j-1]] > values[map[j]]; j--) {
                        std::swap(map[j], map[j-1]);
                    }
                }
            }
        public:
            static constexpr std::array Offsets = [] {
                std::array offsets{};
                offsets[0] = 0;
                if constexpr (sizeof...(Ts) > 0) {
                    /* Get size, alignment for each type. */
                    const std::array sizes = { LayoutHelper::Size... };
                    const std::array aligns = { LayoutHelper::Alignment... };
                    /* We want to sort...by alignment. */
                    std::array map = {};
                    for (size_t i = 0; i < sizeof...(Ts); i++) { map[i] = i; }
                    StableSort(map, aligns);
                    /* Iterate over sorted sizes. */
                    size_t cur_offset = 0;
                    for (size_t i : map) {
                        cur_offset = util::AlignUp(cur_offset, aligns[i]);
                        offsets[i] = cur_offset;
                        cur_offset += sizes[i];
                    }
                    offsets[sizeof...(Ts)] = cur_offset;
                }
                return offsets;
            }();
    };
    struct ArgumentSerializationInfo {
        /* Type used to select from below fields. */
        ArgumentType arg_type;
        /* Raw data indexing. */
        size_t in_raw_data_index;
        size_t out_raw_data_index;
        /* Buffer indexing. */
        size_t buffer_index;
        size_t send_map_alias_index;
        size_t recv_map_alias_index;
        size_t send_pointer_index;
        size_t recv_pointer_index;
        size_t unfixed_recv_pointer_index;
        size_t fixed_size;
        /* Handle indexing. */
        size_t in_move_handle_index;
        size_t in_copy_handle_index;
        size_t out_move_handle_index;
        size_t out_copy_handle_index;
        /* Object indexing. */
        size_t in_object_index;
        size_t out_object_index;
    };
    template
    using DecayForCommandMetaArguments = typename std::conditional<(sf::IsLargeData::type> && !std::is_base_of::type>::value), T, typename std::conditional<(std::same_as || std::same_as), typename std::decay::type &, typename std::decay::type>::type>::type;
    template
    struct CommandMetaInfo {
        public:
            using ArgsTypeForInvoke = std::tuple...>;
            using ArgsType          = std::tuple::type...>;
            using InDatas    = TupleFilter::FilteredType;
            using OutDatas   = TupleFilter::FilteredType;
            using Buffers    = TupleFilter::FilteredType;
            using InHandles  = TupleFilter::FilteredType;
            using OutHandles = TupleFilter::FilteredType;
            using InObjects  = TupleFilter::FilteredType;
            using OutObjects = TupleFilter::FilteredType;
            /* Not kept separate from InDatas when processing, for reasons. */
            using InProcessIdHolders = TupleFilter::FilteredType;
            /* TODO: Support OutProcessIdHolders? */
            static constexpr size_t NumInDatas    = std::tuple_size::value;
            static constexpr size_t NumOutDatas   = std::tuple_size::value;
            static constexpr size_t NumBuffers    = std::tuple_size::value;
            static constexpr size_t NumInHandles  = std::tuple_size::value;
            static constexpr size_t NumOutHandles = std::tuple_size::value;
            static constexpr size_t NumInObjects  = std::tuple_size::value;
            static constexpr size_t NumOutObjects = std::tuple_size::value;
            static constexpr size_t NumInProcessIdHolders  = std::tuple_size::value;
            static constexpr bool   HasInProcessIdHolder   = NumInProcessIdHolders >= 1;
            template
            static constexpr bool IsInProcessIdHolder = GetArgumentType == ArgumentType::InData && std::is_base_of::value;
            template
            static constexpr bool IsInProcessIdHolderIndex = I < std::tuple_size::value && IsInProcessIdHolder::type>;
            static_assert(NumBuffers <= 8, "Methods must take in <= 8 Buffers");
            static_assert(NumInHandles <= 8, "Methods must take in <= 8 Handles");
            static_assert(NumOutHandles + NumOutObjects <= 8, "Methods must output <= 8 Handles");
            /* Buffer marshalling. */
            static constexpr std::array BufferAttributes = BufferAttributeArrayGetter::value;
            static constexpr size_t NumInHipcMapAliasBuffers              = BufferAttributeCounter::GetCount(BufferAttributes);
            static constexpr size_t NumOutHipcMapAliasBuffers             = BufferAttributeCounter::GetCount(BufferAttributes);
            static constexpr size_t NumInHipcPointerBuffers               = BufferAttributeCounter::GetCount(BufferAttributes);
            static constexpr size_t NumOutHipcPointerBuffers              = BufferAttributeCounter::GetCount(BufferAttributes);
            static constexpr size_t NumFixedSizeOutHipcPointerBuffers     = BufferAttributeCounter::GetCount(BufferAttributes);
            static constexpr size_t NumUnfixedSizeOutHipcPointerBuffers   = NumOutHipcPointerBuffers - NumFixedSizeOutHipcPointerBuffers;
            /* In/Out data marshalling. */
            static constexpr std::array InDataOffsets = RawDataOffsetCalculator::Offsets;
            static constexpr size_t InDataSize  = util::AlignUp(InDataOffsets[NumInDatas], alignof(u16));
            static constexpr std::array OutDataOffsets = RawDataOffsetCalculator::Offsets;
            static constexpr size_t OutDataSize = util::AlignUp(OutDataOffsets[NumOutDatas], alignof(u32));
            static constexpr size_t OutDataAlign = [] {
                if constexpr (std::tuple_size::value) {
                    return alignof(typename std::tuple_element<0, OutDatas>::type);
                }
                return static_cast(0);
            }();
            /* Handle marshalling. */
            static constexpr size_t NumInMoveHandles = std::tuple_size::FilteredType>::value;
            static constexpr size_t NumInCopyHandles = std::tuple_size::FilteredType>::value;
            static constexpr size_t NumOutMoveHandles = std::tuple_size::FilteredType>::value;
            static constexpr size_t NumOutCopyHandles = std::tuple_size::FilteredType>::value;
            static_assert(NumInMoveHandles + NumInCopyHandles   == NumInHandles,  "NumInMoveHandles + NumInCopyHandles   == NumInHandles");
            static_assert(NumOutMoveHandles + NumOutCopyHandles == NumOutHandles, "NumOutMoveHandles + NumOutCopyHandles == NumOutHandles");
            /* Used by server message processor at runtime. */
            static constexpr inline const cmif::ServerMessageRuntimeMetadata RuntimeMetadata = cmif::ServerMessageRuntimeMetadata{
                .in_data_size      = InDataSize,
                .out_data_size     = OutDataSize,
                .in_headers_size   = sizeof(CmifInHeader),
                .out_headers_size  = sizeof(CmifOutHeader),
                .in_object_count   = NumInObjects,
                .out_object_count  = NumOutObjects,
            };
        /* Construction of argument serialization structs. */
        private:
            template
            struct ArgumentSerializationInfoConstructor;
            template
            struct ArgumentSerializationInfoConstructor> {
                template
                static constexpr ArgumentSerializationInfo ProcessUpdate(ArgumentSerializationInfo ¤t_info) {
                    /* Save a copy of the current state to return. */
                    ArgumentSerializationInfo returned_info = current_info;
                    /* Clear previous iteration's fixed size. */
                    returned_info.fixed_size = 0;
                    current_info.fixed_size = 0;
                    constexpr auto arg_type = GetArgumentType;
                    returned_info.arg_type = arg_type;
                    if constexpr (arg_type == ArgumentType::InData) {
                        /* New rawdata, so increment index. */
                        current_info.in_raw_data_index++;
                    } else if constexpr (arg_type == ArgumentType::OutData) {
                        /* New rawdata, so increment index. */
                        current_info.out_raw_data_index++;
                    } else if constexpr (arg_type == ArgumentType::InHandle) {
                        /* New InHandle, increment the appropriate index. */
                        if constexpr (std::is_same::value) {
                            current_info.in_move_handle_index++;
                        } else if constexpr (std::is_same::value) {
                            current_info.in_copy_handle_index++;
                        } else {
                            static_assert(!std::is_same::value, "Invalid InHandle kind");
                        }
                    } else if constexpr (arg_type == ArgumentType::OutHandle) {
                        /* New OutHandle, increment the appropriate index. */
                        if constexpr (std::is_same>::value) {
                            current_info.out_move_handle_index++;
                        } else if constexpr (std::is_same>::value) {
                            current_info.out_copy_handle_index++;
                        } else {
                            static_assert(!std::is_same::value, "Invalid OutHandle kind");
                        }
                    } else if constexpr (arg_type == ArgumentType::InObject) {
                        /* New InObject, increment the appropriate index. */
                        current_info.in_object_index++;
                    } else if constexpr (arg_type == ArgumentType::OutObject) {
                        /* New OutObject, increment the appropriate index. */
                        current_info.out_object_index++;
                    } else if constexpr (arg_type == ArgumentType::Buffer) {
                        /* New Buffer, increment the appropriate index. */
                        const auto attributes = BufferAttributes[current_info.buffer_index];
                        current_info.buffer_index++;
                        if (attributes & SfBufferAttr_HipcMapAlias) {
                            if (attributes & SfBufferAttr_In) {
                                current_info.send_map_alias_index++;
                            } else if (attributes & SfBufferAttr_Out) {
                                current_info.recv_map_alias_index++;
                            }
                        } else if (attributes & SfBufferAttr_HipcPointer) {
                            if (attributes & SfBufferAttr_In) {
                                current_info.send_pointer_index++;
                            } else if (attributes & SfBufferAttr_Out) {
                                current_info.recv_pointer_index++;
                                if (!(attributes & SfBufferAttr_FixedSize)) {
                                    current_info.unfixed_recv_pointer_index++;
                                } else {
                                    returned_info.fixed_size = LargeDataSize;
                                }
                            }
                        } else if (attributes & SfBufferAttr_HipcAutoSelect) {
                            if (attributes & SfBufferAttr_In) {
                                current_info.send_map_alias_index++;
                                current_info.send_pointer_index++;
                            } else if (attributes & SfBufferAttr_Out) {
                                current_info.recv_map_alias_index++;
                                current_info.recv_pointer_index++;
                                if (!(attributes & SfBufferAttr_FixedSize)) {
                                    current_info.unfixed_recv_pointer_index++;
                                } else {
                                    returned_info.fixed_size = LargeDataSize;
                                }
                            }
                        }
                    } else {
                        static_assert(!std::is_same::value, "Invalid ArgumentType");
                    }
                    return returned_info;
                }
                static constexpr std::array ArgumentSerializationInfos = [] {
                    ArgumentSerializationInfo current_info = {};
                    return std::array{ ProcessUpdate(current_info)... };
                }();
            };
        public:
            static constexpr std::array::value> ArgumentSerializationInfos = ArgumentSerializationInfoConstructor::ArgumentSerializationInfos;
    };
    template
    class OutRawHolder {
        public:
            static constexpr size_t Size = _Size;
            static constexpr size_t Align = _Align ? _Align : alignof(u8);
        private:
            alignas(Align) u8 m_data[Size];
        public:
            constexpr OutRawHolder() : m_data() { /* ... */ }
            template
            constexpr inline uintptr_t GetAddress() const {
                static_assert(Offset <= Size, "Offset <= Size");
                static_assert(TypeSize <= Size, "TypeSize <= Size");
                static_assert(Offset + TypeSize <= Size, "Offset + TypeSize <= Size");
                return reinterpret_cast(std::addressof(m_data[Offset]));
            }
            constexpr inline void CopyTo(void *dst) const {
                if constexpr (Size > 0) {
                    std::memcpy(dst, m_data, Size);
                }
            }
    };
    template
    class InHandleHolder {
        public:
            static constexpr size_t NumMove = _NumMove;
            static constexpr size_t NumCopy = _NumCopy;
        private:
            MoveHandle m_move_handles[NumMove];
            CopyHandle m_copy_handles[NumCopy];
        public:
            constexpr InHandleHolder() : m_move_handles(), m_copy_handles() { /* ... */ }
            template
            constexpr inline MoveHandle &SetMoveHandle(os::NativeHandle os_handle) {
                static_assert(Index < NumMove);
                m_move_handles[Index] = sf::NativeHandle(os_handle, true);
                return m_move_handles[Index];
            }
            template
            constexpr inline CopyHandle &SetCopyHandle(os::NativeHandle os_handle) {
                static_assert(Index < NumCopy);
                m_copy_handles[Index] = sf::NativeHandle(os_handle, true);
                return m_copy_handles[Index];
            }
    };
    template
    class OutHandleHolder {
        public:
            static constexpr size_t NumMove = _NumMove;
            static constexpr size_t NumCopy = _NumCopy;
        private:
            NativeHandle m_move_handles[NumMove];
            NativeHandle m_copy_handles[NumCopy];
        public:
            constexpr OutHandleHolder() : m_move_handles(), m_copy_handles() { /* ... */ }
            template
            constexpr inline NativeHandle *GetMoveHandlePointer() {
                static_assert(Index < NumMove, "Index < NumMove");
                return m_move_handles + Index;
            }
            template
            constexpr inline NativeHandle *GetCopyHandlePointer() {
                static_assert(Index < NumCopy, "Index < NumCopy");
                return m_copy_handles + Index;
            }
            constexpr inline void CopyTo(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, const size_t num_out_object_handles) {
                ctx.handles_to_close->num_handles = 0;
                #define _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(n) do { if constexpr (NumCopy > n) { const auto handle = m_copy_handles[n].GetOsHandle(); response.copy_handles[n] = handle; if (m_copy_handles[n].IsManaged()) { ctx.handles_to_close->handles[ctx.handles_to_close->num_handles++] = handle; } m_copy_handles[n].Detach(); } } while (0)
                _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(0);
                _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(1);
                _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(2);
                _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(3);
                _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(4);
                _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(5);
                _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(6);
                _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(7);
                #undef _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE
                #define _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(n) do { if constexpr (NumMove > n) { response.move_handles[n + num_out_object_handles] = m_move_handles[n].GetOsHandle(); m_move_handles[n].Detach(); } } while (0)
                _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(0);
                _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(1);
                _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(2);
                _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(3);
                _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(4);
                _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(5);
                _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(6);
                _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(7);
                #undef _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE
            }
    };
    template
    class InOutObjectHolder {
        private:
            std::array m_in_object_holders;
            std::array m_out_object_holders;
            std::array>, NumOutObjects> m_out_shared_pointers;
            std::array m_out_object_ids;
        public:
            constexpr InOutObjectHolder() : m_in_object_holders(), m_out_object_holders() {
                #define _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(n) if constexpr (NumOutObjects > n) { m_out_object_ids[n] = cmif::InvalidDomainObjectId; }
                _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(0)
                _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(1)
                _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(2)
                _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(3)
                _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(4)
                _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(5)
                _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(6)
                _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(7)
                #undef _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID
            }
            Result GetInObjects(const sf::cmif::ServerMessageProcessor *processor) {
                if constexpr (NumInObjects > 0) {
                    R_TRY(processor->GetInObjects(m_in_object_holders.data()));
                }
                return ResultSuccess();
            }
            template
            constexpr inline Result ValidateInObjects() const {
                static_assert(std::tuple_size::value == NumInObjects);
                #define _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(n) do { \
                    if constexpr (NumInObjects > n) { \
                        using SharedPointerType = typename std::tuple_element::type; \
                        using ServiceImplType   = typename SharedPointerType::Interface; \
                        R_UNLESS((m_in_object_holders[n].template IsServiceObjectValid()), sf::cmif::ResultInvalidInObject()); \
                    } \
                } while (0)
                _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(0);
                _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(1);
                _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(2);
                _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(3);
                _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(4);
                _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(5);
                _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(6);
                _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(7);
                #undef _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT
                return ResultSuccess();
            }
            template
            SharedPointer *GetOutObjectSharedPointer() {
                static_assert(sizeof(SharedPointer) == sizeof(SharedPointer));
                return static_cast *>(static_cast(GetPointer(m_out_shared_pointers[Index])));
            }
            template
            Out> GetOutObject() {
                auto sp = std::construct_at(GetOutObjectSharedPointer());
                return Out>(sp, std::addressof(m_out_object_ids[Index]));
            }
            template
            void SetOutObject() {
                m_out_object_holders[Index] = cmif::ServiceObjectHolder(std::move(*GetOutObjectSharedPointer()));
            }
            constexpr void SetOutObjects(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response) {
                if constexpr (NumOutObjects > 0) {
                    ctx.processor->SetOutObjects(ctx, response, m_out_object_holders.data(), m_out_object_ids.data());
                }
            }
    };
    class HipcCommandProcessorCommon : public sf::cmif::ServerMessageProcessor {
        public:
            virtual void SetImplementationProcessor(sf::cmif::ServerMessageProcessor *) override final { /* ... */ }
            virtual void PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &out_raw_data, const cmif::ServerMessageRuntimeMetadata runtime_metadata) override final {
                const size_t raw_size = runtime_metadata.GetOutHeadersSize();
                const auto response = hipcMakeRequestInline(ctx.out_message_buffer.GetPointer(),
                    .type = CmifCommandType_Invalid, /* Really response */
                    .num_data_words = static_cast((util::AlignUp(raw_size, 0x4) + 0x10 /* padding */) / sizeof(u32)),
                );
                out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast(response.data_words), 0x10), raw_size);
            }
            virtual Result GetInObjects(cmif::ServiceObjectHolder *in_objects) const override final {
                /* By default, InObjects aren't supported. */
                AMS_UNUSED(in_objects);
                return sf::ResultNotSupported();
            }
    };
    template
    struct HipcCommandProcessor : public HipcCommandProcessorCommon {
        public:
            virtual const cmif::ServerMessageRuntimeMetadata GetRuntimeMetadata() const override final {
                return CommandMeta::RuntimeMetadata;
            }
            virtual Result PrepareForProcess(const cmif::ServiceDispatchContext &ctx, const cmif::ServerMessageRuntimeMetadata runtime_metadata) const override final {
                const auto &meta = ctx.request.meta;
                bool is_request_valid = true;
                is_request_valid &= meta.send_pid         == CommandMeta::HasInProcessIdHolder;
                is_request_valid &= meta.num_send_statics == CommandMeta::NumInHipcPointerBuffers;
             /* is_request_valid &= meta.num_recv_statics == CommandMeta::NumOutHipcPointerBuffers; */
                is_request_valid &= meta.num_send_buffers == CommandMeta::NumInHipcMapAliasBuffers;
                is_request_valid &= meta.num_recv_buffers == CommandMeta::NumOutHipcMapAliasBuffers;
                is_request_valid &= meta.num_exch_buffers == 0; /* Exchange buffers aren't supported. */
                is_request_valid &= meta.num_copy_handles == CommandMeta::NumInCopyHandles;
                is_request_valid &= meta.num_move_handles == CommandMeta::NumInMoveHandles;
                const size_t meta_raw_size = meta.num_data_words * sizeof(u32);
                const size_t command_raw_size = util::AlignUp(runtime_metadata.GetUnfixedOutPointerSizeOffset() + (CommandMeta::NumUnfixedSizeOutHipcPointerBuffers * sizeof(u16)), alignof(u32));
                is_request_valid &= meta_raw_size >= command_raw_size;
                R_UNLESS(is_request_valid, sf::hipc::ResultInvalidCmifRequest());
                return ResultSuccess();
            }
            virtual HipcRequest PrepareForReply(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &out_raw_data, const cmif::ServerMessageRuntimeMetadata runtime_metadata) override final {
                const size_t raw_size = runtime_metadata.GetOutDataSize() + runtime_metadata.GetOutHeadersSize();
                const auto response = hipcMakeRequestInline(ctx.out_message_buffer.GetPointer(),
                    .type = CmifCommandType_Invalid, /* Really response */
                    .num_send_statics = CommandMeta::NumOutHipcPointerBuffers,
                    .num_data_words = static_cast((util::AlignUp(raw_size, 0x4) + 0x10 /* padding */) / sizeof(u32)),
                    .num_copy_handles = CommandMeta::NumOutCopyHandles,
                    .num_move_handles = static_cast(CommandMeta::NumOutMoveHandles + runtime_metadata.GetOutObjectCount()),
                );
                out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast(response.data_words), 0x10), raw_size);
                return response;
            }
            virtual void SetOutObjects(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, cmif::ServiceObjectHolder *out_objects, cmif::DomainObjectId *ids) override final {
                AMS_UNUSED(ids);
                #define _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(n) do { if constexpr (CommandMeta::NumOutObjects > n) { SetOutObjectImpl(response, ctx.manager, std::move(out_objects[n])); } } while (0)
                _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(0);
                _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(1);
                _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(2);
                _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(3);
                _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(4);
                _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(5);
                _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(6);
                _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(7);
                #undef _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL
            }
        /* Useful defines. */
        using ArgsTypeForInvoke = typename CommandMeta::ArgsTypeForInvoke;
        using ArgsType          = typename CommandMeta::ArgsType;
        using BufferArrayType = std::array;
        using OutRawHolderType = OutRawHolder;
        using InHandleHolderType = InHandleHolder;
        using OutHandleHolderType = OutHandleHolder;
        using InOutObjectHolderType = InOutObjectHolder;
        /* Buffer processing. */
        private:
            template
            NX_CONSTEXPR void SetOutObjectImpl(const HipcRequest &response, hipc::ServerSessionManager *manager, cmif::ServiceObjectHolder &&object) {
                /* If no object, write os::InvalidNativeHandle. This is what official software does. */
                if (!object) {
                    response.move_handles[Index] = os::InvalidNativeHandle;
                    return;
                }
                os::NativeHandle server_handle, client_handle;
                R_ABORT_UNLESS(sf::hipc::CreateSession(std::addressof(server_handle), std::addressof(client_handle)));
                R_ABORT_UNLESS(manager->RegisterSession(server_handle, std::move(object)));
                response.move_handles[Index] = client_handle;
            }
            template