mirror of
https://github.com/Atmosphere-NX/Atmosphere-libs.git
synced 2025-06-21 19:12:42 +02:00
199 lines
19 KiB
C++
199 lines
19 KiB
C++
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#pragma once
|
|
#include <vapours.hpp>
|
|
#include <stratosphere/tipc/tipc_message_types.hpp>
|
|
#include <stratosphere/tipc/tipc_service_object_base.hpp>
|
|
#include <stratosphere/tipc/impl/tipc_impl_command_serialization.hpp>
|
|
|
|
namespace ams::tipc::impl {
|
|
|
|
template<typename T>
|
|
concept HasDefaultServiceCommandProcessor = requires (T &t, const MessageBuffer &message_buffer) {
|
|
{ t.ProcessDefaultServiceCommand(message_buffer) } -> std::same_as<Result>;
|
|
};
|
|
|
|
struct SyncFunctionTraits {
|
|
public:
|
|
template<typename R, typename C, typename... A>
|
|
static std::tuple<A...> GetArgsImpl(R(C::*)(A...));
|
|
};
|
|
|
|
template<auto F>
|
|
using SyncFunctionArgsType = decltype(SyncFunctionTraits::GetArgsImpl(F));
|
|
|
|
#define AMS_TIPC_IMPL_DEFINE_SYNC_METHOD_HOLDER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
|
struct NAME##ArgumentsFunctionHolder { RETURN f ARGS; };
|
|
|
|
#define AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
|
using NAME##ArgumentsType = ::ams::tipc::impl::SyncFunctionArgsType<&NAME##ArgumentsFunctionHolder::f>;
|
|
|
|
#define AMS_TIPC_IMPL_GET_MAXIMUM_REQUEST_SIZE(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
|
, ::ams::tipc::impl::CommandMetaInfo<CMD_ID + 0x10, NAME##ArgumentsType>::InMessageTotalSize
|
|
|
|
#define AMS_TIPC_IMPL_GET_MAXIMUM_RESPONSE_SIZE(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
|
, ::ams::tipc::impl::CommandMetaInfo<CMD_ID + 0x10, NAME##ArgumentsType>::OutMessageTotalSize
|
|
|
|
#define AMS_TIPC_IMPL_DEFINE_INTERFACE(BASECLASS, CLASSNAME, CMD_MACRO) \
|
|
class CLASSNAME : public BASECLASS { \
|
|
private: \
|
|
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_DEFINE_SYNC_METHOD_HOLDER) \
|
|
public: \
|
|
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS) \
|
|
public: \
|
|
static constexpr size_t MaximumRequestSize = std::max<size_t>({ \
|
|
0 \
|
|
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_GET_MAXIMUM_REQUEST_SIZE) \
|
|
}); \
|
|
\
|
|
static constexpr size_t MaximumResponseSize = std::max<size_t>({ \
|
|
0 \
|
|
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_GET_MAXIMUM_RESPONSE_SIZE) \
|
|
}); \
|
|
};
|
|
|
|
#define AMS_TIPC_IMPL_DEFINE_CONCEPT_HELPERS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
|
template<typename T, typename... Args> \
|
|
concept Is##CLASSNAME##__##NAME##Impl = requires (T &t, Args &&... args) { \
|
|
{ t.NAME(std::forward<Args>(args)...) } -> std::same_as<RETURN>; \
|
|
}; \
|
|
\
|
|
template<typename T, typename A> \
|
|
struct Is##CLASSNAME##__##NAME##Holder : std::false_type{}; \
|
|
\
|
|
template<typename T, typename... Args> requires std::same_as<std::tuple<Args...>, CLASSNAME::NAME##ArgumentsType> \
|
|
struct Is##CLASSNAME##__##NAME##Holder<T, std::tuple<Args...>> : std::bool_constant<Is##CLASSNAME##__##NAME##Impl<T, Args...>>{}; \
|
|
\
|
|
template<typename T> \
|
|
static constexpr inline bool Is##CLASSNAME##__##NAME = Is##CLASSNAME##__##NAME##Holder<T, CLASSNAME::NAME##ArgumentsType>::value;
|
|
|
|
#define AMS_TIPC_IMPL_CHECK_CONCEPT_HELPER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
|
Is##CLASSNAME##__##NAME<T> &&
|
|
|
|
#define AMS_TIPC_IMPL_DEFINE_CONCEPT(CLASSNAME, CMD_MACRO) \
|
|
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_DEFINE_CONCEPT_HELPERS) \
|
|
\
|
|
template<typename T> \
|
|
concept Is##CLASSNAME = CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_CHECK_CONCEPT_HELPER) true;
|
|
|
|
#define AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
|
else if (constexpr u16 TipcCommandId = CMD_ID + 0x10; tag == TipcCommandId) { \
|
|
R_RETURN((this->ProcessMethodById<TipcCommandId, ImplType>(impl, message_buffer, fw_ver))); \
|
|
}
|
|
|
|
#define AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST_BY_ID(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
|
if constexpr (constexpr u16 TipcCommandId = CMD_ID + 0x10; CommandId == TipcCommandId) { \
|
|
constexpr bool MinValid = VERSION_MIN == hos::Version_Min; \
|
|
constexpr bool MaxValid = VERSION_MAX == hos::Version_Max; \
|
|
if ((MinValid || VERSION_MIN <= fw_ver) && (MaxValid || fw_ver <= VERSION_MAX)) { \
|
|
R_RETURN((::ams::tipc::impl::InvokeServiceCommandImpl<TipcCommandId, &ImplType::NAME, ImplType>(impl, message_buffer))); \
|
|
} \
|
|
}
|
|
|
|
#define AMS_TIPC_IMPL_IS_FIRMWARE_VERSION_ALWAYS_VALID(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
|
{ \
|
|
constexpr bool MinValid = VERSION_MIN == hos::Version_Min; \
|
|
constexpr bool MaxValid = VERSION_MAX == hos::Version_Max; \
|
|
if (!MinValid || !MaxValid) { \
|
|
return false; \
|
|
} \
|
|
}
|
|
|
|
|
|
#define AMS_TIPC_DEFINE_INTERFACE_WITH_DEFAULT_BASE(NAMESPACE, INTERFACE, BASE, CMD_MACRO) \
|
|
namespace NAMESPACE { \
|
|
\
|
|
AMS_TIPC_IMPL_DEFINE_INTERFACE(BASE, INTERFACE, CMD_MACRO) \
|
|
AMS_TIPC_IMPL_DEFINE_CONCEPT(INTERFACE, CMD_MACRO) \
|
|
\
|
|
} \
|
|
\
|
|
namespace ams::tipc::impl { \
|
|
\
|
|
template<typename Base, typename ImplHolder, typename ImplGetter, typename Root> \
|
|
class ImplTemplateBaseT<::NAMESPACE::INTERFACE, Base, ImplHolder, ImplGetter, Root> : public Base, public ImplHolder { \
|
|
public: \
|
|
template<typename... Args> \
|
|
constexpr explicit ImplTemplateBaseT(Args &&...args) : ImplHolder(std::forward<Args>(args)...) { /* ... */ } \
|
|
private: \
|
|
template<typename ImplType> \
|
|
ALWAYS_INLINE Result ProcessDefaultMethod(ImplType *impl, const MessageBuffer &message_buffer) const { \
|
|
/* Handle a default command. */ \
|
|
if constexpr (HasDefaultServiceCommandProcessor<ImplType>) { \
|
|
R_RETURN(impl->ProcessDefaultServiceCommand(message_buffer)); \
|
|
} else { \
|
|
R_THROW(tipc::ResultInvalidMethod()); \
|
|
} \
|
|
} \
|
|
\
|
|
template<u16 CommandId, typename ImplType> \
|
|
ALWAYS_INLINE Result ProcessMethodById(ImplType *impl, const MessageBuffer &message_buffer, hos::Version fw_ver) const { \
|
|
CMD_MACRO(ImplType, AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST_BY_ID) \
|
|
\
|
|
R_RETURN(this->ProcessDefaultMethod<ImplType>(impl, message_buffer)); \
|
|
} \
|
|
\
|
|
static consteval bool IsFirmwareVersionAlwaysValid() { \
|
|
CMD_MACRO(ImplType, AMS_TIPC_IMPL_IS_FIRMWARE_VERSION_ALWAYS_VALID); \
|
|
return true; \
|
|
} \
|
|
public: \
|
|
virtual Result ProcessRequest() override { \
|
|
/* Get the implementation object. */ \
|
|
auto * const impl = ImplGetter::GetImplPointer(static_cast<ImplHolder *>(this)); \
|
|
\
|
|
/* Get the implementation type. */ \
|
|
using ImplType = typename std::remove_reference<decltype(*impl)>::type; \
|
|
static_assert(::NAMESPACE::Is##INTERFACE<ImplType>); \
|
|
\
|
|
/* Get accessor to the message buffer. */ \
|
|
const MessageBuffer message_buffer(tipc::GetMessageBuffer()); \
|
|
\
|
|
/* Get decision variables. */ \
|
|
const auto tag = MessageBuffer::MessageHeader(message_buffer).GetTag(); \
|
|
const auto fw_ver = IsFirmwareVersionAlwaysValid() ? hos::Version_Current : hos::GetVersion(); \
|
|
\
|
|
/* Process against the command ids. */ \
|
|
if (false) { } \
|
|
CMD_MACRO(ImplType, AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST) \
|
|
else { \
|
|
R_RETURN(this->ProcessDefaultMethod<ImplType>(impl, message_buffer)); \
|
|
} \
|
|
} \
|
|
}; \
|
|
\
|
|
}
|
|
|
|
|
|
#define AMS_TIPC_DEFINE_INTERFACE(NAMESPACE, INTERFACE, CMD_MACRO) \
|
|
AMS_TIPC_DEFINE_INTERFACE_WITH_DEFAULT_BASE(NAMESPACE, INTERFACE, ::ams::tipc::ServiceObjectBase, CMD_MACRO)
|
|
|
|
#define AMS_TIPC_METHOD_INFO_7(CLASSNAME, HANDLER, CMD_ID, RETURN, NAME, ARGS, ARGNAMES) \
|
|
HANDLER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, hos::Version_Min, hos::Version_Max)
|
|
|
|
#define AMS_TIPC_METHOD_INFO_8(CLASSNAME, HANDLER, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN) \
|
|
HANDLER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, hos::Version_Max)
|
|
|
|
#define AMS_TIPC_METHOD_INFO_9(CLASSNAME, HANDLER, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
|
HANDLER(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX)
|
|
|
|
#define AMS_TIPC_METHOD_INFO_X(_, _0, _1, _2, _3, _4, _5, _6, _7, _8, FUNC, ...) FUNC
|
|
|
|
#define AMS_TIPC_METHOD_INFO(...) \
|
|
AMS_TIPC_METHOD_INFO_X(, ## __VA_ARGS__, AMS_TIPC_METHOD_INFO_9(__VA_ARGS__), AMS_TIPC_METHOD_INFO_8(__VA_ARGS__), AMS_TIPC_METHOD_INFO_7(__VA_ARGS__))
|
|
|
|
}
|