code:
#pragma once
#include "Rpc/rpc.pb-c.h"

namespace Mira
{
    namespace Messaging
    {
        namespace Rpc
        {
            class Connection;
        }

        class MessageListener
        {
        private:
            RpcCategory m_Category;
            int32_t m_Type;
            void(*m_Callback)(Rpc::Connection* p_Connection, const RpcTransport* p_Message);

        public:
            MessageListener() :
                m_Category(RPC_CATEGORY__NONE),
                m_Type(0),
                m_Callback(nullptr)
            {

            }

            MessageListener(RpcCategory p_Category, int32_t p_Type, void(*p_Callback)(Rpc::Connection* p_Connection, const RpcTransport* p_Message)) :
                m_Category(p_Category),
                m_Type(p_Type),
                m_Callback(p_Callback)
            {

            }

            ~MessageListener()
            {
                m_Category = RPC_CATEGORY__NONE;
                m_Type = 0;
                m_Callback = nullptr;
            }

            // Copy constructor
            MessageListener(const MessageListener& p_Other) :
                m_Category(p_Other.m_Category),
                m_Type(p_Other.m_Type),
                m_Callback(p_Other.m_Callback)
            {

            }

            // Copy assignment
            MessageListener& operator=(const MessageListener& p_Other)
            {
                if (&p_Other == this)
                    return *this;

                m_Category = p_Other.m_Category;
                m_Type = p_Other.m_Type;
                m_Callback = p_Other.m_Callback;

                return *this;
            }

            // Move constructor
            MessageListener(MessageListener&& p_Other) :
                m_Category(p_Other.m_Category),
                m_Type(p_Other.m_Type),
                m_Callback(p_Other.m_Callback)
            {

            }

            // Move assignment
            MessageListener& operator=(MessageListener&& p_Other)
            {
                if (&p_Other == this)
                    return *this;

                m_Category = p_Other.m_Category;
                m_Type = p_Other.m_Type;
                m_Callback = p_Other.m_Callback;

                return *this;
            }

            RpcCategory GetCategory() { return m_Category; }
            int32_t GetType() { return m_Type; }
            auto GetCallback() -> void(*)(Rpc::Connection* p_Connection, const RpcTransport* p_Message)
            {
                return m_Callback;
            };

            void Zero()
            {
                m_Category = RPC_CATEGORY__NONE;
                m_Type = 0;
                m_Callback = nullptr;
            }
        };
    }
}

repo_name: veiledmerc/mira

license: mit


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include "MessageManager.hpp"
#include "MessageListener.hpp"

#include <Utils/Kdlsym.hpp>
#include <Utils/SysWrappers.hpp>
#include <Utils/Kernel.hpp>

#include "Rpc/Server.hpp"
#include "Rpc/Connection.hpp"

#include <Mira.hpp>

using namespace Mira::Messaging;

MessageManager::MessageManager()
{
    auto mtx_init = (void(*)(struct mtx *m, const char *name, const char *type, int opts))kdlsym(mtx_init);
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    // Create the message manager
	mtx_init(&m_Mutex, "MiraMM", nullptr, MTX_DEF);

    _mtx_lock_flags(&m_Mutex, 0);
    for (auto i = 0; i < ARRAYSIZE(m_Listeners); ++i)
        m_Listeners[i].Zero();
    _mtx_unlock_flags(&m_Mutex, 0);
}

MessageManager::~MessageManager()
{

}

bool MessageManager::RegisterCallback(RpcCategory p_Category, int32_t p_Type, void(*p_Callback)(Rpc::Connection*, const RpcTransport*))
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    bool s_Success = false;
    _mtx_lock_flags(&m_Mutex, 0);
    do
    {
        if (p_Category < RPC_CATEGORY__NONE || p_Category >= RPC_CATEGORY__MAX)
        {
            WriteLog(LL_Error, "could not register callback, invalid category (%d).", p_Category);
            break;
        }

        if (p_Callback == nullptr)
        {
            WriteLog(LL_Error, "invalid callback.");
            break;
        }

        MessageListener* s_FoundEntry = nullptr;
        int32_t s_FreeIndex = -1;

        auto s_ListenerCount = ARRAYSIZE(m_Listeners);
        for (auto i = 0; i < s_ListenerCount; ++i)
        {
            auto& l_CategoryEntry = m_Listeners[i];

            if (!l_CategoryEntry.GetCallback() &&
                !l_CategoryEntry.GetCategory() &&
                !l_CategoryEntry.GetType())
            {
                s_FreeIndex = i;
                continue;
            }

            if (l_CategoryEntry.GetCategory() != p_Category)
                continue;

            if (l_CategoryEntry.GetType() != p_Type)
                continue;

            if (l_CategoryEntry.GetCallback() != p_Callback)
                continue;

            s_FoundEntry = &m_Listeners[i];
            break;
        }

        if (s_FoundEntry != nullptr)
        {
            WriteLog(LL_Error, "callback already exists");
            break;
        }

        if (s_FreeIndex < 0 || s_FreeIndex >= ARRAYSIZE(m_Listeners))
        {
            WriteLog(LL_Error, "no free index");
            break;
        }

        m_Listeners[s_FreeIndex] = MessageListener(p_Category, p_Type, p_Callback);
        s_Success = true;
    } while (false);
    _mtx_unlock_flags(&m_Mutex, 0);

    return s_Success;
}

bool MessageManager::UnregisterCallback(RpcCategory p_Category, int32_t p_Type, void(*p_Callback)(Rpc::Connection*, const RpcTransport*))
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    bool s_Success = false;
    _mtx_lock_flags(&m_Mutex, 0);
    do
    {
        if (p_Category < RPC_CATEGORY__NONE || p_Category >= RPC_CATEGORY__MAX)
        {
            WriteLog(LL_Error, "could not register callback, invalid category (%d).", p_Category);
            break;
        }

        if (p_Callback == nullptr)
        {
            WriteLog(LL_Error, "invalid callback.");
            break;
        }

        MessageListener* s_FoundEntry = nullptr;
        int32_t s_FoundIndex = -1;

        for (auto i = 0; i < ARRAYSIZE(m_Listeners); ++i)
        {
            auto& l_CategoryEntry = m_Listeners[i];
            if (l_CategoryEntry.GetCategory() != p_Category)
                continue;

            if (l_CategoryEntry.GetType() != p_Type)
                continue;

            if (l_CategoryEntry.GetCallback() != p_Callback)
                continue;

            s_FoundEntry = &m_Listeners[i];
            s_FoundIndex = i;
            break;
        }

        if (s_FoundEntry == nullptr || s_FoundIndex == -1)
        {
            WriteLog(LL_Error, "category entry not found for cat: (%d).", p_Category);
            break;
        }

        // Reset
        s_FoundEntry->Zero();
        s_Success = true;
    } while (false);
    _mtx_unlock_flags(&m_Mutex, 0);

    return s_Success;
}

void MessageManager::SendErrorResponse(Rpc::Connection* p_Connection, RpcCategory p_Category, int32_t p_Error)
{
    if (p_Connection == nullptr)
        return;

    SendResponse(p_Connection, p_Category, 0,  p_Error < 0 ? p_Error : (-p_Error), nullptr, 0);
}

void MessageManager::SendResponse(Rpc::Connection* p_Connection, const RpcTransport* p_Message)
{
    if (p_Connection == nullptr)
    {
        WriteLog(LL_Error, "could not get connection.");
        return;
    }

    if (p_Message == nullptr)
    {
        WriteLog(LL_Error, "could not get message.");
        return;
    }

    auto s_MainThread = Mira::Framework::GetFramework()->GetMainThread();
    if (s_MainThread == nullptr)
    {
        WriteLog(LL_Error, "could not get main thread");
        return;
    }

    auto s_Framework = Mira::Framework::GetFramework();
    if (s_Framework == nullptr)
    {
        WriteLog(LL_Error, "could not get framework");
        return;
    }

    // Get the socket for this thread
    auto s_Socket = p_Connection->GetSocket();
    if (s_Socket < 0)
    {
        WriteLog(LL_Error, "invalid socket (%d).", s_Socket);
        return;
    }

    auto s_SerializedSize = rpc_transport__get_packed_size(p_Message);
    if (s_SerializedSize <= 0)
    {
        WriteLog(LL_Error, "invalid serialized size (%x)", s_SerializedSize);
        return;
    }

    if (s_SerializedSize > MessageManager_MaxMessageSize)
    {
        WriteLog(LL_Error, "serialized size too large (%x) > (%llx)", s_SerializedSize, MessageManager_MaxMessageSize);
        return;
    }

    // Allocate enough space for size + message data
    auto s_TotalSize = sizeof(uint64_t) + s_SerializedSize;
    auto s_SerializedData = new uint8_t[s_TotalSize];
    if (s_SerializedData == nullptr)
    {
        WriteLog(LL_Error, "could not allocate (%x).", s_SerializedSize);
        return;
    }
    memset(s_SerializedData, 0, s_TotalSize);

    // Set the message size
    *(uint64_t*)s_SerializedData = s_SerializedSize;

    // Get the message start
    auto s_MessageStart = s_SerializedData + sizeof(uint64_t);

    // Pack the message
    auto s_Ret = rpc_transport__pack(p_Message, s_MessageStart);
    if (s_Ret != s_SerializedSize)
    {
        WriteLog(LL_Error, "could not serialize data (%llx) != (%llx).", s_Ret, s_SerializedSize);
        memset(s_SerializedData, 0, s_TotalSize);
        delete [] s_SerializedData;
        return;
    }

    // Get and send the data
    auto s_BytesWritten = kwrite_t(s_Socket, s_SerializedData, s_TotalSize, s_MainThread);
    if (s_BytesWritten < 0)
    {
        WriteLog(LL_Error, "could not send data (%lld).", s_BytesWritten);
        memset(s_SerializedData, 0, s_TotalSize);
        delete [] s_SerializedData;
        return;
    }

    memset(s_SerializedData, 0, s_TotalSize);
    delete [] s_SerializedData;
}

void MessageManager::SendResponse(Rpc::Connection* p_Connection, RpcCategory p_Category, uint32_t p_Type, int64_t p_Error, void* p_Data, uint32_t p_DataSize)
{
    if (p_Connection == nullptr)
    {
        WriteLog(LL_Error, "invalid connection");
        return;
    }

    if (p_Category < RPC_CATEGORY__NONE || p_Category >= RPC_CATEGORY__MAX)
    {
        WriteLog(LL_Error, "invalid category (%d)", p_Category);
        return;
    }

    RpcHeader s_Header = RPC_HEADER__INIT;
    s_Header.category = p_Category;
    s_Header.type = p_Type;
    s_Header.error = p_Error;
    s_Header.isrequest = false;
    s_Header.magic = 2;

    RpcTransport s_Response = RPC_TRANSPORT__INIT;
    s_Response.header = &s_Header;

    s_Response.data.data = static_cast<uint8_t*>(p_Data);
    s_Response.data.len = p_Data == nullptr ? 0 : p_DataSize;

    SendResponse(p_Connection, &s_Response);
}

void MessageManager::OnRequest(Rpc::Connection* p_Connection, const RpcTransport* p_Message)
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    if (p_Connection == nullptr)
    {
        WriteLog(LL_Error, "invalid connection.");
        return;
    }

    if (p_Message == nullptr)
    {
        WriteLog(LL_Error, "could not get message.");
        return;
    }

    MessageListener* s_FoundEntry = nullptr;
    int32_t s_FoundIndex = -1;

    _mtx_lock_flags(&m_Mutex, 0);
    do
    {
        for (auto i = 0; i < ARRAYSIZE(m_Listeners); ++i)
        {
            auto l_CategoryEntry = &m_Listeners[i];
            if (l_CategoryEntry == nullptr)
                continue;

            if (l_CategoryEntry->GetCategory() != p_Message->header->category)
                continue;

            if (l_CategoryEntry->GetType() != p_Message->header->type)
                continue;

            s_FoundEntry = &m_Listeners[i];
            s_FoundIndex = i;
            break;
        }

        if (s_FoundEntry == nullptr || s_FoundIndex == -1)
        {
            WriteLog(LL_Error, "could not find endpoint c: (%x) t:(%x)", p_Message->header->category, p_Message->header->type);
            break;
        }

    } while (false);
    _mtx_unlock_flags(&m_Mutex, 0);

    if (s_FoundEntry == nullptr)
        return;

    if (s_FoundEntry->GetCallback())
        s_FoundEntry->GetCallback()(p_Connection, p_Message);
}


code:
#pragma once
#include "MessageListener.hpp"

extern "C"
{
    #include "Rpc/rpc.pb-c.h"
    #include <sys/param.h>
    #include <sys/lock.h>
    #include <sys/mutex.h>
}

namespace Mira
{
    namespace Messaging
    {
        namespace Rpc
        {
            class Connection;
        }

        enum
        {
            MessageManager_MaxCategories = 14,
            MessageManager_MaxListeners = 64,
            MessageManager_MaxMessageSize = 0x4000000,
        };

        class MessageManager
        {
        private:
            Messaging::MessageListener m_Listeners[MessageManager_MaxListeners];
            struct mtx m_Mutex;

        public:
            MessageManager();
            ~MessageManager();

            bool RegisterCallback(RpcCategory p_Category, int32_t p_Type, void(*p_Callback)(Rpc::Connection*, const RpcTransport*));
            bool UnregisterCallback(RpcCategory p_Category, int32_t p_Type, void(*p_Callback)(Rpc::Connection*, const RpcTransport*));
            void UnregisterAllCallbacks();

            void SendErrorResponse(Rpc::Connection* p_Connection, RpcCategory p_Category, int32_t p_Error);

            void SendResponse(Rpc::Connection* p_Connection, const RpcTransport* p_Message);
            void SendResponse(Rpc::Connection* p_Connection, RpcCategory p_Category, uint32_t p_Type, int64_t p_Error, void* p_Data, uint32_t p_DataSize);

            void OnRequest(Rpc::Connection* p_Connection, const RpcTransport* p_Message);
        };
    }
}


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include "Connection.hpp"
#include "Server.hpp"

#include <Messaging/MessageManager.hpp>

#include <Utils/Kdlsym.hpp>
#include <Utils/Kernel.hpp>
#include <Utils/Logger.hpp>
#include <Utils/Span.hpp>
#include <Utils/SysWrappers.hpp>

#include <Mira.hpp>

extern "C"
{
    #include <sys/socket.h>
    #include <sys/filedesc.h>
    #include <sys/proc.h>
    #include <sys/pcpu.h>

    #include "rpc.pb-c.h"
}

using namespace Mira::Messaging::Rpc;

Connection::Connection(Rpc::Server* p_Server, uint32_t p_ClientId, int32_t p_Socket, struct sockaddr_in& p_Address) :
    m_Socket(p_Socket),
    m_Id(p_ClientId),
    m_Running(false),
    m_Thread(nullptr),
    m_Address{0},
    m_Server(p_Server)
{
    memcpy(&m_Address, &p_Address, sizeof(m_Address));

    auto mtx_init = (void(*)(struct mtx *m, const char *name, const char *type, int opts))kdlsym(mtx_init);
    mtx_init(&m_Mutex, "MiraRpcConMtx", nullptr, MTX_DEF);
}

Connection::~Connection()
{
    if (m_Running)
        Disconnect();

    auto mtx_destroy = (void(*)(struct mtx* mutex))kdlsym(mtx_destroy);
	mtx_destroy(&m_Mutex);
}

void Connection::Disconnect()
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    _mtx_lock_flags(&m_Mutex, 0);
    do
    {
        // Even if something freakish happens try to free resources
        if (m_Running)
            m_Running = false;

        auto s_MainThread = Mira::Framework::GetFramework()->GetMainThread();
        if (s_MainThread == nullptr)
        {
            WriteLog(LL_Error, "could not get main thread");
            break;
        }

        // If the socket is in use, kill it, should break out of the threaded loop
        if (m_Socket > 0)
        {
            kshutdown_t(m_Socket, SHUT_RDWR, s_MainThread);
            kclose_t(m_Socket, s_MainThread);
            m_Socket = -1;
        }

        WriteLog(LL_Debug, "client (%p) disconnecting.", this);
    } while (false);
    _mtx_unlock_flags(&m_Mutex, 0);
}

void Connection::SetRunning(bool p_Running)
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    _mtx_lock_flags(&m_Mutex, 0);
    m_Running = p_Running;
    _mtx_unlock_flags(&m_Mutex, 0);
}

void Connection::ConnectionThread(void* p_Connection)
{
    auto kthread_exit = (void(*)(void))kdlsym(kthread_exit);
    auto s_MainThread = Mira::Framework::GetFramework()->GetMainThread();
    if (s_MainThread == nullptr)
    {
        WriteLog(LL_Error, "could not get main thread");
        kthread_exit();
        return;
    }

    // Validate connection
    auto s_Connection = reinterpret_cast<Rpc::Connection*>(p_Connection);
    if (s_Connection == nullptr)
    {
        WriteLog(LL_Error, "invalid connection instance");
        kthread_exit();
        return;
    }

    auto s_Framework = Mira::Framework::GetFramework();
    if (s_Framework == nullptr)
    {
        WriteLog(LL_Error, "could not get mira framework.");
        kthread_exit();
        return;
    }

    auto s_MessageManager = s_Framework->GetMessageManager();
    if (s_MessageManager == nullptr)
    {
        WriteLog(LL_Error, "could not get message manager");
        kthread_exit();
        return;
    }

    auto s_Buffer = new uint8_t[MaxBufferSize];
    if (s_Buffer == nullptr)
    {
        WriteLog(LL_Error, "could not allocate maximum buffer length.");
        kthread_exit();
        return;
    }

    WriteLog(LL_Info, "rpc connection thread created socket: (%d), addr: (%x), thread: (%p).",
                    s_Connection->m_Socket,
                    s_Connection->m_Address.sin_addr.s_addr,
                    s_Connection->m_Thread);

    // Update the running state, this takes a lock
    s_Connection->SetRunning(true);

    auto s_Socket = s_Connection->GetSocket();

    uint64_t s_IncomingMessageSize = 0;
    ssize_t s_Ret = -1;
    while (((s_Ret = krecv_t(s_Socket, &s_IncomingMessageSize, sizeof(s_IncomingMessageSize), 0, s_MainThread)) > 0) && s_Connection->IsRunning())
    {
        // Make sure that we got all of the data
        if (s_Ret != sizeof(s_IncomingMessageSize))
        {
            WriteLog(LL_Error, "could not get message header.");
            break;
        }

        // Validate the incoming message size
        if (s_IncomingMessageSize > MaxBufferSize)
        {
            WriteLog(LL_Error, "incoming message size (%llx) > max buffer size (%llx)", s_IncomingMessageSize, MaxBufferSize);
            break;
        }

        if (s_IncomingMessageSize == 0)
        {
            WriteLog(LL_Error, "invalid message size.");
            break;
        }

        // Zero our buffer
        memset(s_Buffer, 0, MaxBufferSize);

        // Get the message data from the wire
        s_Ret = krecv_t(s_Socket, s_Buffer, s_IncomingMessageSize, 0, s_MainThread);
        if (s_Ret != s_IncomingMessageSize)
        {
            WriteLog(LL_Error, "did not get correct amount of data (%llx) wanted (%llx)", s_Ret, s_IncomingMessageSize);
            memset(s_Buffer, 0, MaxBufferSize);
            break;
        }

        RpcTransport* s_Transport = rpc_transport__unpack(nullptr, s_IncomingMessageSize, s_Buffer);
        if (s_Transport == nullptr)
        {
            WriteLog(LL_Error, "error unpacking incoming message");
            memset(s_Buffer, 0, MaxBufferSize);
            break;
        }

        // Validate the header magic
        auto s_Header = s_Transport->header;

        if (s_Header == nullptr)
        {
            WriteLog(LL_Error, "could not get the transport header.");
            memset(s_Buffer, 0, MaxBufferSize);
            break;
        }

        if (s_Header->magic != 2)
        {
            WriteLog(LL_Error, "incorrect magic got(%d) wanted (%d).", s_Header->magic, 2);
            rpc_transport__free_unpacked(s_Transport, nullptr);
            memset(s_Buffer, 0, MaxBufferSize);
            break;
        }

        // Validate message category
        auto s_Category = s_Header->category;
        if (s_Category < RPC_CATEGORY__NONE || s_Category >= RPC_CATEGORY__MAX)
        {
            WriteLog(LL_Error, "invalid category (%d).", s_Category);
            rpc_transport__free_unpacked(s_Transport, nullptr);
            memset(s_Buffer, 0, MaxBufferSize);
            break;
        }

        // We do not want to handle any responses
        if (!s_Header->isrequest)
        {
            WriteLog(LL_Error, "attempted to handle outgoing message, fix ya code");
            rpc_transport__free_unpacked(s_Transport, nullptr);
            memset(s_Buffer, 0, MaxBufferSize);
            break;
        }

        // Bounds check the span
        if (s_Transport->data.len > MaxBufferSize)
        {
            WriteLog(LL_Error, "transport data does not have enough data, wanted (%d), have (%d).", s_Transport->data.len, MaxBufferSize);
            rpc_transport__free_unpacked(s_Transport, nullptr);
            memset(s_Buffer, 0, MaxBufferSize);
            break;
        }

        s_MessageManager->OnRequest(s_Connection, const_cast<const RpcTransport*>(s_Transport));

        WriteLog(LL_Error, "here");

        // Free the protobuf
        rpc_transport__free_unpacked(s_Transport, nullptr);

        // Zero out the header for use next iteration
        memset(s_Buffer, 0, MaxBufferSize);
        s_IncomingMessageSize = 0;
    }

    // Free the maximum buffer size
    memset(s_Buffer, 0, MaxBufferSize);
    delete [] s_Buffer;

    WriteLog(LL_Error, "why did we get here (%d)", s_Connection->m_Running);

    // Cleans up resources
    s_Connection->Disconnect();

    // Tell the server to free this memory
    if (s_Connection != nullptr && s_Connection->m_Server != nullptr)
        s_Connection->m_Server->OnConnectionDisconnected(s_Connection);

    kthread_exit();
}


code:
#pragma once
#include <Utils/Vector.hpp>

extern "C"
{
    #include <netinet/in.h>
    #include <sys/param.h>
    #include <sys/lock.h>
    #include <sys/mutex.h>
};

namespace Mira
{
    namespace Messaging
    {
        namespace Rpc
        {
            class Server;

            class Connection
            {
            private:
                enum { MaxBufferSize = 0x8000 };
                // Connection socket
                int32_t m_Socket;

                // Client id
                uint32_t m_Id;

                // Is the client connection still running
                volatile bool m_Running;

                // Client thread loop
                void* m_Thread;

                // Client address
                struct sockaddr_in m_Address;

                // Server reference
                Rpc::Server* m_Server;

                struct mtx m_Mutex;

            public:
                Connection(Rpc::Server* p_Server, uint32_t p_ClientId, int32_t p_Socket, struct sockaddr_in& p_Address);
                virtual ~Connection();

                // This Takes a lock
                void Disconnect();

                // This takes a lock
                void SetRunning(bool p_Running);

                struct mtx* GetMutex() { return &m_Mutex; }
                int32_t GetSocket() { return m_Socket; }
                uint32_t GetId() { return m_Id; }
                bool IsRunning() { return m_Running; }
                struct sockaddr_in* GetAdress() { return &m_Address; }
                void** Internal_GetThread() { return &m_Thread; }
                void* GetConnectionThread() { return m_Thread; }

                static void ConnectionThread(void* p_Connection);
            };
        }
    }
}


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include "Server.hpp"
#include "Connection.hpp"

#include <Utils/Kdlsym.hpp>
#include <Utils/Kernel.hpp>
#include <Utils/Vector.hpp>
#include <Utils/SysWrappers.hpp>

extern "C"
{
    #include <sys/proc.h>
    #include <sys/socket.h>
    #include <sys/filedesc.h>
};

#include <Mira.hpp>

using namespace Mira::Messaging::Rpc;

Server::Server(uint16_t p_Port) :
    m_Socket(-1),
    m_Port(p_Port),
    m_Thread(nullptr),
    m_Running(false),
    m_NextConnectionId(1),
    m_Connections { 0 }
{
    // Zero out the address
    memset(&m_Address, 0, sizeof(m_Address));

    auto mtx_init = (void(*)(struct mtx *m, const char *name, const char *type, int opts))kdlsym(mtx_init);
    mtx_init(&m_Mutex, "MiraRpcSrv", nullptr, MTX_DEF);
}

Server::~Server()
{
    auto mtx_destroy = (void(*)(struct mtx* mutex))kdlsym(mtx_destroy);
	mtx_destroy(&m_Mutex);
}

bool Server::OnLoad()
{
    WriteLog(LL_Info, "starting rpc server");
    return Startup();
}

bool Server::OnUnload()
{
    WriteLog(LL_Error, "unloading rpc server");
    return Teardown();
}

bool Server::OnSuspend()
{
    WriteLog(LL_Error, "suspending rpc server");
    return Teardown();
}

bool Server::OnResume()
{
    WriteLog(LL_Error, "resuming rpc server");
    return Startup();
}

bool Server::Startup()
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);
    auto kthread_add = (int(*)(void(*func)(void*), void* arg, struct proc* procptr, struct thread** tdptr, int flags, int pages, const char* fmt, ...))kdlsym(kthread_add);

    bool s_Success = false;
    _mtx_lock_flags(&m_Mutex, 0);
    do
    {
        WriteLog(LL_Error, "RpcServer thread starting...");
        auto s_MainThread = Mira::Framework::GetFramework()->GetMainThread();
        if (s_MainThread == nullptr)
        {
            WriteLog(LL_Error, "could not get main thread");
            break;
        }

        // Create a socket
        WriteLog(LL_Error, "Creating new socket");
        auto s_Ret = ksocket_t(AF_INET, SOCK_STREAM, 0, s_MainThread);
        if (s_Ret < 0)
        {
            WriteLog(LL_Error, "could not initialize socket (%d).", s_Ret);
            break;
        }
        m_Socket = s_Ret;
        WriteLog(LL_Info, "socket created: (%d).", s_Ret);

        // Set our server to listen on 0.0.0.0:<port>
        memset(&m_Address, 0, sizeof(m_Address));
        m_Address.sin_family = AF_INET;
        m_Address.sin_addr.s_addr = htonl(INADDR_ANY);
        m_Address.sin_port = htons(m_Port);
        m_Address.sin_len = sizeof(m_Address);

        // Bind to port
        WriteLog(LL_Debug, "Attempting to bind...");
        s_Ret = kbind_t(m_Socket, reinterpret_cast<struct sockaddr*>(&m_Address), sizeof(m_Address), s_MainThread);
        if (s_Ret < 0)
        {
            WriteLog(LL_Error, "could not bind socket (%d).", s_Ret);
            kshutdown_t(m_Socket, SHUT_RDWR, s_MainThread);
            kclose_t(m_Socket, s_MainThread);
            m_Socket = -1;
            break;
        }
        WriteLog(LL_Info, "socket (%d) bound to port (%d).", m_Socket, m_Port);

        // Listen on the port for new connections
        WriteLog(LL_Debug, "Attempting to listen for new connections...");
        s_Ret = klisten_t(m_Socket, RpcServer_MaxConnections, s_MainThread);
        if (s_Ret < 0)
        {
            WriteLog(LL_Error, "could not listen on socket (%d).", s_Ret);
            kshutdown_t(m_Socket, SHUT_RDWR, s_MainThread);
            kclose_t(m_Socket, s_MainThread);
            m_Socket = -1;
            break;
        }

        // Create the new server processing thread, 8MiB stack
        WriteLog(LL_Debug, "Creating new server thread");
        s_Ret = kthread_add(Server::ServerThread, this, Mira::Framework::GetFramework()->GetInitParams()->process, reinterpret_cast<thread**>(&m_Thread), 0, 32, "RpcServer");

        WriteLog(LL_Debug, "rpcserver kthread_add returned (%d).", s_Ret);

        s_Success = s_Ret == 0;
    } while (false);
    _mtx_unlock_flags(&m_Mutex, 0);

    return s_Success;
}

bool Server::Teardown()
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    bool s_Success = false;
    _mtx_lock_flags(&m_Mutex, 0);
    do
    {
        auto s_MainThread = Mira::Framework::GetFramework()->GetMainThread();
        if (s_MainThread == nullptr)
        {
            WriteLog(LL_Error, "could not get main thread");
            break;
        }

        // Set that we no longer want to run this server instance
        m_Running = false;
        WriteLog(LL_Debug, "we are no longer running...");

        // Iterate through all connections and disconnect them
        for (auto i = 0; i < ARRAYSIZE(m_Connections); ++i)
        {
            // Check that we have a valid connection
            // _mtx_lock_spin_flags(&m_Mutex, 0);
            auto l_Connection = m_Connections[i];
            // _mtx_unlock_spin_flags(&m_Mutex, 0);

            if (l_Connection == nullptr)
                continue;

            // Disconnect the client, the client will fire the Server::OnConnectionDisconnected
            if (l_Connection->IsRunning())
                l_Connection->Disconnect();
        }

        WriteLog(LL_Debug, "all clients have been disconnected");

        // Close the server socket
        if (m_Socket > 0)
        {
            kshutdown_t(m_Socket, SHUT_RDWR, s_MainThread);
            kclose_t(m_Socket, s_MainThread);
            m_Socket = -1;
        }

        WriteLog(LL_Debug, "socket is killed");
        s_Success = true;
    } while (false);
    _mtx_unlock_flags(&m_Mutex, 0);

    return s_Success;
}

bool Server::SetClientIndex(int32_t p_Index, Rpc::Connection* p_Connection)
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    bool s_Success = false;
    _mtx_lock_flags(&m_Mutex, 0);
    do
    {
        if (p_Index < 0 || p_Index >= ARRAYSIZE(m_Connections))
            break;

        if (m_Connections[p_Index] != nullptr)
            break;

        m_Connections[p_Index] = p_Connection;
        s_Success = true;
    } while (false);
    _mtx_unlock_flags(&m_Mutex, 0);

    return s_Success;
}

void Server::SetRunningState(bool p_IsRunning)
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    _mtx_lock_flags(&m_Mutex, 0);
    m_Running = p_IsRunning;
    _mtx_unlock_flags(&m_Mutex, 0);
}

void Server::ServerThread(void* p_UserArgs)
{
    auto kthread_exit = (void(*)(void))kdlsym(kthread_exit);

    auto s_MainThread = Mira::Framework::GetFramework()->GetMainThread();
    if (s_MainThread == nullptr)
    {
        WriteLog(LL_Error, "no main thread");
        kthread_exit();
        return;
    }

    // Check for invalid usage
    Server* s_Server = static_cast<Server*>(p_UserArgs);
    if (s_Server == nullptr)
    {
        WriteLog(LL_Error, "invalid usage, userargs are null.");
        kthread_exit();
        return;
    }

    // Create our timeout
    struct timeval s_Timeout
    {
        .tv_sec = 3,
        .tv_usec = 0
    };

    int s_ClientSocket = -1;
    struct sockaddr_in s_ClientAddress = { 0 };
    size_t s_ClientAddressLen = sizeof(s_ClientAddress);
    memset(&s_ClientAddress, 0, s_ClientAddressLen);
    s_ClientAddress.sin_len = s_ClientAddressLen;

    // Set our running state
    s_Server->SetRunningState(true);

    fd_set s_ReadFds;
    FD_ZERO(&s_ReadFds);
    FD_SET(s_Server->m_Socket, &s_ReadFds);

    while ((s_ClientSocket = kaccept_t(s_Server->m_Socket, reinterpret_cast<struct sockaddr*>(&s_ClientAddress), &s_ClientAddressLen, s_MainThread)) > 0)
    {
        // Set the send recv and linger timeouts
        /*int32_t result = ksetsockopt(l_ClientSocket, SOL_SOCKET, SO_RCVTIMEO, (caddr_t)&s_Timeout, sizeof(s_Timeout));
        if (result < 0)
        {
            WriteLog(LL_Error, "could not set recv timeout (%d).", result);
            kclose(l_ClientSocket);
            continue;
        }

        result = ksetsockopt(l_ClientSocket, SOL_SOCKET, SO_SNDTIMEO, (caddr_t)&s_Timeout, sizeof(s_Timeout));
        if (result < 0)
        {
            WriteLog(LL_Error, "could not set send timeout (%d).", result);
            kclose(l_ClientSocket);
            continue;
        }*/

        // SO_LINGER
        s_Timeout.tv_sec = 0;
        auto result = ksetsockopt_t(s_ClientSocket, SOL_SOCKET, SO_LINGER, (caddr_t)&s_Timeout, sizeof(s_Timeout), s_MainThread);
        if (result < 0)
        {
            WriteLog(LL_Error, "could not set send timeout (%d).", result);
            kshutdown_t(s_ClientSocket, SHUT_RDWR, s_MainThread);
            kclose_t(s_ClientSocket, s_MainThread);
            continue;
        }

        uint32_t l_Addr = (uint32_t)s_ClientAddress.sin_addr.s_addr;

        WriteLog(LL_Debug, "got new rpc connection (%d) from IP (%03d.%03d.%03d.%03d).", s_ClientSocket,
            (l_Addr & 0xFF),
            (l_Addr >> 8) & 0xFF,
            (l_Addr >> 16) & 0xFF,
            (l_Addr >> 24) & 0xFF);

        auto l_Connection = new Rpc::Connection(s_Server, s_Server->m_NextConnectionId, s_ClientSocket, s_ClientAddress);
        if (!l_Connection)
        {
            WriteLog(LL_Error, "could not allocate new connection for socket (%d).", s_ClientSocket);

            kshutdown_t(s_ClientSocket, SHUT_RDWR, s_MainThread);
            kclose_t(s_ClientSocket, s_MainThread);
            break;
        }

        auto s_FreeIndex = s_Server->GetFreeConnectionIndex();
        if (s_FreeIndex < 0)
        {
            WriteLog(LL_Error, "could not get free connection index");

            kshutdown_t(s_ClientSocket, SHUT_RDWR, s_MainThread);
            kclose_t(s_ClientSocket, s_MainThread);

            delete l_Connection;
            l_Connection = nullptr;
            break;
        }

        // Takes server lock
        if (!s_Server->SetClientIndex(s_FreeIndex, l_Connection))
        {
            WriteLog(LL_Error, "could not set into free index, something's wrong here.");

            kshutdown_t(s_ClientSocket, SHUT_RDWR, s_MainThread);
            kclose_t(s_ClientSocket, s_MainThread);

            delete l_Connection;
            l_Connection = nullptr;
            break;
        }

        // Send off for new client thread creation
        s_Server->OnHandleConnection(l_Connection);

        // Zero out the address information for the next call
        memset(&s_ClientAddress, 0, s_ClientAddressLen);
        s_ClientAddress.sin_len = s_ClientAddressLen;

        if (!s_Server->m_Running)
            break;
    }

    // Disconnects all clients, set running to false
    WriteLog(LL_Debug, "rpcserver tearing down");
    s_Server->Teardown();

    WriteLog(LL_Debug, "rpcserver exiting cleanly");
    kthread_exit();
}

void Server::OnHandleConnection(Rpc::Connection* p_Connection)
{
    // This does not need kthread_exit as it is called "flatly"
    auto kthread_add = (int(*)(void(*func)(void*), void* arg, struct proc* procptr, struct thread** tdptr, int flags, int pages, const char* fmt, ...))kdlsym(kthread_add);

    // Validate connection instance
    if (!p_Connection)
    {
        WriteLog(LL_Error, "invalid connection instance");
        return;
    }

    auto s_Framework = Mira::Framework::GetFramework();
    if (s_Framework == nullptr)
    {
        WriteLog(LL_Error, "could not get framework");
        return;
    }

    auto s_Process = s_Framework->GetInitParams()->process;
    if (s_Process == nullptr)
    {
        WriteLog(LL_Error, "could not get mira process");
        return;
    }

    // Get connection
    auto s_Ret = kthread_add(Connection::ConnectionThread, p_Connection, s_Process, reinterpret_cast<thread **>(p_Connection->Internal_GetThread()), 0, 32, "RpcConn");
    if (s_Ret < 0)
    {
        WriteLog(LL_Error, "could not start new connection thread (%d).", s_Ret);
        return;
    }
}

// This is sent from any thread, the connection has terminated and is requesting cleanup
void Server::OnConnectionDisconnected(Rpc::Connection* p_Connection)
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    if (p_Connection == nullptr)
    {
        WriteLog(LL_Error, "invalid connection");
        return;
    }
    WriteLog(LL_Debug, "client disconnect (%p).", p_Connection);

    _mtx_lock_flags(&m_Mutex, 0);
    do
    {
        auto s_ConnectionSize = ARRAYSIZE(m_Connections);
        // Iterate through all connections and disconnect them
        for (auto i = 0; i < s_ConnectionSize; ++i)
        {
            // Check that we have a valid connection
            auto l_Connection = m_Connections[i];
            if (l_Connection == nullptr)
                continue;

            if (l_Connection->GetId() != p_Connection->GetId())
                continue;

            // Attempt to get the client lock
            _mtx_lock_flags(l_Connection->GetMutex(), 0);

            // Remove the client from the list
            m_Connections[i] = nullptr;

            // Once we have excluse access, release and free
            _mtx_unlock_flags(l_Connection->GetMutex(), 0);

            WriteLog(LL_Debug, "freeing connection at (%d) (%p).", i, l_Connection);
            delete l_Connection;
            break;
        }
    } while (false);
    _mtx_unlock_flags(&m_Mutex, 0);
}

int32_t Server::GetSocketById(uint32_t p_Id)
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    int32_t s_SocketId = -1;

    _mtx_lock_flags(&m_Mutex, 0);
    for (uint32_t i = 0; i < ARRAYSIZE(m_Connections); ++i)
    {
        auto l_Connection = m_Connections[i];
        if (!l_Connection)
            continue;

        if (l_Connection->IsRunning())
            continue;

        if (p_Id != l_Connection->GetId())
            continue;

        s_SocketId = l_Connection->GetSocket();
        break;
    }
    _mtx_unlock_flags(&m_Mutex, 0);

    return s_SocketId;
}

int32_t Server::GetFreeConnectionCount()
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    int32_t s_Count = 0;

    _mtx_lock_flags(&m_Mutex, 0);
    for (auto i = 0; i < ARRAYSIZE(m_Connections); ++i)
    {
        if (m_Connections[i] == nullptr)
            s_Count++;
    }
    _mtx_unlock_flags(&m_Mutex, 0);

    return s_Count;
}

int32_t Server::GetUsedConnectionCount()
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    int32_t s_Count = 0;

    _mtx_lock_flags(&m_Mutex, 0);
    for (auto i = 0; i < ARRAYSIZE(m_Connections); ++i)
    {
        if (m_Connections[i] != nullptr)
            s_Count++;
    }
    _mtx_unlock_flags(&m_Mutex, 0);

    return s_Count;
}

int32_t Server::GetFreeConnectionIndex()
{
    auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
    auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

    int32_t s_FreeConnectionIndex = -1;

    _mtx_lock_flags(&m_Mutex, 0);
    for (auto i = 0; i < ARRAYSIZE(m_Connections); ++i)
    {
        if (m_Connections[i] == nullptr)
        {
            s_FreeConnectionIndex = i;
            break;
        }
    }
    _mtx_unlock_flags(&m_Mutex, 0);

    return s_FreeConnectionIndex;
}


code:
#pragma once
#include <Utils/IModule.hpp>
#include <Utils/Vector.hpp>

extern "C"
{
    #include <netinet/in.h>
    #include <sys/param.h>
    #include <sys/lock.h>
    #include <sys/mutex.h>
}

struct thread;

namespace Mira
{
    namespace Messaging
    {
        namespace Rpc
        {
            enum {  RpcServer_MaxConnections = 8,
                    RpcServer_DefaultPort = 9999, };
            class Connection;

            class Server : Mira::Utils::IModule
            {
            private:
                // Server address
                struct sockaddr_in m_Address;

                // Socket
                int32_t m_Socket;

                // Server port
                uint16_t m_Port;

                // Thread
                void* m_Thread;

                // Running
                volatile bool m_Running;

                // Give a id to each connection for lookup later
                uint32_t m_NextConnectionId;

                Rpc::Connection* m_Connections[RpcServer_MaxConnections];

                struct mtx m_Mutex;

            public:
                Server(uint16_t p_Port = RpcServer_DefaultPort);
                virtual ~Server();

                bool Startup();
                bool Teardown();

                void OnHandleConnection(Rpc::Connection* p_Connection);
                void OnConnectionDisconnected(Rpc::Connection* p_Connection);

                virtual const char* GetName() override { return "RpcServer"; }
                virtual bool OnLoad() override;
                virtual bool OnUnload() override;
                virtual bool OnSuspend() override;
                virtual bool OnResume() override;

            public:
                int32_t GetSocketById(uint32_t p_Id);
                int32_t GetSocketByConnection(Rpc::Connection* p_Connection);
                int32_t GetFreeConnectionCount();
                int32_t GetUsedConnectionCount();
                int32_t GetFreeConnectionIndex();

            private:
                static void ServerThread(void* p_UserData);

                bool SetClientIndex(int32_t p_Index, Rpc::Connection* p_Connection);
                void SetRunningState(bool p_IsRunning);
            };
        }
    }
}


code:
/* Generated by the protocol buffer compiler.  DO NOT EDIT! */
/* Generated from: external/rpc.proto */

/* Do not generate deprecated warnings for self */
#ifndef PROTOBUF_C__NO_DEPRECATED
#define PROTOBUF_C__NO_DEPRECATED
#endif

#include "rpc.pb-c.h"
void   rpc_header__init
                     (RpcHeader         *message)
{
  static const RpcHeader init_value = RPC_HEADER__INIT;
  *message = init_value;
}
size_t rpc_header__get_packed_size
                     (const RpcHeader *message)
{
  assert(message->base.descriptor == &rpc_header__descriptor);
  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
}
size_t rpc_header__pack
                     (const RpcHeader *message,
                      uint8_t       *out)
{
  assert(message->base.descriptor == &rpc_header__descriptor);
  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
}
size_t rpc_header__pack_to_buffer
                     (const RpcHeader *message,
                      ProtobufCBuffer *buffer)
{
  assert(message->base.descriptor == &rpc_header__descriptor);
  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
}
RpcHeader *
       rpc_header__unpack
                     (ProtobufCAllocator  *allocator,
                      size_t               len,
                      const uint8_t       *data)
{
  return (RpcHeader *)
     protobuf_c_message_unpack (&rpc_header__descriptor,
                                allocator, len, data);
}
void   rpc_header__free_unpacked
                     (RpcHeader *message,
                      ProtobufCAllocator *allocator)
{
  if(!message)
    return;
  assert(message->base.descriptor == &rpc_header__descriptor);
  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
void   rpc_transport__init
                     (RpcTransport         *message)
{
  static const RpcTransport init_value = RPC_TRANSPORT__INIT;
  *message = init_value;
}
size_t rpc_transport__get_packed_size
                     (const RpcTransport *message)
{
  assert(message->base.descriptor == &rpc_transport__descriptor);
  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
}
size_t rpc_transport__pack
                     (const RpcTransport *message,
                      uint8_t       *out)
{
  assert(message->base.descriptor == &rpc_transport__descriptor);
  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
}
size_t rpc_transport__pack_to_buffer
                     (const RpcTransport *message,
                      ProtobufCBuffer *buffer)
{
  assert(message->base.descriptor == &rpc_transport__descriptor);
  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
}
RpcTransport *
       rpc_transport__unpack
                     (ProtobufCAllocator  *allocator,
                      size_t               len,
                      const uint8_t       *data)
{
  return (RpcTransport *)
     protobuf_c_message_unpack (&rpc_transport__descriptor,
                                allocator, len, data);
}
void   rpc_transport__free_unpacked
                     (RpcTransport *message,
                      ProtobufCAllocator *allocator)
{
  if(!message)
    return;
  assert(message->base.descriptor == &rpc_transport__descriptor);
  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
static const ProtobufCFieldDescriptor rpc_header__field_descriptors[5] =
{
  {
    "magic",
    1,
    PROTOBUF_C_LABEL_NONE,
    PROTOBUF_C_TYPE_UINT32,
    0,   /* quantifier_offset */
    offsetof(RpcHeader, magic),
    NULL,
    NULL,
    0,             /* flags */
    0,NULL,NULL    /* reserved1,reserved2, etc */
  },
  {
    "category",
    2,
    PROTOBUF_C_LABEL_NONE,
    PROTOBUF_C_TYPE_ENUM,
    0,   /* quantifier_offset */
    offsetof(RpcHeader, category),
    &rpc_category__descriptor,
    NULL,
    0,             /* flags */
    0,NULL,NULL    /* reserved1,reserved2, etc */
  },
  {
    "type",
    3,
    PROTOBUF_C_LABEL_NONE,
    PROTOBUF_C_TYPE_UINT32,
    0,   /* quantifier_offset */
    offsetof(RpcHeader, type),
    NULL,
    NULL,
    0,             /* flags */
    0,NULL,NULL    /* reserved1,reserved2, etc */
  },
  {
    "error",
    4,
    PROTOBUF_C_LABEL_NONE,
    PROTOBUF_C_TYPE_INT64,
    0,   /* quantifier_offset */
    offsetof(RpcHeader, error),
    NULL,
    NULL,
    0,             /* flags */
    0,NULL,NULL    /* reserved1,reserved2, etc */
  },
  {
    "isRequest",
    5,
    PROTOBUF_C_LABEL_NONE,
    PROTOBUF_C_TYPE_BOOL,
    0,   /* quantifier_offset */
    offsetof(RpcHeader, isrequest),
    NULL,
    NULL,
    0,             /* flags */
    0,NULL,NULL    /* reserved1,reserved2, etc */
  },
};
static const unsigned rpc_header__field_indices_by_name[] = {
  1,   /* field[1] = category */
  3,   /* field[3] = error */
  4,   /* field[4] = isRequest */
  0,   /* field[0] = magic */
  2,   /* field[2] = type */
};
static const ProtobufCIntRange rpc_header__number_ranges[1 + 1] =
{
  { 1, 0 },
  { 0, 5 }
};
const ProtobufCMessageDescriptor rpc_header__descriptor =
{
  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
  "RpcHeader",
  "RpcHeader",
  "RpcHeader",
  "",
  sizeof(RpcHeader),
  5,
  rpc_header__field_descriptors,
  rpc_header__field_indices_by_name,
  1,  rpc_header__number_ranges,
  (ProtobufCMessageInit) rpc_header__init,
  NULL,NULL,NULL    /* reserved[123] */
};
static const ProtobufCFieldDescriptor rpc_transport__field_descriptors[2] =
{
  {
    "header",
    1,
    PROTOBUF_C_LABEL_NONE,
    PROTOBUF_C_TYPE_MESSAGE,
    0,   /* quantifier_offset */
    offsetof(RpcTransport, header),
    &rpc_header__descriptor,
    NULL,
    0,             /* flags */
    0,NULL,NULL    /* reserved1,reserved2, etc */
  },
  {
    "data",
    2,
    PROTOBUF_C_LABEL_NONE,
    PROTOBUF_C_TYPE_BYTES,
    0,   /* quantifier_offset */
    offsetof(RpcTransport, data),
    NULL,
    NULL,
    0,             /* flags */
    0,NULL,NULL    /* reserved1,reserved2, etc */
  },
};
static const unsigned rpc_transport__field_indices_by_name[] = {
  1,   /* field[1] = data */
  0,   /* field[0] = header */
};
static const ProtobufCIntRange rpc_transport__number_ranges[1 + 1] =
{
  { 1, 0 },
  { 0, 2 }
};
const ProtobufCMessageDescriptor rpc_transport__descriptor =
{
  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
  "RpcTransport",
  "RpcTransport",
  "RpcTransport",
  "",
  sizeof(RpcTransport),
  2,
  rpc_transport__field_descriptors,
  rpc_transport__field_indices_by_name,
  1,  rpc_transport__number_ranges,
  (ProtobufCMessageInit) rpc_transport__init,
  NULL,NULL,NULL    /* reserved[123] */
};
static const ProtobufCEnumValue rpc_category__enum_values_by_number[7] =
{
  { "NONE", "RPC_CATEGORY__NONE", 0 },
  { "SYSTEM", "RPC_CATEGORY__SYSTEM", 1 },
  { "LOG", "RPC_CATEGORY__LOG", 2 },
  { "DEBUG", "RPC_CATEGORY__DEBUG", 3 },
  { "FILE", "RPC_CATEGORY__FILE", 4 },
  { "COMMAND", "RPC_CATEGORY__COMMAND", 5 },
  { "MAX", "RPC_CATEGORY__MAX", 6 },
};
static const ProtobufCIntRange rpc_category__value_ranges[] = {
{0, 0},{0, 7}
};
static const ProtobufCEnumValueIndex rpc_category__enum_values_by_name[7] =
{
  { "COMMAND", 5 },
  { "DEBUG", 3 },
  { "FILE", 4 },
  { "LOG", 2 },
  { "MAX", 6 },
  { "NONE", 0 },
  { "SYSTEM", 1 },
};
const ProtobufCEnumDescriptor rpc_category__descriptor =
{
  PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
  "RpcCategory",
  "RpcCategory",
  "RpcCategory",
  "",
  7,
  rpc_category__enum_values_by_number,
  7,
  rpc_category__enum_values_by_name,
  1,
  rpc_category__value_ranges,
  NULL,NULL,NULL,NULL   /* reserved[1234] */
};


code:
/* Generated by the protocol buffer compiler.  DO NOT EDIT! */
/* Generated from: external/rpc.proto */

#ifndef PROTOBUF_C_external_2frpc_2eproto__INCLUDED
#define PROTOBUF_C_external_2frpc_2eproto__INCLUDED

#include <protobuf-c/protobuf-c.h>

PROTOBUF_C__BEGIN_DECLS

#if PROTOBUF_C_VERSION_NUMBER < 1003000
# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
#elif 1003001 < PROTOBUF_C_MIN_COMPILER_VERSION
# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
#endif

typedef struct _RpcHeader RpcHeader;
typedef struct _RpcTransport RpcTransport;

/* --- enums --- */

typedef enum _RpcCategory {
  RPC_CATEGORY__NONE = 0,
  RPC_CATEGORY__SYSTEM = 1,
  RPC_CATEGORY__LOG = 2,
  RPC_CATEGORY__DEBUG = 3,
  RPC_CATEGORY__FILE = 4,
  RPC_CATEGORY__COMMAND = 5,
  RPC_CATEGORY__MAX = 6
    PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(RPC_CATEGORY)
} RpcCategory;

/* --- messages --- */

struct  _RpcHeader
{
  ProtobufCMessage base;
  uint32_t magic;
  RpcCategory category;
  uint32_t type;
  int64_t error;
  protobuf_c_boolean isrequest;
};
#define RPC_HEADER__INIT \
 { PROTOBUF_C_MESSAGE_INIT (&rpc_header__descriptor) \
    , 0, RPC_CATEGORY__NONE, 0, 0, 0 }

struct  _RpcTransport
{
  ProtobufCMessage base;
  RpcHeader *header;
  ProtobufCBinaryData data;
};
#define RPC_TRANSPORT__INIT \
 { PROTOBUF_C_MESSAGE_INIT (&rpc_transport__descriptor) \
    , NULL, {0,NULL} }

/* RpcHeader methods */
void   rpc_header__init
                     (RpcHeader         *message);
size_t rpc_header__get_packed_size
                     (const RpcHeader   *message);
size_t rpc_header__pack
                     (const RpcHeader   *message,
                      uint8_t             *out);
size_t rpc_header__pack_to_buffer
                     (const RpcHeader   *message,
                      ProtobufCBuffer     *buffer);
RpcHeader *
       rpc_header__unpack
                     (ProtobufCAllocator  *allocator,
                      size_t               len,
                      const uint8_t       *data);
void   rpc_header__free_unpacked
                     (RpcHeader *message,
                      ProtobufCAllocator *allocator);
/* RpcTransport methods */
void   rpc_transport__init
                     (RpcTransport         *message);
size_t rpc_transport__get_packed_size
                     (const RpcTransport   *message);
size_t rpc_transport__pack
                     (const RpcTransport   *message,
                      uint8_t             *out);
size_t rpc_transport__pack_to_buffer
                     (const RpcTransport   *message,
                      ProtobufCBuffer     *buffer);
RpcTransport *
       rpc_transport__unpack
                     (ProtobufCAllocator  *allocator,
                      size_t               len,
                      const uint8_t       *data);
void   rpc_transport__free_unpacked
                     (RpcTransport *message,
                      ProtobufCAllocator *allocator);
/* --- per-message closures --- */

typedef void (*RpcHeader_Closure)
                 (const RpcHeader *message,
                  void *closure_data);
typedef void (*RpcTransport_Closure)
                 (const RpcTransport *message,
                  void *closure_data);

/* --- services --- */

/* --- descriptors --- */

extern const ProtobufCEnumDescriptor    rpc_category__descriptor;
extern const ProtobufCMessageDescriptor rpc_header__descriptor;
extern const ProtobufCMessageDescriptor rpc_transport__descriptor;

PROTOBUF_C__END_DECLS

#endif  /* PROTOBUF_C_external_2frpc_2eproto__INCLUDED */


code:
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

#include "Mira.hpp"
#if __has_include("GitHash.hpp")
#include "GitHash.hpp"
#endif

//
// Boot
//
#include <Boot/InitParams.hpp>
#include <Boot/Patches.hpp>

//
// Devices
//
#include <Driver/CtrlDriver.hpp>

//
// Managers
//
#include <Messaging/MessageManager.hpp>
#include <Plugins/PluginManager.hpp>
#include <Messaging/Rpc/Server.hpp>

#include <OrbisOS/Utilities.hpp>

///
/// Utilities
///
#include <Utils/Kernel.hpp>
#include <Utils/Kdlsym.hpp>
#include <Utils/Logger.hpp>
#include <Utils/SysWrappers.hpp>
#include <Utils/Types.hpp>
#include <Utils/Hook.hpp>
#include <OrbisOS/Utilities.hpp>

//
//	Free-BSD Specifics
//
extern "C"
{
	#include <sys/eventhandler.h>
	#include <sys/sysent.h>					// sysent_t
	#include <sys/proc.h>					// proc
	#include <sys/filedesc.h>				// filedesc
	#include <vm/vm.h>
	#include <vm/pmap.h>
	#include <machine/trap.h>
	#include <machine/pmap.h>
	#include <machine/psl.h>
	#include <machine/segments.h>
	#include <machine/trap.h>
}

const char* gNull = "(null)";
uint8_t* gKernelBase = nullptr;
struct logger_t* gLogger = nullptr;

Mira::Framework* Mira::Framework::m_Instance = nullptr;
Mira::Framework* Mira::Framework::GetFramework()
{
	if (m_Instance == nullptr)
		m_Instance = new Mira::Framework();

	return m_Instance;
}

Mira::Framework::Framework() :
	m_InitParams(),
	m_EventHandlersInstalled(false),
	m_SuspendTag(nullptr),
	m_ResumeTag(nullptr),
	m_ProcessExec(nullptr),
	m_ProcessExecEnd(nullptr),
	m_ProcessExit(nullptr),
	m_PluginManager(nullptr),
	m_MessageManager(nullptr),
	m_CtrlDriver(nullptr)
{

}

Mira::Framework::~Framework()
{
	if (!Terminate())
		WriteLog(LL_Error, "could not terminate successfully");
	else
		WriteLog(LL_Info, "terminated successfully");
}

extern "C" void mira_entry(void* args)
{
	// Fill the kernel base address
	gKernelBase = (uint8_t*)kernelRdmsr(0xC0000082) - kdlsym_addr_Xfast_syscall;

	cpu_disable_wp();

	Mira::Boot::Patches::install_prePatches();

	cpu_enable_wp();

    auto kthread_exit = (void(*)(void))kdlsym(kthread_exit);
	//auto kproc_exit = (void(*)(int ecode))kdlsym(kproc_exit);
	auto vmspace_alloc = (struct vmspace* (*)(vm_offset_t min, vm_offset_t max))kdlsym(vmspace_alloc);
	auto pmap_activate = (void(*)(struct thread *td))kdlsym(pmap_activate);
	auto printf = (void(*)(const char *format, ...))kdlsym(printf);
	//auto avcontrol_sleep = (void(*)(int milliseconds))kdlsym(avcontrol_sleep);

    // Let'em know we made it
	printf("[+] mira has reached stage 2\n");

    // These are the initialization parameters from the loader
    Mira::Boot::InitParams* initParams = static_cast<Mira::Boot::InitParams*>(args);
    if (initParams == nullptr)
    {
		printf("[-] no init params\n");
		kthread_exit();
		return;
	}

	printf("[+] starting logging\n");
	auto s_Logger = Mira::Utils::Logger::GetInstance();
	if (!s_Logger)
	{
		printf("[-] could not allocate logger\n");
		kthread_exit();
		return;
	}
	// Create new credentials
	WriteLog(LL_Debug, "Creating new thread credentials");
	(void)ksetuid_t(0, curthread);

	// Root and escape our thread
	if (curthread->td_ucred)
	{
		WriteLog(LL_Info, "escaping thread");

		curthread->td_ucred->cr_rgid = 0;
		curthread->td_ucred->cr_svgid = 0;

		curthread->td_ucred->cr_uid = 0;
		curthread->td_ucred->cr_ruid = 0;

		if (curthread->td_ucred->cr_prison)
			curthread->td_ucred->cr_prison = *(struct prison**)kdlsym(prison0);

		if (curthread->td_proc->p_fd)
			curthread->td_proc->p_fd->fd_rdir = curthread->td_proc->p_fd->fd_jdir = *(struct vnode**)kdlsym(rootvnode);

		// Set our auth id as debugger
		curthread->td_ucred->cr_sceAuthID = SceAuthenticationId::Decid;

		// make system credentials
		curthread->td_ucred->cr_sceCaps[0] = SceCapabilites::Max;
		curthread->td_ucred->cr_sceCaps[1] = SceCapabilites::Max;

		WriteLog(LL_Debug, "credentials rooted for new proc");
	}
#ifdef GIT_HASH
	WriteLog(LL_Debug, "gitHash: %s", GIT_HASH);
#endif
	WriteLog(LL_Debug, "payloadBase: %p", initParams->payloadBase);
	WriteLog(LL_Debug, "allocatedData: %p", initParams->allocatedBase);
	WriteLog(LL_Debug, "entryPoint: %p", initParams->entrypoint);
	WriteLog(LL_Debug, "kernelBase: %p", gKernelBase);
	WriteLog(LL_Debug, "kthread_exit: %p", kthread_exit);
	WriteLog(LL_Debug, "vmspace_alloc: %p", vmspace_alloc);
	WriteLog(LL_Debug, "pmap_activate: %p", pmap_activate);
	WriteLog(LL_Debug, "printf: %p", printf);

	// Create new vm_space
	WriteLog(LL_Debug, "Creating new vm space");
	struct vmspace* vmspace = vmspace_alloc(0, PAGE_SIZE * 2048); // Allocate 8MiB
	WriteLog(LL_Debug, "here");
	if (!vmspace)
	{
		WriteLog(LL_Error, "vmspace_alloc failed\n");
		kthread_exit();
		return;
	}

	// Assign our new vmspace to our process
	initParams->process->p_vmspace = vmspace;
	if (initParams->process == curthread->td_proc)
	{
		WriteLog(LL_Debug, "Activating vmspace physical map");
		pmap_activate(curthread);
	}

	// Because we have now forked into a new realm of fuckery
	// We need to reserve the first 3 file descriptors in our process
	WriteLog(LL_Debug, "Creating initial 3 file descriptors (0, 1, 2).");
	int descriptor = kopen_t(const_cast<char*>("/dev/console"), 1, 0, curthread);
	WriteLog(LL_Debug, "/dev/console descriptor: %d", descriptor);
	WriteLog(LL_Info, "dup2(desc, 1) result: %d", kdup2_t(descriptor, 1, curthread));
	WriteLog(LL_Info, "dup2(1, 2) result: %d", kdup2_t(1, 2, curthread));

	// Show over UART that we are running in a new process
	WriteLog(LL_Info, "oni_kernelInitialization in new process!\n");

	// Initialize miraframework
	WriteLog(LL_Debug, "Creating MiraFramework...");
	auto s_Framework = Mira::Framework::GetFramework();

	// Set the initparams so we do not lose track of it
	WriteLog(LL_Debug, "Updating initialization parameters...");
	s_Framework->SetInitParams(initParams);

	// We are successfully running
	s_Framework->GetInitParams()->isRunning = true;

	WriteLog(LL_Debug, "Initializing MiraFramework...");
	if (!s_Framework->Initialize())
	{
		WriteLog(LL_Error, "could not initialize mira framework.");
		WriteLog(LL_Debug, "MiraTerminate: %s", s_Framework->Terminate() ? "Success" : "Failure");
		kthread_exit();
		return;
	}

	// At this point we don't need kernel context anymore
	WriteLog(LL_Info, "Mira initialization complete");
	kthread_exit();
}

bool Mira::Framework::SetInitParams(Mira::Boot::InitParams* p_Params)
{
	if (p_Params == NULL)
		return false;

	m_InitParams.elfLoader = p_Params->elfLoader;
	m_InitParams.entrypoint = p_Params->entrypoint;
	m_InitParams.isElf = p_Params->isElf;
	m_InitParams.payloadBase = p_Params->payloadBase;
	m_InitParams.payloadSize = p_Params->payloadSize;
	m_InitParams.process = p_Params->process;
	m_InitParams.isRunning = p_Params->isRunning;
	m_InitParams.allocatedBase = p_Params->allocatedBase;

	return true;
}

bool Mira::Framework::Initialize()
{
	// TODO: Load settings
	WriteLog(LL_Warn, "FIXME: loading settings not implemented!!!!");

	// Initialize message manager
	WriteLog(LL_Debug, "Initializing the message manager");
	m_MessageManager = new Mira::Messaging::MessageManager();
	if (m_MessageManager == nullptr)
	{
		WriteLog(LL_Error, "could not allocate message manager.");
		return false;
	}

	// Initialize plugin manager
	WriteLog(LL_Debug, "Initializing the plugin manager");
	m_PluginManager = new Mira::Plugins::PluginManager();
	if (m_PluginManager == nullptr)
	{
		WriteLog(LL_Error, "could not allocate plugin manager.");
		return false;
	}

	// Load the plugin manager
	WriteLog(LL_Debug, "Loading plugin manager");
	if (!m_PluginManager->OnLoad())
	{
		WriteLog(LL_Error, "could not initialize default plugins");
		return false;
	}

	// Install eventhandler's
	WriteLog(LL_Debug, "Installing event handlers");
	if (!InstallEventHandlers())
		WriteLog(LL_Error, "could not register event handlers");

	// TODO: Install needed hooks for Mira
	WriteLog(LL_Warn, "FIXME: Syscall table hooks not implemented!!!!");

	// Install device driver
	WriteLog(LL_Warn, "Initializing the /dev/mira control driver");
	m_CtrlDriver = new Mira::Driver::CtrlDriver();
	if (m_CtrlDriver == nullptr)
	{
		WriteLog(LL_Error, "could not allocate control driver.");
		return false;
	}

	// Set the running flag
	m_InitParams.isRunning = true;

  // Mira is now ready ! Now Killing SceShellUI for relaunching UI Process :D
  struct proc* ui_proc = Mira::OrbisOS::Utilities::FindProcessByName("SceShellUI");
  if (ui_proc) {
      Mira::OrbisOS::Utilities::KillProcess(ui_proc);
  } else {
      WriteLog(LL_Error, "Unable to find SceShellUI Process !");
  }

	return true;
}

bool Mira::Framework::Terminate()
{
	// Unload the plugin manager
	if (m_PluginManager && !m_PluginManager->OnUnload())
		WriteLog(LL_Error, "could not unload plugin manager");

	// Free the plugin manager
	delete m_PluginManager;
	m_PluginManager = nullptr;

	// Remove all eventhandlers
	if (!RemoveEventHandlers())
		WriteLog(LL_Error, "could not remove event handlers");

	// Remove the device driver
	if (m_CtrlDriver != nullptr)
	{
		delete m_CtrlDriver;
		m_CtrlDriver = nullptr;
	}

	// Update our running state, to allow the proc to terminate
	m_InitParams.isRunning = false;

	return true;
}

struct thread* Mira::Framework::GetMainThread()
{
	auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
	auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);

	auto s_Process = m_InitParams.process;
	if (s_Process == nullptr)
		return nullptr;

	_mtx_lock_flags(&s_Process->p_mtx, 0);
	struct thread* s_Thread = s_Process->p_singlethread;
	if (s_Thread == nullptr)
		s_Thread = FIRST_THREAD_IN_PROC(s_Process);
	_mtx_unlock_flags(&s_Process->p_mtx, 0);

	return s_Thread;
}

struct thread* Mira::Framework::GetSyscoreThread()
{
	auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
	auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);
	auto s_Process = OrbisOS::Utilities::FindProcessByName("SceSysCore.elf");
	if (s_Process == nullptr)
	{
		WriteLog(LL_Error, "could not get SceSysCore process.");
		return nullptr;
	}

	struct thread* s_MainThread = nullptr;

	_mtx_lock_flags(&s_Process->p_mtx, 0);
	s_MainThread = FIRST_THREAD_IN_PROC(s_Process);
	_mtx_unlock_flags(&s_Process->p_mtx, 0);

	return s_MainThread;
}

struct thread* Mira::Framework::GetShellcoreThread()
{
	auto _mtx_lock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_lock_flags);
	auto _mtx_unlock_flags = (void(*)(struct mtx *mutex, int flags))kdlsym(_mtx_unlock_flags);
	struct ::proc* s_Process = OrbisOS::Utilities::FindProcessByName("SceShellCore");
	if (s_Process == nullptr)
	{
		WriteLog(LL_Error, "could not get SceShellCore process.");
		return nullptr;
	}

	struct thread* s_MainThread = nullptr;

	_mtx_lock_flags(&s_Process->p_mtx, 0);
	s_MainThread = FIRST_THREAD_IN_PROC(s_Process);
	_mtx_unlock_flags(&s_Process->p_mtx, 0);

	return s_MainThread;
}

bool Mira::Framework::InstallEventHandlers()
{
	if (m_EventHandlersInstalled)
		return false;

	auto eventhandler_register = (eventhandler_tag
	(*)(struct eventhandler_list *list, const char *name,
		void *func, void *arg, int priority))kdlsym(eventhandler_register);

	// Register our event handlers
	//const int32_t prio = 1337;
	m_SuspendTag = EVENTHANDLER_REGISTER(system_suspend_phase1, reinterpret_cast<void*>(Mira::Framework::OnMiraSuspend), GetFramework(), EVENTHANDLER_PRI_FIRST);
	m_ResumeTag = EVENTHANDLER_REGISTER(system_resume_phase1, reinterpret_cast<void*>(Mira::Framework::OnMiraResume), GetFramework(), EVENTHANDLER_PRI_LAST);

	m_ProcessExec = EVENTHANDLER_REGISTER(process_exec, reinterpret_cast<void*>(Mira::Framework::OnMiraProcessExec), GetFramework(), EVENTHANDLER_PRI_ANY);
	m_ProcessExecEnd = EVENTHANDLER_REGISTER(process_exec_end, reinterpret_cast<void*>(Mira::Framework::OnMiraProcessExecEnd), GetFramework(), EVENTHANDLER_PRI_LAST);
	m_ProcessExit = EVENTHANDLER_REGISTER(process_exit, reinterpret_cast<void*>(Mira::Framework::OnMiraProcessExit), GetFramework(), EVENTHANDLER_PRI_FIRST);

	// Set our event handlers as installed
	m_EventHandlersInstalled = true;

	return true;
}

bool Mira::Framework::RemoveEventHandlers()
{
	if (!m_EventHandlersInstalled)
		return false;

	if (m_SuspendTag == nullptr || m_ResumeTag == nullptr)
		return false;

	auto eventhandler_deregister = (void(*)(struct eventhandler_list* a, struct eventhandler_entry* b))kdlsym(eventhandler_deregister);
	auto eventhandler_find_list = (struct eventhandler_list * (*)(const char *name))kdlsym(eventhandler_find_list);

	EVENTHANDLER_DEREGISTER(system_suspend_phase1, m_SuspendTag);
	EVENTHANDLER_DEREGISTER(system_resume_phase1, m_ResumeTag);

	EVENTHANDLER_DEREGISTER(process_exec, m_ProcessExec);
	EVENTHANDLER_DEREGISTER(process_exec_end, m_ProcessExecEnd);
	EVENTHANDLER_DEREGISTER(process_exit, m_ProcessExit);

	m_SuspendTag = nullptr;
	m_ResumeTag = nullptr;

	m_ProcessExec = nullptr;
	m_ProcessExecEnd = nullptr;
	m_ProcessExit = nullptr;

	m_EventHandlersInstalled = false;

	return true;
}

void Mira::Framework::OnMiraSuspend(void* __unused p_Reserved)
{
	if (GetFramework() == nullptr)
		return;

	WriteLog(LL_Warn, "SUPSEND SUSPEND SUSPEND");

	auto s_PluginManager = GetFramework()->m_PluginManager;
	if (s_PluginManager)
		s_PluginManager->OnSuspend();
}

void Mira::Framework::OnMiraResume(void* __unused p_Reserved)
{
	if (GetFramework() == nullptr)
		return;

	WriteLog(LL_Warn, "RESUME RESUME RESUME");

	// Handle resume events
	auto s_PluginManager = GetFramework()->GetPluginManager();
	if (s_PluginManager)
		s_PluginManager->OnResume();
}

void Mira::Framework::OnMiraShutdown(void* __unused p_Reserved)
{
	auto kproc_exit = (int(*)(int code))kdlsym(kproc_exit);
	auto kthread_exit = (void(*)(void))kdlsym(kthread_exit);

	WriteLog(LL_Warn, "SHUTDOWN SHUTDOWN SHUTDOWN");

	if (GetFramework() == nullptr)
		return;

	if (!GetFramework()->Terminate())
	{
		WriteLog(LL_Error, "could not terminate cleanly");
		return;
	}

	kproc_exit(0);
	kthread_exit();
}

void Mira::Framework::OnMiraProcessExec(void* p_Framework, struct proc* p_Process)
{
	WriteLog(LL_Warn, "Process Executing: ");

	auto s_Framework = GetFramework();
	if (s_Framework == nullptr)
		return;

	auto s_PluginManager = s_Framework->GetPluginManager();
	if (s_PluginManager)
	{
		if (!s_PluginManager->OnProcessExec(p_Process))
			WriteLog(LL_Error, "could not call exit process on plugin manager.");
	}

	if (s_Framework->m_CtrlDriver)
		s_Framework->m_CtrlDriver->OnProcessExec(p_Framework, p_Process);
}

void Mira::Framework::OnMiraProcessExecEnd(void* p_Framework, struct proc* p_Process)
{
	WriteLog(LL_Warn, "Process Ending: ");

	auto s_Framework = GetFramework();
	if (s_Framework == nullptr)
		return;

	auto s_PluginManager = s_Framework->GetPluginManager();
	if (s_PluginManager)
	{
		if (!s_PluginManager->OnProcessExecEnd(p_Process))
			WriteLog(LL_Error, "could not call end process on plugin manager.");
	}
}

void Mira::Framework::OnMiraProcessExit(void* p_Framework, struct proc* p_Process)
{
	WriteLog(LL_Warn, "Process Exiting: ");

	if (GetFramework() == nullptr)
		return;

	if (!GetFramework()->GetPluginManager()->OnProcessExit(p_Process))
	{
		WriteLog(LL_Error, "could not call exit process on plugin manager.");
	}
}


code:
#pragma once
#include <Boot/InitParams.hpp>

struct eventhandler_entry;
struct eventhandler_list;
typedef eventhandler_entry* eventhandler_tag;

extern "C"
{
    #include <sys/param.h>
    #include <sys/proc.h>
    #include <sys/sysproto.h> // ioctl_args

    void mira_entry(void* args);
};

extern "C"

namespace Mira
{
    namespace OrbisOS
    {
        class ThreadManager;
    }

    namespace Driver
    {
        class CtrlDriver;
    }

    namespace Plugins
    {
        class PluginManager;
    }

    namespace Messaging
    {
        namespace Rpc
        {
            class Server;
        }

        class MessageManager;
    }

    namespace Utils
    {
        class Hook;
    }

    class Framework
    {
    private:
        static Framework* m_Instance;
        Mira::Boot::InitParams m_InitParams;

        bool m_EventHandlersInstalled;

        // System state events
        struct eventhandler_entry* m_SuspendTag;
        struct eventhandler_entry* m_ResumeTag;

        // Process system events
        struct eventhandler_entry* m_ProcessExec;
        struct eventhandler_entry* m_ProcessExecEnd;
        struct eventhandler_entry* m_ProcessExit;

        Mira::Plugins::PluginManager* m_PluginManager;
        Mira::Messaging::MessageManager* m_MessageManager;

        Mira::Driver::CtrlDriver* m_CtrlDriver;

    public:
        static Framework* GetFramework();

    protected:
        Framework();
        ~Framework();

        bool InstallEventHandlers();
        bool RemoveEventHandlers();

    public:
        bool SetInitParams(Mira::Boot::InitParams* p_Params);
        Mira::Boot::InitParams* GetInitParams() { return &m_InitParams; }

        bool Initialize();
        bool Terminate();

        Mira::Plugins::PluginManager* GetPluginManager() { return m_PluginManager; }
        Mira::Messaging::MessageManager* GetMessageManager() { return m_MessageManager; }

        struct thread* GetMainThread();
        struct thread* GetSyscoreThread();
        struct thread* GetShellcoreThread();

    private:
        static void OnMiraSuspend(void* __unused p_Reserved);
        static void OnMiraResume(void* __unused p_Reserved);
        static void OnMiraShutdown(void* __unused p_Reserved);

        static void OnMiraProcessExec(void* p_Framework, struct proc* p_Process);
        static void OnMiraProcessExecEnd(void* p_Framework, struct proc* p_Process);
        static void OnMiraProcessExit(void* p_Framework, struct proc* p_Process);

    };
}


code:
#pragma once
#include <Utils/Types.hpp>
#include <Boot/Config.hpp>

extern "C"
{
    #include <sys/param.h>
    #include <sys/lock.h>
    #include <sys/mutex.h>
};

namespace Mira
{
    namespace OrbisOS
    {
        typedef enum _SelfFormat
        {
            None,
            Elf,
            Self,
            Count
        } SelfFormat;

        enum
        {
            EKPFS_SIZE = 0x20,
            EEKPFS_SIZE = 0x100,
            PFS_SEED_SIZE = 0x10,
            PFS_FINAL_KEY_SIZE = 0x20,
            SIZEOF_PFS_KEY_BLOB = 0x140,
            CONTENT_KEY_SEED_SIZE = 0x10,
            SELF_KEY_SEED_SIZE = 0x10,
            EEKC_SIZE = 0x20,
            MAX_FAKE_KEYS = 32,
            SIZEOF_RSA_KEY = 0x48,
            PFS_FAKE_OBF_KEY_ID = 0x1337,
            SIZEOF_PFS_HEADER = 0x5A0,

            // RIF
            RIF_DATA_SIZE = 0x90,
            RIF_DIGEST_SIZE = 0x10,
            RIF_KEY_TABLE_SIZE = 0x230,
            RIF_MAX_KEY_SIZE = 0x20,
            SIZEOF_ACTDAT = 0x200,
            SIZEOF_RIF = 0x400,
            RIF_PAYLOAD_SIZE = (RIF_DIGEST_SIZE + RIF_DATA_SIZE),

            #define CCP_OP(cmd) (cmd >> 24)
            CCP_MAX_PAYLOAD_SIZE = 0x88,
            CCP_OP_AES =  0,
            CCP_OP_XTS = 2,
            CCP_OP_HMAC= 9,
            CCP_USE_KEY_FROM_SLOT   = (1 << 18),
            CCP_GENERATE_KEY_AT_SLOT= (1 << 19),
            CCP_USE_KEY_HANDLE      = (1 << 20),

            SCE_SBL_ERROR_NPDRM_ENOTSUP = 0x800F0A25,
            SIZEOF_SBL_KEY_RBTREE_ENTRY = 0xA8, // sceSblKeymgrSetKey
            SIZEOF_SBL_MAP_LIST_ENTRY = 0x50, // sceSblDriverMapPages
            TYPE_SBL_KEY_RBTREE_ENTRY_DESC_OFFSET = 0x04,
            TYPE_SBL_KEY_RBTREE_ENTRY_LOCKED_OFFSET = 0x80,
            SIZEOF_SBL_KEY_DESC = 0x7C, // sceSblKeymgrSetKey
            SBL_MSG_SERVICE_MAILBOX_MAX_SIZE = 0x80,
            SBL_MSG_CCP = 0x8,

            #define SWAP_16(x) ((((uint16_t)(x) & 0xff) << 8) | ((uint16_t)(x) >> 8))
            #define BE16(val) SWAP_16(val)
            #define LE32(val) (val)
        };

        typedef struct _SelfHeader
        {
            uint32_t magic;
            uint8_t version;
            uint8_t mode;
            uint8_t endian;
            uint8_t attr;
            uint32_t keyType;
            uint16_t headerSize;
            uint16_t metaSize;
            uint64_t fileSize;
            uint16_t numEntries;
            uint16_t flags;
        } SelfHeader;
        static_assert(offsetof(SelfHeader, magic) == 0x00);
        static_assert(offsetof(SelfHeader, version) == 0x04);
        static_assert(offsetof(SelfHeader, mode) == 0x05);
        static_assert(offsetof(SelfHeader, endian) == 0x06);
        static_assert(offsetof(SelfHeader, attr) == 0x07);
        static_assert(offsetof(SelfHeader, keyType) == 0x08);
        static_assert(offsetof(SelfHeader, headerSize) == 0x0C);
        static_assert(offsetof(SelfHeader, metaSize) == 0x0E);
        static_assert(offsetof(SelfHeader, fileSize) == 0x10);
        static_assert(offsetof(SelfHeader, numEntries) == 0x18);
        static_assert(offsetof(SelfHeader, flags) == 0x1A);
        static_assert(sizeof(SelfHeader) == 0x20);

        typedef struct _SelfContext
        {
            SelfFormat format;          // 0x00
            int32_t elfAuthType;        // 0x04
            uint32_t totalHeaderSize;   // 0x08
            uint32_t unk12;             // 0x0C
            void* segment;              // 0x10
            uint32_t unk24;             // 0x18
            int32_t contextId;          // 0x1C
            uint64_t serviceId;         // 0x20
            uint64_t unk40;             // 0x28
            int32_t bufferId;           // 0x30
            uint32_t unk52;             // 0x34
            _SelfHeader* header;        // 0x38
            struct mtx lock;            // 0x40
        } SelfContext;
        static_assert(offsetof(SelfContext, format) == 0x00);
        static_assert(offsetof(SelfContext, elfAuthType) == 0x04);
        static_assert(offsetof(SelfContext, totalHeaderSize) == 0x08);
        static_assert(offsetof(SelfContext, unk12) == 0x0C);
        static_assert(offsetof(SelfContext, segment) == 0x10);
        static_assert(offsetof(SelfContext, contextId) == 0x1C);
        static_assert(offsetof(SelfContext, serviceId) == 0x20);
        static_assert(offsetof(SelfContext, bufferId) == 0x30);
        static_assert(offsetof(SelfContext, unk52) == 0x34);
        static_assert(offsetof(SelfContext, header) == 0x38);
        static_assert(offsetof(SelfContext, lock) == 0x40);
        static_assert(sizeof(SelfContext) == 0x60);

        typedef struct _SelfEntry
        {
            uint64_t props;
            uint64_t offset;
            uint64_t fileSize;
            uint64_t memorySize;
        } SelfEntry;
        static_assert(offsetof(SelfEntry, props) == 0x00);
        static_assert(offsetof(SelfEntry, offset) == 0x08);
        static_assert(offsetof(SelfEntry, fileSize) == 0x10);
        static_assert(offsetof(SelfEntry, memorySize) == 0x18);
        static_assert(sizeof(SelfEntry) == 0x20);

        typedef struct _SelfExInfo
        {
            uint64_t paid;
            uint64_t ptype;
            uint64_t appVersion;
            uint64_t firmwareVersion;
            uint8_t digest[0x20];
        } SelfExInfo;
        static_assert(offsetof(SelfExInfo, paid) == 0x00);
        static_assert(offsetof(SelfExInfo, ptype) == 0x08);
        static_assert(offsetof(SelfExInfo, appVersion) == 0x10);
        static_assert(offsetof(SelfExInfo, firmwareVersion) == 0x18);
        static_assert(offsetof(SelfExInfo, digest) == 0x20);
        static_assert(sizeof(SelfExInfo) == 0x40);

        typedef struct _SelfAuthInfo
        {
            uint64_t paid;
            uint64_t caps[4];
            uint64_t attrs[4];
            uint8_t unk[0x40];
        } SelfAuthInfo;
        static_assert(offsetof(SelfAuthInfo, paid) == 0x00);
        static_assert(offsetof(SelfAuthInfo, caps) == 0x08);
        static_assert(offsetof(SelfAuthInfo, attrs) == 0x28);
        static_assert(offsetof(SelfAuthInfo, unk) == 0x48);
        static_assert(sizeof(SelfAuthInfo) == 0x88);

        typedef struct _SelfFakeAuthInfo
        {
            uint64_t size;
            SelfAuthInfo info;
        } SelfFakeAuthInfo;
        static_assert(offsetof(SelfFakeAuthInfo, size) == 0x00);
        static_assert(offsetof(SelfFakeAuthInfo, info) == 0x08);
        static_assert(sizeof(SelfFakeAuthInfo) == sizeof(uint64_t) + sizeof(SelfAuthInfo));

        typedef struct _MailboxMessage
        {
            int16_t funcId; // 2
            char pad02[2];
            int32_t retVal; // Return Value
            uint64_t unk08;
            uint32_t unk16;
            uint32_t unk20;
            uint64_t unk24;
            uint64_t unk32;
            uint64_t unk40;
            uint32_t unk48;
            char unk52[76];
        } MailboxMessage;
        static_assert(offsetof(MailboxMessage, unk08) == 8);
        static_assert(offsetof(MailboxMessage, retVal) == 4);
        static_assert(offsetof(MailboxMessage, unk24) == 24);
        static_assert(offsetof(MailboxMessage, unk48) == 48);
        static_assert(sizeof(MailboxMessage) == 0x80);

        typedef union _SblKeyDesc
        {
            struct _Pfs
            {
                uint16_t obfuscatedKeyId;
                uint16_t keySize;
                uint8_t escrowedKey[0x20];
            } Pfs;
            struct _Portability
            {
                uint16_t command;
                uint16_t pad;
                uint16_t keyId;
            } Portability;
            uint8_t raw[SIZEOF_SBL_KEY_DESC];
        } SblKeyDesc;
        static_assert(sizeof(SblKeyDesc) == 0x7C);

        typedef struct _SblKeySlotDesc
        {
            uint32_t keyId;
            uint32_t unk04;
            uint32_t keyHandle; // -1 if freed
            uint32_t unk12;
            TAILQ_ENTRY(_SblKeySlotDesc) list;
        } SblKeySlotDesc;
        static_assert(sizeof(SblKeySlotDesc) == 0x20);

        TAILQ_HEAD(_SblKeySlotQueue, _SblKeySlotDesc);

        typedef struct _SblKeyRbtreeEntry
        {
            uint32_t handle;
            uint32_t occupied;
            SblKeyDesc desc;
            uint8_t pad[0x4];
            //uint32_t locked; // this seems wrong, it says 0x80, but that's in the SblKeyDesc??
            struct _SblKeyRbtreeEntry* left;
            struct _SblKeyRbtreeEntry* right;
            struct _SblKeyRbtreeEntry* parent;
            uint32_t set;
        } SblKeyRbtreeEntry;
        static_assert(offsetof(SblKeyRbtreeEntry, handle) == 0x00);
        static_assert(offsetof(SblKeyRbtreeEntry, occupied) == 0x04);
        static_assert(offsetof(SblKeyRbtreeEntry, desc) == 0x08);
        //static_assert(offsetof(SblKeyRbtreeEntry, locked) == 0x80);
        static_assert(offsetof(SblKeyRbtreeEntry, left) == 0x88);
        static_assert(offsetof(SblKeyRbtreeEntry, right) == 0x90);
        static_assert(offsetof(SblKeyRbtreeEntry, parent) == 0x98);
        static_assert(offsetof(SblKeyRbtreeEntry, set) == 0xA0);
        static_assert(sizeof(SblKeyRbtreeEntry) == 0xA8);

        typedef struct _SblMapListEntry
        {
            struct _SblMapListEntry* next;
            struct _SblMapListEntry* prev;
            uint64_t cpuVa;
            uint32_t numPageGroups;
            uint64_t gpuVa;
            void* pageGroups;
            uint32_t numPages;
            uint64_t flags;
            struct proc* proc;
            void* vmPage;
        } SblMapListEntry;
        static_assert(offsetof(SblMapListEntry, next) == 0x00);
        static_assert(offsetof(SblMapListEntry, prev) == 0x08);
        static_assert(offsetof(SblMapListEntry, cpuVa) == 0x10);
        static_assert(offsetof(SblMapListEntry, numPageGroups) == 0x18);
        static_assert(offsetof(SblMapListEntry, gpuVa) == 0x20);
        static_assert(offsetof(SblMapListEntry, pageGroups) == 0x28);
        static_assert(offsetof(SblMapListEntry, numPages) == 0x30);
        static_assert(offsetof(SblMapListEntry, flags) == 0x38);
        static_assert(offsetof(SblMapListEntry, proc) == 0x40);
        static_assert(offsetof(SblMapListEntry, vmPage) == 0x48);
        static_assert(sizeof(SblMapListEntry) == 0x50);

        typedef struct _FakeKeyDesc
        {
            uint8_t key[0x20];
            bool occupied;
        } FakeKeyDesc;

        typedef struct _FakeKeyD
        {
            uint32_t index;
            uint8_t seed[PFS_SEED_SIZE];
        } FakeKeyD;

        typedef struct _Ekc
        {
            uint8_t contentKeySeed[CONTENT_KEY_SEED_SIZE];
            uint8_t selfKeySeed[SELF_KEY_SEED_SIZE];
        } Ekc;

        struct ccp_link
        {
            void* p;
        };

        union ccp_op
        {
            struct
            {
                uint32_t cmd;
                uint32_t status;
            } common;
            struct
            {
                uint32_t cmd;
                uint32_t status;
                uint64_t data_size;
                uint64_t in_data;
                uint64_t out_data;
                union
                {
                    uint32_t key_index;
                    uint8_t key[0x20];
                };
                uint8_t iv[0x10];
            } aes;
            uint8_t buf[CCP_MAX_PAYLOAD_SIZE];
        };

        struct ccp_msg
        {
            union ccp_op op;
            uint32_t index;
            uint32_t result;
            TAILQ_ENTRY(ccp_msg) next;
            uint64_t message_id;
            LIST_ENTRY(ccp_link) links;
        };

        struct ccp_req
        {
            TAILQ_HEAD(, ccp_msg) msgs;
            void (*cb)(void* arg, int result);
            void* arg;
            uint64_t message_id;
            LIST_ENTRY(ccp_link) links;
        };

        typedef union _SblMsgService
        {
            struct
            {
                union ccp_op op;
            } ccp;

        } SblMsgService;

        typedef struct _SblMsgHeader
        {
            uint32_t cmd;
            uint32_t status;
            uint64_t message_id;
            uint64_t extended_msgs;
        } _SblMsgHeader;

        typedef struct _SblMsg
        {
            _SblMsgHeader hdr;
            union
            {
                SblMsgService service;
                uint8_t raw[0x1000];
            };

        } SblMsg;

        typedef struct _RifKeyBlob
        {
            Ekc eekc;
            uint8_t entitlementKey[0x10];
        } RifKeyBlob;

        typedef union _PfsKeyBlob
        {
            struct _In
            {
                uint8_t eekpfs[EEKPFS_SIZE];
                Ekc eekc;
                uint32_t pubkeyVer; /* 0x1/0x80000001/0xC0000001 */
                uint32_t keyVer;    /* 1 (if (rif_ver_major & 0x1) != 0, then pfs_key_ver=1, otherwise pfs_key_ver=0) */
                uint64_t headerGva;
                uint32_t headerSize;
                uint32_t type;
                uint32_t finalized;
                uint32_t isDisc;
            } In;
            struct _Out
            {
                uint8_t escrowedKeys[0x40];
            } Out;

        } PfsKeyBlob;
        static_assert(sizeof(_PfsKeyBlob) == SIZEOF_PFS_KEY_BLOB);

        typedef union _KeymgrPayload
        {
            struct
            {
                uint32_t cmd;
                uint32_t status;
                uint64_t data;
            };
            uint8_t buf[0x80];
        } KeymgrPayload;

        typedef struct _RsaKey
        {
            uint8_t _pad00[0x20];
            uint8_t* p;
            uint8_t* q;
            uint8_t* dmp1;
            uint8_t* dmq1;
            uint8_t* iqmp;
        } RsaKey;
        static_assert(offsetof(_RsaKey, p) == 0x20);
        static_assert(offsetof(_RsaKey, q) == 0x28);
        static_assert(offsetof(_RsaKey, dmp1) == 0x30);
        static_assert(offsetof(_RsaKey, dmq1) == 0x38);
        static_assert(offsetof(_RsaKey, iqmp) == 0x40);
        static_assert(sizeof(_RsaKey) == SIZEOF_RSA_KEY);

        typedef struct _ActDat
        {
            uint32_t magic;
            uint16_t versionMajor;
            uint16_t versionMinor;
            uint64_t accountId;
            uint64_t startTime;
            uint64_t endTime;
            uint64_t flags;
            uint32_t unk3;
            uint32_t unk4;
            uint8_t _pad30[0x30];
            uint8_t openPsidHash[0x20];
            uint8_t staticPerConsoleData1[0x20];
            uint8_t digest[0x10];
            uint8_t keyTable[0x20];
            uint8_t staticPerConsoleData2[0x10];
            uint8_t staticPerConsoleData3[0x20];
            uint8_t signature[0x100];
        } ActDat;
        static_assert(offsetof(_ActDat, magic) == 0x00);
        static_assert(offsetof(_ActDat, versionMajor) == 0x04);
        static_assert(offsetof(_ActDat, versionMinor) == 0x06);
        static_assert(offsetof(_ActDat, accountId) == 0x08);
        static_assert(offsetof(_ActDat, startTime) == 0x10);
        static_assert(offsetof(_ActDat, endTime) == 0x18);
        static_assert(offsetof(_ActDat, flags) == 0x20);
        static_assert(offsetof(_ActDat, unk3) == 0x28);
        static_assert(offsetof(_ActDat, unk4) == 0x2C);
        static_assert(offsetof(_ActDat, openPsidHash) == 0x60);
        static_assert(offsetof(_ActDat, staticPerConsoleData1) == 0x80);
        static_assert(offsetof(_ActDat, digest) == 0xA0);
        static_assert(offsetof(_ActDat, keyTable) == 0xB0);
        static_assert(offsetof(_ActDat, staticPerConsoleData2) == 0xD0);
        static_assert(offsetof(_ActDat, staticPerConsoleData3) == 0xE0);
        static_assert(offsetof(_ActDat, signature) == 0x100);
        static_assert(sizeof(_ActDat) == SIZEOF_ACTDAT);

        typedef struct _Rif
        {
            uint32_t magic;
            uint16_t versionMajor;
            uint16_t versionMinor;
            uint64_t accountId;
            uint64_t startTime;
            uint64_t endTime;
            char contentId[0x30];
            uint16_t format;
            uint16_t drmType;
            uint16_t contentType;
            uint16_t skuFlag;
            uint64_t contentFlags;
            uint32_t iroTag;
            uint32_t ekcVersion;
            uint8_t _pad6A[2];
            uint16_t unk3;
            uint16_t unk4;
            uint8_t _pad6E[0x1F2];
            uint8_t digest[0x10];
            uint8_t data[RIF_DATA_SIZE];
            uint8_t signature[0x100];
        } Rif;
        static_assert(offsetof(_Rif, magic) == 0x00);
        static_assert(offsetof(_Rif, versionMajor) == 0x04);
        static_assert(offsetof(_Rif, versionMinor) == 0x06);
        static_assert(offsetof(_Rif, accountId) == 0x08);
        static_assert(offsetof(_Rif, startTime) == 0x10);
        static_assert(offsetof(_Rif, endTime) == 0x18);
        static_assert(offsetof(_Rif, contentId) == 0x20);
        static_assert(offsetof(_Rif, format) == 0x50);
        static_assert(offsetof(_Rif, drmType) == 0x52);
        static_assert(offsetof(_Rif, contentType) == 0x54);
        static_assert(offsetof(_Rif, skuFlag) == 0x56);
        static_assert(offsetof(_Rif, contentFlags) == 0x58);
        static_assert(offsetof(_Rif, iroTag) == 0x60);
        static_assert(offsetof(_Rif, ekcVersion) == 0x64);
        static_assert(offsetof(_Rif, unk3) == 0x6A);
        static_assert(offsetof(_Rif, unk4) == 0x6C);
        static_assert(offsetof(_Rif, digest) == 0x260);
        static_assert(offsetof(_Rif, data) == 0x270);
        static_assert(offsetof(_Rif, signature) == 0x300);
        static_assert(sizeof(_Rif) == SIZEOF_RIF);

        typedef struct _RsaBuffer
        {
            uint8_t* ptr;
            size_t size;
        } RsaBuffer;

        typedef struct _PfsHeader
        {
            uint8_t _pad00[0x370];
            uint8_t cryptSeed[0x10];
            uint8_t _pad380[0x220];
        } PfsHeader;
        static_assert(offsetof(_PfsHeader, cryptSeed) == 0x370);
        static_assert(sizeof(_PfsHeader) == SIZEOF_PFS_HEADER);

                typedef union _KeymgrResponse
        {
            struct
            {
                uint32_t type;
                uint8_t key[RIF_MAX_KEY_SIZE];
                uint8_t data[RIF_DIGEST_SIZE + RIF_DATA_SIZE];
            } DecryptRif;
            struct
            {
                uint8_t raw[SIZEOF_RIF];
            } DecryptEntireRif;
        } KeymgrResponse;

        typedef union _KeymgrRequest
        {
            struct
            {
                uint32_t type;
                uint8_t key[RIF_MAX_KEY_SIZE];
                uint8_t data[RIF_DIGEST_SIZE + RIF_DATA_SIZE];
            } DecryptRif;

//#if MIRA_PLATFORM == MIRA_PLATFORM_ORBIS_BSD_455 || MIRA_PLATFORM == MIRA_PLATFORM_ORBIS_BSD_501 || MIRA_PLATFORM == MIRA_PLATFORM_ORBIS_BSD_555
            struct
            {
                _Rif rif;
                uint8_t keyTable[RIF_KEY_TABLE_SIZE];
                uint64_t timestamp;
                int status;
            } DecryptEntireRif;
//# endif
        } KeymgrRequest;
    }
}