diff --git a/stratosphere/libstratosphere/Makefile b/stratosphere/libstratosphere/Makefile
index 677d955..79d160a 100644
--- a/stratosphere/libstratosphere/Makefile
+++ b/stratosphere/libstratosphere/Makefile
@@ -16,7 +16,7 @@
# INCLUDES is a list of directories containing header files
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
-SOURCES := source source/ams source/os source/os/impl source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb
+SOURCES := source source/ams source/os source/os/impl source/sf source/sf/cmif source/sf/hipc source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb
DATA := data
INCLUDES := include
@@ -32,7 +32,7 @@
CFLAGS += $(INCLUDE) -D__SWITCH__
-CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -flto -std=gnu++17
+CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
diff --git a/stratosphere/libstratosphere/include/stratosphere.hpp b/stratosphere/libstratosphere/include/stratosphere.hpp
index a5b239d..b1a36f4 100644
--- a/stratosphere/libstratosphere/include/stratosphere.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere.hpp
@@ -37,4 +37,5 @@
#include "stratosphere/ncm.hpp"
#include "stratosphere/pm.hpp"
#include "stratosphere/rnd.hpp"
-#include "stratosphere/sm.hpp"
\ No newline at end of file
+#include "stratosphere/sm.hpp"
+#include "stratosphere/sf.hpp"
diff --git a/stratosphere/libstratosphere/include/stratosphere/ams.hpp b/stratosphere/libstratosphere/include/stratosphere/ams.hpp
index 4a679b3..9af4ad1 100644
--- a/stratosphere/libstratosphere/include/stratosphere/ams.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere/ams.hpp
@@ -17,4 +17,4 @@
#pragma once
#include "ams/ams_types.hpp"
-#include "ams/ams_firmware_version_api.hpp"
\ No newline at end of file
+#include "ams/ams_hos_version_api.hpp"
\ No newline at end of file
diff --git a/stratosphere/libstratosphere/include/stratosphere/ams/ams_firmware_version_api.hpp b/stratosphere/libstratosphere/include/stratosphere/ams/ams_firmware_version_api.hpp
deleted file mode 100644
index d509d77..0000000
--- a/stratosphere/libstratosphere/include/stratosphere/ams/ams_firmware_version_api.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2018-2019 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#pragma once
-#include "ams_types.hpp"
-
-namespace sts::ams {
-
- FirmwareVersion GetRuntimeFirmwareVersion();
- void SetFirmwareVersionForLibnx();
-
-}
diff --git a/stratosphere/libstratosphere/include/stratosphere/ams/ams_hos_version_api.hpp b/stratosphere/libstratosphere/include/stratosphere/ams/ams_hos_version_api.hpp
new file mode 100644
index 0000000..6197e48
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/ams/ams_hos_version_api.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "ams_types.hpp"
+
+namespace sts::hos {
+
+ sts::hos::Version GetVersion();
+ void SetVersionForLibnx();
+
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/ams/ams_types.hpp b/stratosphere/libstratosphere/include/stratosphere/ams/ams_types.hpp
index 9989e62..9c0e243 100644
--- a/stratosphere/libstratosphere/include/stratosphere/ams/ams_types.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere/ams/ams_types.hpp
@@ -21,21 +21,25 @@
/* Define firmware version in global namespace, for convenience. */
namespace sts {
- enum FirmwareVersion : u32 {
- FirmwareVersion_Min = 0,
- FirmwareVersion_100 = FirmwareVersion_Min,
- FirmwareVersion_200 = 1,
- FirmwareVersion_300 = 2,
- FirmwareVersion_400 = 3,
- FirmwareVersion_500 = 4,
- FirmwareVersion_600 = 5,
- FirmwareVersion_700 = 6,
- FirmwareVersion_800 = 7,
- FirmwareVersion_810 = 8,
- FirmwareVersion_900 = 9,
- FirmwareVersion_Current = FirmwareVersion_900,
- FirmwareVersion_Max = 32,
- };
+ namespace hos {
+
+ enum Version : u16 {
+ Version_Min = 0,
+ Version_100 = Version_Min,
+ Version_200 = 1,
+ Version_300 = 2,
+ Version_400 = 3,
+ Version_500 = 4,
+ Version_600 = 5,
+ Version_700 = 6,
+ Version_800 = 7,
+ Version_810 = 8,
+ Version_900 = 9,
+ Version_Current = Version_900,
+ Version_Max = 32,
+ };
+
+ }
}
diff --git a/stratosphere/libstratosphere/include/stratosphere/cfg/cfg_api.hpp b/stratosphere/libstratosphere/include/stratosphere/cfg/cfg_api.hpp
index 9c5c8ef..8c6e8cb 100644
--- a/stratosphere/libstratosphere/include/stratosphere/cfg/cfg_api.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere/cfg/cfg_api.hpp
@@ -22,7 +22,7 @@
/* Privileged Process configuration. */
bool IsInitialProcess();
- void GetInitialProcessRange(u64 *out_min, u64 *out_max);
+ void GetInitialProcessRange(os::ProcessId *out_min, os::ProcessId *out_max);
/* SD card configuration. */
bool IsSdCardInitialized();
diff --git a/stratosphere/libstratosphere/include/stratosphere/os/os_common_types.hpp b/stratosphere/libstratosphere/include/stratosphere/os/os_common_types.hpp
index b6d36df..9c44ccd 100644
--- a/stratosphere/libstratosphere/include/stratosphere/os/os_common_types.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere/os/os_common_types.hpp
@@ -29,4 +29,49 @@
ForNotFull,
};
+ struct ProcessId {
+ u64 value;
+
+ inline constexpr explicit operator u64() const {
+ return this->value;
+ }
+
+ /* Invalid Process ID. */
+ static const ProcessId Invalid;
+ };
+
+ inline constexpr const ProcessId ProcessId::Invalid = {static_cast(-1ull)};
+
+ inline constexpr const ProcessId InvalidProcessId = ProcessId::Invalid;
+
+ NX_INLINE ProcessId GetCurrentProcessId() {
+ u64 current_process_id = 0;
+ R_ASSERT(svcGetProcessId(¤t_process_id, CUR_PROCESS_HANDLE));
+ return os::ProcessId{current_process_id};
+ }
+
+ inline constexpr bool operator==(const ProcessId &lhs, const ProcessId &rhs) {
+ return lhs.value == rhs.value;
+ }
+
+ inline constexpr bool operator!=(const ProcessId &lhs, const ProcessId &rhs) {
+ return lhs.value != rhs.value;
+ }
+
+ inline constexpr bool operator<(const ProcessId &lhs, const ProcessId &rhs) {
+ return lhs.value < rhs.value;
+ }
+
+ inline constexpr bool operator<=(const ProcessId &lhs, const ProcessId &rhs) {
+ return lhs.value <= rhs.value;
+ }
+
+ inline constexpr bool operator>(const ProcessId &lhs, const ProcessId &rhs) {
+ return lhs.value > rhs.value;
+ }
+
+ inline constexpr bool operator>=(const ProcessId &lhs, const ProcessId &rhs) {
+ return lhs.value >= rhs.value;
+ }
+
}
diff --git a/stratosphere/libstratosphere/include/stratosphere/results/hipc_results.hpp b/stratosphere/libstratosphere/include/stratosphere/results/hipc_results.hpp
index 86ef678..5406123 100644
--- a/stratosphere/libstratosphere/include/stratosphere/results/hipc_results.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere/results/hipc_results.hpp
@@ -19,9 +19,19 @@
static constexpr u32 Module_Hipc = 11;
-static constexpr Result ResultHipcOutOfDomains = MAKERESULT(Module_Hipc, 200);
+static constexpr Result ResultHipcSessionAllocationFailure = MAKERESULT(Module_Hipc, 102);
-static constexpr Result ResultHipcSessionClosed = MAKERESULT(Module_Hipc, 301);
+static constexpr Result ResultHipcOutOfSessions = MAKERESULT(Module_Hipc, 131);
+static constexpr Result ResultHipcPointerBufferTooSmall = MAKERESULT(Module_Hipc, 141);
+
+static constexpr Result ResultHipcOutOfDomains = MAKERESULT(Module_Hipc, 200);
+
+static constexpr Result ResultHipcSessionClosed = MAKERESULT(Module_Hipc, 301);
+
+static constexpr Result ResultHipcInvalidRequestSize = MAKERESULT(Module_Hipc, 402);
+static constexpr Result ResultHipcUnknownCommandType = MAKERESULT(Module_Hipc, 403);
+
+static constexpr Result ResultHipcInvalidRequest = MAKERESULT(Module_Hipc, 420);
static constexpr Result ResultHipcTargetNotDomain = MAKERESULT(Module_Hipc, 491);
static constexpr Result ResultHipcDomainObjectNotFound = MAKERESULT(Module_Hipc, 492);
diff --git a/stratosphere/libstratosphere/include/stratosphere/results/kernel_results.hpp b/stratosphere/libstratosphere/include/stratosphere/results/kernel_results.hpp
index c536630..1482f5d 100644
--- a/stratosphere/libstratosphere/include/stratosphere/results/kernel_results.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere/results/kernel_results.hpp
@@ -57,6 +57,7 @@
static constexpr Result ResultKernelConnectionRefused = MAKERESULT(Module_Kernel, KernelError_ConnectionRefused);
static constexpr Result ResultKernelLimitReached = MAKERESULT(Module_Kernel, 132 /* KernelError_OutOfResource */);
+static constexpr Result ResultKernelReceiveListBroken = MAKERESULT(Module_Kernel, 258);
static constexpr Result ResultKernelIpcMapFailed = MAKERESULT(Module_Kernel, KernelError_IpcMapFailed);
static constexpr Result ResultKernelIpcCmdBufTooSmall = MAKERESULT(Module_Kernel, KernelError_IpcCmdbufTooSmall);
diff --git a/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp b/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp
index aed8d5f..a68a295 100644
--- a/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp
@@ -19,9 +19,17 @@
static constexpr u32 Module_ServiceFramework = 10;
-static constexpr Result ResultServiceFrameworkTargetNotFound = MAKERESULT(Module_ServiceFramework, 261);
+static constexpr Result ResultServiceFrameworkNotSupported = MAKERESULT(Module_ServiceFramework, 1);
+static constexpr Result ResultServiceFrameworkPreconditionViolation = MAKERESULT(Module_ServiceFramework, 3);
-static constexpr Result ResultServiceFrameworkOutOfDomainEntries = MAKERESULT(Module_ServiceFramework, 301);
+static constexpr Result ResultServiceFrameworkInvalidCmifHeaderSize = MAKERESULT(Module_ServiceFramework, 202);
+static constexpr Result ResultServiceFrameworkInvalidCmifInHeader = MAKERESULT(Module_ServiceFramework, 211);
+static constexpr Result ResultServiceFrameworkUnknownCmifCommandId = MAKERESULT(Module_ServiceFramework, 221);
+static constexpr Result ResultServiceFrameworkInvalidCmifOutRawSize = MAKERESULT(Module_ServiceFramework, 232);
+
+static constexpr Result ResultServiceFrameworkTargetNotFound = MAKERESULT(Module_ServiceFramework, 261);
+
+static constexpr Result ResultServiceFrameworkOutOfDomainEntries = MAKERESULT(Module_ServiceFramework, 301);
static constexpr Result ResultServiceFrameworkRequestDeferred = MAKERESULT(Module_ServiceFramework, 811);
diff --git a/stratosphere/libstratosphere/include/stratosphere/ro/ro_types.hpp b/stratosphere/libstratosphere/include/stratosphere/ro/ro_types.hpp
index 7c09215..19c3d2b 100644
--- a/stratosphere/libstratosphere/include/stratosphere/ro/ro_types.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere/ro/ro_types.hpp
@@ -18,6 +18,7 @@
#include
#include
#include "../defines.hpp"
+#include "../ncm.hpp"
namespace sts::ro {
@@ -44,7 +45,7 @@
u8 modulus[0x100];
u8 fixed_key_signature[0x100];
u8 nrr_signature[0x100];
- u64 title_id;
+ ncm::TitleId title_id;
u32 size;
u8 type; /* 7.0.0+ */
u8 reserved_33D[3];
@@ -57,7 +58,7 @@
}
bool IsTitleIdValid() const {
- return (this->title_id & this->title_id_mask) == this->title_id_pattern;
+ return (static_cast(this->title_id) & this->title_id_mask) == this->title_id_pattern;
}
ModuleType GetType() const {
@@ -66,7 +67,7 @@
return type;
}
- u64 GetTitleId() const {
+ ncm::TitleId GetTitleId() const {
return this->title_id;
}
diff --git a/stratosphere/libstratosphere/include/stratosphere/sf.hpp b/stratosphere/libstratosphere/include/stratosphere/sf.hpp
new file mode 100644
index 0000000..8b39292
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/sf.hpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+
+#include "sf/sf_common.hpp"
+#include "sf/sf_service_object.hpp"
+#include "sf/hipc/sf_hipc_server_session_manager.hpp"
+
+#include "sf/sf_out.hpp"
+#include "sf/sf_buffers.hpp"
+#include "sf/impl/sf_impl_command_serialization.hpp"
+
+#include "sf/hipc/sf_hipc_server_manager.hpp"
diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp
new file mode 100644
index 0000000..f09993d
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "../sf_common.hpp"
+#include "sf_cmif_service_object_holder.hpp"
+
+namespace sts::sf::cmif {
+
+ struct DomainObjectId {
+ u32 value;
+
+ constexpr void SetValue(u32 new_value) { this->value = new_value; }
+ };
+
+ static_assert(std::is_trivial::value, "DomainObjectId");
+
+ inline constexpr bool operator==(const DomainObjectId &lhs, const DomainObjectId &rhs) {
+ return lhs.value == rhs.value;
+ }
+
+ inline constexpr bool operator!=(const DomainObjectId &lhs, const DomainObjectId &rhs) {
+ return lhs.value != rhs.value;
+ }
+
+ inline constexpr bool operator<(const DomainObjectId &lhs, const DomainObjectId &rhs) {
+ return lhs.value < rhs.value;
+ }
+
+ inline constexpr bool operator<=(const DomainObjectId &lhs, const DomainObjectId &rhs) {
+ return lhs.value <= rhs.value;
+ }
+
+ inline constexpr bool operator>(const DomainObjectId &lhs, const DomainObjectId &rhs) {
+ return lhs.value > rhs.value;
+ }
+
+ inline constexpr bool operator>=(const DomainObjectId &lhs, const DomainObjectId &rhs) {
+ return lhs.value >= rhs.value;
+ }
+
+ constexpr inline const DomainObjectId InvalidDomainObjectId = { .value = 0 };
+
+ class ServerDomainBase {
+ public:
+ virtual Result ReserveIds(DomainObjectId *out_ids, size_t count) = 0;
+ virtual Result AlterReservedIds(const DomainObjectId *old_reserved_ids, const DomainObjectId *new_reserved_ids, size_t count) = 0;
+ virtual void UnreserveIds(const DomainObjectId *ids, size_t count) = 0;
+ virtual void RegisterObject(DomainObjectId id, ServiceObjectHolder &&obj) = 0;
+
+ virtual ServiceObjectHolder UnregisterObject(DomainObjectId id) = 0;
+ virtual ServiceObjectHolder GetObject(DomainObjectId id) = 0;
+ };
+
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_pointer_and_size.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_pointer_and_size.hpp
new file mode 100644
index 0000000..13f1858
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_pointer_and_size.hpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "../sf_common.hpp"
+
+namespace sts::sf::cmif {
+
+ class PointerAndSize {
+ private:
+ uintptr_t pointer;
+ size_t size;
+ public:
+ constexpr PointerAndSize() : pointer(0), size(0) { /* ... */ }
+ constexpr PointerAndSize(uintptr_t ptr, size_t sz) : pointer(ptr), size(sz) { /* ... */ }
+ constexpr PointerAndSize(void *ptr, size_t sz) : PointerAndSize(reinterpret_cast(ptr), sz) { /* ... */ }
+
+ constexpr void *GetPointer() const {
+ return reinterpret_cast(this->pointer);
+ }
+
+ constexpr uintptr_t GetAddress() const {
+ return this->pointer;
+ }
+
+ constexpr size_t GetSize() const {
+ return this->size;
+ }
+ };
+
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp
new file mode 100644
index 0000000..4bc1fdb
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "../sf_service_object.hpp"
+#include "sf_cmif_pointer_and_size.hpp"
+
+namespace sts::sf::cmif {
+
+ /* Forward declare ServiceDispatchContext, ServiceObjectHolder. */
+ struct ServiceDispatchContext;
+ class ServiceObjectHolder;
+ struct DomainObjectId;
+
+ class ServerMessageProcessor {
+ public:
+ /* Used to enabled templated message processors. */
+ virtual void SetImplementationProcessor(ServerMessageProcessor *impl) { /* ... */ }
+
+ virtual Result PrepareForProcess(const ServiceDispatchContext &ctx, const size_t headers_size) const = 0;
+ virtual Result GetInObjects(ServiceObjectHolder *in_objects) const = 0;
+ virtual HipcRequest PrepareForReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const size_t headers_size, size_t &num_out_object_handles) = 0;
+ virtual void PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const size_t headers_size) = 0;
+ virtual void SetOutObjects(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, ServiceObjectHolder *out_objects, DomainObjectId *ids) = 0;
+ };
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp
new file mode 100644
index 0000000..fdc493e
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "../sf_service_object.hpp"
+#include "sf_cmif_pointer_and_size.hpp"
+#include "sf_cmif_server_message_processor.hpp"
+
+namespace sts::sf::hipc {
+
+ class ServerSessionManager;
+
+}
+
+namespace sts::sf::cmif {
+
+ class ServerMessageProcessor;
+
+ struct HandlesToClose {
+ Handle handles[8];
+ size_t num_handles;
+ };
+
+ struct ServiceDispatchContext {
+ sf::IServiceObject *srv_obj;
+ hipc::ServerSessionManager *manager;
+ ServerMessageProcessor *processor;
+ HandlesToClose *handles_to_close;
+ const PointerAndSize pointer_buffer;
+ const PointerAndSize in_message_buffer;
+ const PointerAndSize out_message_buffer;
+ const HipcParsedRequest request;
+ };
+
+ struct ServiceCommandMeta {
+ hos::Version hosver_low;
+ hos::Version hosver_high;
+ u32 cmd_id;
+ Result (*handler)(CmifOutHeader **out_header_ptr, ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data);
+
+ constexpr inline bool Matches(u32 cmd_id, hos::Version hosver) const {
+ return this->cmd_id == cmd_id && this->hosver_low <= hosver && hosver <= this->hosver_high;
+ }
+
+ constexpr inline decltype(handler) GetHandler() const {
+ return this->handler;
+ }
+ };
+ static_assert(std::is_pod::value && sizeof(ServiceCommandMeta) == 0x10, "sizeof(ServiceCommandMeta)");
+
+ namespace impl {
+
+ class ServiceDispatchTableBase {
+ protected:
+ Result ProcessMessageImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count) const;
+ Result ProcessMessageForMitmImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count) const;
+ public:
+ /* CRTP. */
+ template
+ Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
+ static_assert(std::is_base_of::value, "ServiceDispatchTableBase::Process");
+ return static_cast(this)->ProcessMessage(ctx, in_raw_data);
+ }
+
+ template
+ Result ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
+ static_assert(std::is_base_of::value, "ServiceDispatchTableBase::ProcessForMitm");
+ return static_cast(this)->ProcessMessageForMitm(ctx, in_raw_data);
+ }
+ };
+
+ template>
+ class ServiceDispatchTableImpl;
+
+ template
+ class ServiceDispatchTableImpl> : public ServiceDispatchTableBase {
+ private:
+ template
+ using EntryType = ServiceCommandMeta;
+ private:
+ const std::array entries;
+ public:
+ explicit constexpr ServiceDispatchTableImpl(EntryType... args) : entries { args... } { /* ... */ }
+
+ Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
+ return this->ProcessMessageImpl(ctx, in_raw_data, this->entries.data(), this->entries.size());
+ }
+
+ Result ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
+ return this->ProcessMessageForMitmImpl(ctx, in_raw_data, this->entries.data(), this->entries.size());
+ }
+ };
+
+ }
+
+ template
+ class ServiceDispatchTable : public impl::ServiceDispatchTableImpl {
+ public:
+ explicit constexpr ServiceDispatchTable(Entries... entries) : impl::ServiceDispatchTableImpl(entries...) { /* ... */ }
+ };
+
+ #define DEFINE_SERVICE_DISPATCH_TABLE \
+ template \
+ static constexpr inline sf::cmif::ServiceDispatchTable s_CmifServiceDispatchTable
+
+ struct ServiceDispatchMeta {
+ const impl::ServiceDispatchTableBase *DispatchTable;
+ Result (impl::ServiceDispatchTableBase::*ProcessHandler)(ServiceDispatchContext &, const cmif::PointerAndSize &) const;
+
+ constexpr uintptr_t GetServiceId() const {
+ return reinterpret_cast(this->DispatchTable);
+ }
+ };
+
+ template
+ struct ServiceDispatchTraits {
+ static_assert(std::is_base_of::value, "ServiceObjects must derive from sf::IServiceObject");
+
+ using ProcessHandlerType = decltype(ServiceDispatchMeta::ProcessHandler);
+
+ static constexpr inline auto DispatchTable = T::template s_CmifServiceDispatchTable;
+ using DispatchTableType = decltype(DispatchTable);
+
+ static constexpr ProcessHandlerType ProcessHandlerImpl = ServiceObjectTraits::IsMitmServiceObject ? (&impl::ServiceDispatchTableBase::ProcessMessageForMitm)
+ : (&impl::ServiceDispatchTableBase::ProcessMessage);
+
+ static constexpr inline ServiceDispatchMeta Meta{&DispatchTable, ProcessHandlerImpl};
+ };
+
+ template
+ NX_CONSTEXPR const ServiceDispatchMeta *GetServiceDispatchMeta() {
+ return &ServiceDispatchTraits::Meta;
+ }
+
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp
new file mode 100644
index 0000000..e585f6c
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "../sf_service_object.hpp"
+#include "sf_cmif_service_dispatch.hpp"
+
+namespace sts::sf::cmif {
+
+ class ServiceObjectHolder {
+ private:
+ std::shared_ptr srv;
+ const ServiceDispatchMeta *dispatch_meta;
+ private:
+ /* Copy constructor. */
+ ServiceObjectHolder(const ServiceObjectHolder &o) : srv(o.srv), dispatch_meta(o.dispatch_meta) { /* ... */ }
+ ServiceObjectHolder &operator=(const ServiceObjectHolder &o) = delete;
+ public:
+ /* Default constructor, null all members. */
+ ServiceObjectHolder() : srv(nullptr), dispatch_meta(nullptr) { /* ... */ }
+
+ /* Ensure correct type id at runtime through template constructor. */
+ template
+ constexpr explicit ServiceObjectHolder(std::shared_ptr &&s) {
+ this->srv = std::move(s);
+ this->dispatch_meta = GetServiceDispatchMeta();
+ }
+
+ /* Move constructor, assignment operator. */
+ ServiceObjectHolder(ServiceObjectHolder &&o) : srv(std::move(o.srv)), dispatch_meta(std::move(o.dispatch_meta)) { /* ... */ }
+ ServiceObjectHolder &operator=(ServiceObjectHolder &&o) {
+ ServiceObjectHolder tmp(std::move(o));
+ tmp.Swap(*this);
+ return *this;
+ }
+
+ /* State management. */
+ void Swap(ServiceObjectHolder &o) {
+ std::swap(this->srv, o.srv);
+ std::swap(this->dispatch_meta, o.dispatch_meta);
+ }
+
+ void Reset() {
+ this->srv = nullptr;
+ this->dispatch_meta = nullptr;
+ }
+
+ ServiceObjectHolder Clone() const {
+ return ServiceObjectHolder(*this);
+ }
+
+ /* Boolean operators. */
+ explicit constexpr operator bool() const {
+ return this->dispatch_meta != nullptr;
+ }
+
+ constexpr bool operator!() const {
+ return this->dispatch_meta == nullptr;
+ }
+
+ /* Getters. */
+ constexpr uintptr_t GetServiceId() const {
+ if (this->dispatch_meta) {
+ return this->dispatch_meta->GetServiceId();
+ }
+ return 0;
+ }
+
+ template
+ ServiceImpl *GetServiceObject() const {
+ if (this->GetServiceId() == GetServiceDispatchMeta()->GetServiceId()) {
+ return static_cast(this->srv.get());
+ }
+ return nullptr;
+ }
+
+ sf::IServiceObject *GetServiceObjectUnsafe() const {
+ return this->srv.get();
+ }
+
+ /* Processing. */
+ Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const;
+ };
+
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp
new file mode 100644
index 0000000..0478ca4
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "../sf_common.hpp"
+#include "../cmif/sf_cmif_pointer_and_size.hpp"
+
+namespace sts::sf::hipc {
+
+ constexpr size_t TlsMessageBufferSize = 0x100;
+
+ enum class ReceiveResult {
+ Success,
+ Closed,
+ NeedsRetry,
+ };
+
+ Result Receive(ReceiveResult *out_recv_result, Handle session_handle, const cmif::PointerAndSize &message_buffer);
+ Result Receive(bool *out_closed, Handle session_handle, const cmif::PointerAndSize &message_buffer);
+ Result Reply(Handle session_handle, const cmif::PointerAndSize &message_buffer);
+
+ Result CreateSession(Handle *out_server_handle, Handle *out_client_handle);
+
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp
new file mode 100644
index 0000000..62f1705
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "sf_hipc_server_session_manager.hpp"
+
+namespace sts::sf::hipc {
+
+ struct DefaultServerManagerOptions {
+ static constexpr size_t PointerBufferSize = 0;
+ static constexpr size_t MaxDomains = 0;
+ static constexpr size_t MaxDomainObjects = 0;
+ };
+
+ static constexpr size_t ServerSessionCountMax = 0x40;
+ static_assert(ServerSessionCountMax == 0x40, "ServerSessionCountMax isn't 0x40 somehow, this assert is a reminder that this will break lots of things");
+
+ template
+ class ServerManager;
+
+ class ServerManagerBase : public ServerSessionManager {
+ NON_COPYABLE(ServerManagerBase);
+ NON_MOVEABLE(ServerManagerBase);
+ private:
+ enum class UserDataTag : uintptr_t {
+ Server = 1,
+ Session = 2,
+ MitmServer = 3,
+ };
+ private:
+ class ServerBase : public os::WaitableHolder {
+ friend class ServerManagerBase;
+ template
+ friend class ServerManager;
+ NON_COPYABLE(ServerBase);
+ NON_MOVEABLE(ServerBase);
+ protected:
+ cmif::ServiceObjectHolder static_object;
+ ::Handle port_handle;
+ sm::ServiceName service_name;
+ bool service_managed;
+ public:
+ ServerBase(Handle ph, sm::ServiceName sn, bool m, cmif::ServiceObjectHolder &&sh) :
+ os::WaitableHolder(ph), static_object(std::move(sh)), port_handle(ph), service_name(sn), service_managed(m)
+ {
+ /* ... */
+ }
+
+ virtual ~ServerBase() = 0;
+ virtual void CreateSessionObjectHolder(cmif::ServiceObjectHolder *out_obj, std::shared_ptr<::Service> *out_fsrv) const = 0;
+ };
+
+ template>
+ class Server : public ServerBase {
+ NON_COPYABLE(Server);
+ NON_MOVEABLE(Server);
+ private:
+ static constexpr bool IsMitmServer = ServiceObjectTraits::IsMitmServiceObject;
+ public:
+ Server(Handle ph, sm::ServiceName sn, bool m, cmif::ServiceObjectHolder &&sh) : ServerBase(ph, sn, m, std::forward(sh)) {
+ /* ... */
+ }
+
+ virtual ~Server() override {
+ if (this->service_managed) {
+ if constexpr (IsMitmServer) {
+ R_ASSERT(sm::mitm::UninstallMitm(this->service_name));
+ } else {
+ R_ASSERT(sm::UnregisterService(this->service_name));
+ }
+ R_ASSERT(svcCloseHandle(this->port_handle));
+ }
+ }
+
+ virtual void CreateSessionObjectHolder(cmif::ServiceObjectHolder *out_obj, std::shared_ptr<::Service> *out_fsrv) const override final {
+ /* If we're serving a static object, use it. */
+ if (this->static_object) {
+ *out_obj = std::move(this->static_object.Clone());
+ *out_fsrv = nullptr;
+ return;
+ }
+
+ /* Otherwise, we're either a mitm session or a non-mitm session. */
+ if constexpr (IsMitmServer) {
+ /* Custom deleter ensures that nothing goes awry. */
+ /* TODO: Should this just be a custom wrapper object? */
+ std::shared_ptr<::Service> forward_service(new ::Service(), [](::Service *srv) {
+ serviceClose(srv);
+ delete srv;
+ });
+
+ /* Get mitm forward session. */
+ os::ProcessId client_pid;
+ ncm::TitleId client_tid;
+ R_ASSERT(sm::mitm::AcknowledgeSession(forward_service.get(), &client_pid, &client_tid, this->service_name));
+
+ *out_obj = std::move(cmif::ServiceObjectHolder(std::move(MakeShared(forward_service))));
+ *out_fsrv = std::move(forward_service);
+ } else {
+ *out_obj = std::move(cmif::ServiceObjectHolder(std::move(MakeShared())));
+ *out_fsrv = nullptr;
+ }
+ }
+ };
+ private:
+ /* Management of waitables. */
+ os::WaitableManager waitable_manager;
+ os::Event request_stop_event;
+ os::WaitableHolder request_stop_event_holder;
+ os::Event notify_event;
+ os::WaitableHolder notify_event_holder;
+
+ os::Mutex waitable_selection_mutex;
+
+ os::Mutex waitlist_mutex;
+ os::WaitableManager waitlist;
+ private:
+ virtual void RegisterSessionToWaitList(ServerSession *session) override final;
+ void RegisterToWaitList(os::WaitableHolder *holder);
+ void ProcessWaitList();
+
+ bool WaitAndProcessImpl();
+
+ Result ProcessForServer(os::WaitableHolder *holder);
+ Result ProcessForMitmServer(os::WaitableHolder *holder);
+ Result ProcessForSession(os::WaitableHolder *holder);
+
+ template>
+ void RegisterServerImpl(Handle port_handle, sm::ServiceName service_name, bool managed, cmif::ServiceObjectHolder &&static_holder) {
+ /* Allocate server memory. */
+ auto *server = this->AllocateServer();
+ STS_ASSERT(server != nullptr);
+ new (server) Server(port_handle, service_name, managed, std::forward(static_holder));
+
+ if constexpr (!ServiceObjectTraits::IsMitmServiceObject) {
+ /* Non-mitm server. */
+ server->SetUserData(static_cast(UserDataTag::Server));
+ } else {
+ /* Mitm server. */
+ server->SetUserData(static_cast(UserDataTag::MitmServer));
+ }
+
+ this->waitable_manager.LinkWaitableHolder(server);
+ }
+ protected:
+ virtual ServerBase *AllocateServer() = 0;
+ virtual void DestroyServer(ServerBase *server) = 0;
+ public:
+ ServerManagerBase() : ServerSessionManager(),
+ request_stop_event(false), request_stop_event_holder(&request_stop_event),
+ notify_event(false), notify_event_holder(¬ify_event)
+ {
+ /* Link waitables. */
+ this->waitable_manager.LinkWaitableHolder(&this->request_stop_event_holder);
+ this->waitable_manager.LinkWaitableHolder(&this->notify_event_holder);
+ }
+
+ template>
+ void RegisterServer(Handle port_handle, std::shared_ptr static_object = nullptr) {
+ static_assert(!ServiceObjectTraits::IsMitmServiceObject, "RegisterServer requires non-mitm object. Use RegisterMitmServer instead.");
+ /* Register server. */
+ cmif::ServiceObjectHolder static_holder;
+ if (static_object != nullptr) {
+ static_holder = cmif::ServiceObjectHolder(std::move(static_object));
+ }
+ this->RegisterServerImpl(port_handle, sm::InvalidServiceName, false, std::move(static_holder));
+ }
+
+ template>
+ Result RegisterServer(sm::ServiceName service_name, size_t max_sessions, std::shared_ptr static_object = nullptr) {
+ static_assert(!ServiceObjectTraits::IsMitmServiceObject, "RegisterServer requires non-mitm object. Use RegisterMitmServer instead.");
+
+ /* Register service. */
+ Handle port_handle;
+ R_TRY(sm::RegisterService(&port_handle, service_name, max_sessions, false));
+
+ /* Register server. */
+ cmif::ServiceObjectHolder static_holder;
+ if (static_object != nullptr) {
+ static_holder = cmif::ServiceObjectHolder(std::move(static_object));
+ }
+ this->RegisterServerImpl(port_handle, service_name, true, std::move(static_holder));
+ return ResultSuccess;
+ }
+
+ /* Processing. */
+ os::WaitableHolder *WaitSignaled();
+
+ void ResumeProcessing();
+ void RequestStopProcessing();
+ void AddUserWaitableHolder(os::WaitableHolder *waitable);
+
+ Result Process(os::WaitableHolder *waitable);
+ void WaitAndProcess();
+ void LoopProcess();
+ };
+
+ template
+ class ServerManager : public ServerManagerBase {
+ NON_COPYABLE(ServerManager);
+ NON_MOVEABLE(ServerManager);
+ static_assert(MaxServers <= ServerSessionCountMax, "MaxServers can never be larger than ServerSessionCountMax (0x40).");
+ static_assert(MaxSessions <= ServerSessionCountMax, "MaxSessions can never be larger than ServerSessionCountMax (0x40).");
+ static_assert(MaxServers + MaxSessions <= ServerSessionCountMax, "MaxServers + MaxSessions can never be larger than ServerSessionCountMax (0x40).");
+ private:
+ /* Resource storage. */
+ os::Mutex resource_mutex;
+ TYPED_STORAGE(ServerBase) server_storages[MaxServers];
+ bool server_allocated[MaxServers];
+ TYPED_STORAGE(ServerSession) session_storages[MaxSessions];
+ bool session_allocated[MaxSessions];
+ u8 pointer_buffer_storage[0x10 + (MaxSessions * ManagerOptions::PointerBufferSize)];
+ u8 saved_message_storage[0x10 + (MaxSessions * hipc::TlsMessageBufferSize)];
+ uintptr_t pointer_buffers_start;
+ uintptr_t saved_messages_start;
+
+ /* TODO: Domain resources. */
+ private:
+ constexpr inline size_t GetServerIndex(const ServerBase *server) const {
+ const size_t i = server - GetPointer(this->server_storages[0]);
+ STS_ASSERT(i < MaxServers);
+ return i;
+ }
+
+ constexpr inline size_t GetSessionIndex(const ServerSession *session) const {
+ const size_t i = session - GetPointer(this->session_storages[0]);
+ STS_ASSERT(i < MaxSessions);
+ return i;
+ }
+
+ constexpr inline cmif::PointerAndSize GetObjectBySessionIndex(const ServerSession *session, uintptr_t start, size_t size) const {
+ return cmif::PointerAndSize(start + this->GetSessionIndex(session) * size, size);
+ }
+ protected:
+ virtual ServerSession *AllocateSession() override final {
+ std::scoped_lock lk(this->resource_mutex);
+ for (size_t i = 0; i < MaxSessions; i++) {
+ if (!this->session_allocated[i]) {
+ this->session_allocated[i] = true;
+ return GetPointer(this->session_storages[i]);
+ }
+ }
+ return nullptr;
+ }
+
+ virtual void FreeSession(ServerSession *session) override final {
+ std::scoped_lock lk(this->resource_mutex);
+ const size_t index = this->GetSessionIndex(session);
+ STS_ASSERT(this->session_allocated[index]);
+ this->session_allocated[index] = false;
+ }
+
+ virtual ServerBase *AllocateServer() override final {
+ std::scoped_lock lk(this->resource_mutex);
+ for (size_t i = 0; i < MaxServers; i++) {
+ if (!this->server_allocated[i]) {
+ this->server_allocated[i] = true;
+ return GetPointer(this->server_storages[i]);
+ }
+ }
+ return nullptr;
+ }
+
+ virtual void DestroyServer(ServerBase *server) override final {
+ std::scoped_lock lk(this->resource_mutex);
+ const size_t index = this->GetServerIndex(server);
+ STS_ASSERT(this->server_allocated[index]);
+ server->~ServerBase();
+ this->server_allocated[index] = false;
+ }
+
+ virtual cmif::PointerAndSize GetSessionPointerBuffer(const ServerSession *session) const override final {
+ if constexpr (ManagerOptions::PointerBufferSize > 0) {
+ return this->GetObjectBySessionIndex(session, this->pointer_buffers_start, ManagerOptions::PointerBufferSize);
+ } else {
+ return cmif::PointerAndSize();
+ }
+ }
+
+ virtual cmif::PointerAndSize GetSessionSavedMessageBuffer(const ServerSession *session) const override final {
+ return this->GetObjectBySessionIndex(session, this->saved_messages_start, hipc::TlsMessageBufferSize);
+ }
+ public:
+ ServerManager() : ServerManagerBase() {
+ /* Clear storages. */
+ std::memset(this->server_storages, 0, sizeof(this->server_storages));
+ std::memset(this->server_allocated, 0, sizeof(this->server_allocated));
+ std::memset(this->session_storages, 0, sizeof(this->session_storages));
+ std::memset(this->session_allocated, 0, sizeof(this->session_allocated));
+ std::memset(this->pointer_buffer_storage, 0, sizeof(this->pointer_buffer_storage));
+ std::memset(this->saved_message_storage, 0, sizeof(this->saved_message_storage));
+
+ /* Set resource starts. */
+ this->pointer_buffers_start = util::AlignUp(reinterpret_cast(this->pointer_buffer_storage), 0x10);
+ this->saved_messages_start = util::AlignUp(reinterpret_cast(this->saved_message_storage), 0x10);
+ }
+
+ ~ServerManager() {
+ /* Close all sessions. */
+ for (size_t i = 0; i < MaxSessions; i++) {
+ if (this->session_allocated[i]) {
+ this->CloseSessionImpl(GetPointer(this->session_storages[i]));
+ }
+ }
+
+ /* Close all servers. */
+ for (size_t i = 0; i < MaxServers; i++) {
+ if (this->server_allocated[i]) {
+ this->DestroyServer(GetPointer(this->server_storages[i]));
+ }
+ }
+ }
+ };
+
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp
new file mode 100644
index 0000000..9e267f5
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "../sf_common.hpp"
+#include "../sf_service_object.hpp"
+#include "../cmif/sf_cmif_pointer_and_size.hpp"
+#include "../cmif/sf_cmif_service_object_holder.hpp"
+#include "sf_hipc_api.hpp"
+
+namespace sts::sf::hipc {
+
+ class ServerSessionManager;
+ class ServerManagerBase;
+
+ class ServerSession : public os::WaitableHolder {
+ friend class ServerSessionManager;
+ friend class ServerManagerBase;
+ NON_COPYABLE(ServerSession);
+ NON_MOVEABLE(ServerSession);
+ private:
+ cmif::ServiceObjectHolder srv_obj_holder;
+ cmif::PointerAndSize pointer_buffer;
+ cmif::PointerAndSize saved_message;
+ std::shared_ptr<::Service> forward_service;
+ Handle session_handle;
+ bool is_closed;
+ bool has_received;
+ public:
+ ServerSession(Handle h, cmif::ServiceObjectHolder &&obj) : WaitableHolder(h), srv_obj_holder(std::move(obj)), session_handle(h) {
+ this->is_closed = false;
+ this->has_received = false;
+ this->forward_service = nullptr;
+ STS_ASSERT(!this->IsMitmSession());
+ }
+
+ ServerSession(Handle h, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) : WaitableHolder(h), srv_obj_holder(std::move(obj)), session_handle(h) {
+ this->is_closed = false;
+ this->has_received = false;
+ this->forward_service = std::move(fsrv);
+ STS_ASSERT(this->IsMitmSession());
+ }
+
+ bool IsMitmSession() const {
+ return this->forward_service != nullptr;
+ }
+ };
+
+ class ServerSessionManager {
+ private:
+ template
+ Result CreateSessionImpl(ServerSession **out, const Constructor &ctor) {
+ /* Allocate session. */
+ ServerSession *session_memory = this->AllocateSession();
+ R_UNLESS(session_memory != nullptr, ResultHipcSessionAllocationFailure);
+ /* Register session. */
+ bool succeeded = false;
+ ON_SCOPE_EXIT {
+ if (!succeeded) {
+ this->DestroySession(session_memory);
+ }
+ };
+ R_TRY(ctor(session_memory));
+ /* Save new session to output. */
+ succeeded = true;
+ *out = session_memory;
+ return ResultSuccess;
+ }
+ void DestroySession(ServerSession *session);
+
+ Result ProcessRequestImpl(ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message);
+ Result DispatchRequest(cmif::ServiceObjectHolder &&obj, ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message);
+ virtual Result DispatchManagerRequest(ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message);
+ virtual void RegisterSessionToWaitList(ServerSession *session) = 0;
+ protected:
+ virtual ServerSession *AllocateSession() = 0;
+ virtual void FreeSession(ServerSession *session) = 0;
+ virtual cmif::PointerAndSize GetSessionPointerBuffer(const ServerSession *session) const = 0;
+ virtual cmif::PointerAndSize GetSessionSavedMessageBuffer(const ServerSession *session) const = 0;
+
+ Result ReceiveRequestImpl(ServerSession *session, const cmif::PointerAndSize &message);
+ void CloseSessionImpl(ServerSession *session);
+ Result RegisterSessionImpl(ServerSession *session_memory, Handle session_handle, cmif::ServiceObjectHolder &&obj);
+ Result AcceptSessionImpl(ServerSession *session_memory, Handle port_handle, cmif::ServiceObjectHolder &&obj);
+ Result RegisterMitmSessionImpl(ServerSession *session_memory, Handle mitm_session_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv);
+ Result AcceptMitmSessionImpl(ServerSession *session_memory, Handle mitm_port_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv);
+
+ Result ReceiveRequest(ServerSession *session, const cmif::PointerAndSize &message) {
+ return this->ReceiveRequestImpl(session, message);
+ }
+
+ Result RegisterSession(ServerSession **out, Handle session_handle, cmif::ServiceObjectHolder &&obj) {
+ auto ctor = [&](ServerSession *session_memory) -> Result {
+ return this->RegisterSessionImpl(session_memory, session_handle, std::forward(obj));
+ };
+ return this->CreateSessionImpl(out, ctor);
+ }
+
+ Result AcceptSession(ServerSession **out, Handle port_handle, cmif::ServiceObjectHolder &&obj) {
+ auto ctor = [&](ServerSession *session_memory) -> Result {
+ return this->AcceptSessionImpl(session_memory, port_handle, std::forward(obj));
+ };
+ return this->CreateSessionImpl(out, ctor);
+ }
+
+ Result RegisterMitmSession(ServerSession **out, Handle mitm_session_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) {
+ auto ctor = [&](ServerSession *session_memory) -> Result {
+ return this->RegisterMitmSessionImpl(session_memory, mitm_session_handle, std::forward(obj), std::forward>(fsrv));
+ };
+ return this->CreateSessionImpl(out, ctor);
+ }
+
+ Result AcceptMitmSession(ServerSession **out, Handle mitm_port_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) {
+ auto ctor = [&](ServerSession *session_memory) -> Result {
+ return this->AcceptMitmSessionImpl(session_memory, mitm_port_handle, std::forward(obj), std::forward>(fsrv));
+ };
+ return this->CreateSessionImpl(out, ctor);
+ }
+
+ virtual ServerSessionManager *GetSessionManagerByTag(u32 tag) {
+ /* This is unused. */
+ return this;
+ }
+ public:
+ Result RegisterSession(Handle session_handle, cmif::ServiceObjectHolder &&obj);
+ Result AcceptSession(Handle port_handle, cmif::ServiceObjectHolder &&obj);
+ Result RegisterMitmSession(Handle session_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv);
+ Result AcceptMitmSession(Handle mitm_port_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv);
+
+ template
+ Result AcceptSession(Handle port_handle, std::shared_ptr obj) {
+ return this->AcceptSession(port_handle, cmif::ServiceObjectHolder(std::move(obj)));
+ }
+
+ template
+ Result AcceptMitmSession(Handle mitm_port_handle, std::shared_ptr obj, std::shared_ptr<::Service> &&fsrv) {
+ return this->AcceptMitmSession(mitm_port_handle, cmif::ServiceObjectHolder(std::move(obj)), std::forward>(fsrv));
+ }
+
+ Result ProcessRequest(ServerSession *session, const cmif::PointerAndSize &message);
+ };
+
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp
new file mode 100644
index 0000000..ff97dff
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp
@@ -0,0 +1,1105 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "../sf_common.hpp"
+#include "../sf_service_object.hpp"
+#include "../sf_out.hpp"
+#include "../sf_buffers.hpp"
+#include "../sf_handles.hpp"
+#include "../cmif/sf_cmif_pointer_and_size.hpp"
+#include "../cmif/sf_cmif_service_dispatch.hpp"
+#include "../cmif/sf_cmif_service_object_holder.hpp"
+#include "../cmif/sf_cmif_domain_api.hpp"
+#include "../hipc/sf_hipc_api.hpp"
+#include "../hipc/sf_hipc_server_session_manager.hpp"
+
+/* Serialization classes. */
+namespace sts::sf {
+
+ namespace impl {
+
+ struct ProcessIdHolder {
+ os::ProcessId process_id;
+
+ constexpr explicit operator os::ProcessId() const { return this->process_id; }
+ constexpr os::ProcessId GetValue() const { return this->process_id; }
+ constexpr void SetValue(const os::ProcessId &p) { this->process_id = p; }
+ };
+
+ }
+
+ struct ClientProcessId : public impl::ProcessIdHolder {};
+ static_assert(std::is_trivial::value && sizeof(ClientProcessId) == sizeof(os::ProcessId), "ClientProcessId");
+
+ struct ClientAppletResourceUserId : public impl::ProcessIdHolder {};
+ static_assert(std::is_trivial::value && sizeof(ClientAppletResourceUserId ) == sizeof(os::ProcessId), "ClientAppletResourceUserId");
+
+ namespace impl {
+
+ constexpr inline Result MarshalProcessId(ClientProcessId &client, const os::ProcessId &client_process_id) {
+ client.SetValue(client_process_id);
+ return ResultSuccess;
+ }
+
+ constexpr inline Result MarshalProcessId(ClientAppletResourceUserId &client, const os::ProcessId &client_process_id) {
+ if (client.GetValue() != client_process_id && client.GetValue() != os::ProcessId{}) {
+ return ResultServiceFrameworkPreconditionViolation;
+ }
+ return ResultSuccess;
+ }
+
+ }
+
+ namespace impl {
+
+ struct OutObjectTag{};
+
+ }
+
+ template
+ class Out> : public impl::OutObjectTag {
+ static_assert(std::is_base_of::value, "Out> requires ServiceObject base.");
+
+ template
+ friend class Out;
+
+ public:
+ using ServiceImplType = ServiceImpl;
+ private:
+ cmif::ServiceObjectHolder *srv;
+ cmif::DomainObjectId *object_id;
+ public:
+ Out(cmif::ServiceObjectHolder *s, cmif::DomainObjectId *o) : srv(s), object_id(o) { /* ... */ }
+
+ void SetValue(std::shared_ptr &&s, cmif::DomainObjectId new_object_id = cmif::InvalidDomainObjectId) {
+ *this->srv = ServiceObjectHolder(std::move(s));
+ if (new_object_id != cmif::InvalidDomainObjectId) {
+ *this->object_id = new_object_id;
+ }
+ }
+ };
+
+}
+
+
+namespace sts::sf::impl {
+
+ /* Machinery for filtering type lists. */
+ template
+ struct TupleCat;
+
+ template
+ struct TupleCat, std::tuple> {
+ using type = std::tuple;
+ };
+
+ template typename Cond>
+ struct TupleFilter {
+ private:
+ template
+ struct ImplType;
+
+ template
+ struct ImplType> {
+ using type = Res;
+ };
+
+ template
+ struct ImplType> {
+ using type = typename std::conditional::value,
+ typename ImplType>::type, std::tuple>::type,
+ typename ImplType>::type
+ >::type;
+ };
+ public:
+ template
+ using FilteredType = typename ImplType, T>::type;
+ };
+
+ enum class ArgumentType {
+ InData,
+ OutData,
+ Buffer,
+ InHandle,
+ OutHandle,
+ InObject,
+ OutObject,
+ };
+
+ template
+ struct IsInObject : public std::false_type{};
+
+ template
+ struct IsInObject> : public std::true_type {
+ static_assert(std::is_base_of::value, "Invalid IsInObject>");
+ };
+
+ template
+ constexpr inline ArgumentType GetArgumentType = [] {
+ if constexpr (sf::IsBuffer) {
+ return ArgumentType::Buffer;
+ } else if constexpr (IsInObject::value) {
+ return ArgumentType::InObject;
+ } else if constexpr (std::is_base_of::value) {
+ return ArgumentType::OutObject;
+ } else if constexpr (std::is_base_of::value) {
+ return ArgumentType::InHandle;
+ } else if constexpr (std::is_base_of::value) {
+ return ArgumentType::OutHandle;
+ } else if constexpr (std::is_base_of::value) {
+ return ArgumentType::OutData;
+ } else if constexpr (std::is_trivial::value && !std::is_pointer::value) {
+ return ArgumentType::InData;
+ } else {
+ static_assert(!std::is_same::value, "Invalid ArgumentType");
+ }
+ }();
+
+ template
+ struct ArgumentTypeFilter : public std::bool_constant == ArgT>{};
+
+ template
+ struct TypeEqualityFilter : public std::bool_constant::value>{};
+
+ /* Argument type filters. */
+ template
+ using InDataFilter = ArgumentTypeFilter;
+
+ template
+ using OutDataFilter = ArgumentTypeFilter;
+
+ template
+ using BufferFilter = ArgumentTypeFilter;
+
+ template
+ using InHandleFilter = ArgumentTypeFilter;
+
+ template
+ using OutHandleFilter = ArgumentTypeFilter;
+
+ template
+ using InObjectFilter = ArgumentTypeFilter;
+
+ template
+ using OutObjectFilter = ArgumentTypeFilter;
+
+ template
+ struct ProcessIdHolderFilter : public std::bool_constant::value>{};
+
+ /* Handle kind filters. */
+ template
+ using InMoveHandleFilter = TypeEqualityFilter;
+
+ template
+ using InCopyHandleFilter = TypeEqualityFilter;
+
+ template
+ using OutMoveHandleFilter = TypeEqualityFilter>;
+
+ template
+ using OutCopyHandleFilter = TypeEqualityFilter>;
+
+ template
+ struct BufferAttributeArrayGetter;
+
+ template
+ struct BufferAttributeArrayGetter> {
+ static constexpr std::array value = { BufferAttributes..., };
+ };
+
+ template<>
+ struct BufferAttributeArrayGetter> {
+ static constexpr std::array value{};
+ };
+
+ template
+ struct BufferAttributeCounter {
+
+ template
+ static constexpr size_t GetCount(const std::array &attributes_array) {
+ size_t count = 0;
+ for (size_t i = 0; i < Size; i++) {
+ if (Predicate(attributes_array[i])) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ };
+
+ NX_CONSTEXPR size_t InHipcMapAliasBufferPredicate(const u32 attribute) {
+ if ((attribute & SfBufferAttr_In) && !(attribute & SfBufferAttr_Out)) {
+ return (attribute & SfBufferAttr_HipcMapAlias) || (attribute & SfBufferAttr_HipcAutoSelect);
+ } else {
+ return false;
+ }
+ }
+
+ NX_CONSTEXPR size_t OutHipcMapAliasBufferPredicate(const u32 attribute) {
+ if (!(attribute & SfBufferAttr_In) && (attribute & SfBufferAttr_Out)) {
+ return (attribute & SfBufferAttr_HipcMapAlias) || (attribute & SfBufferAttr_HipcAutoSelect);
+ } else {
+ return false;
+ }
+ }
+
+ NX_CONSTEXPR size_t InHipcPointerBufferPredicate(const u32 attribute) {
+ if ((attribute & SfBufferAttr_In) && !(attribute & SfBufferAttr_Out)) {
+ return (attribute & SfBufferAttr_HipcPointer) || (attribute & SfBufferAttr_HipcAutoSelect);
+ } else {
+ return false;
+ }
+ }
+
+ NX_CONSTEXPR size_t OutHipcPointerBufferPredicate(const u32 attribute) {
+ if (!(attribute & SfBufferAttr_In) && (attribute & SfBufferAttr_Out)) {
+ return (attribute & SfBufferAttr_HipcPointer) || (attribute & SfBufferAttr_HipcAutoSelect);
+ } else {
+ return false;
+ }
+ }
+
+ NX_CONSTEXPR size_t FixedSizeOutHipcPointerBufferPredicate(const u32 attribute) {
+ return OutHipcPointerBufferPredicate(attribute) && (attribute & SfBufferAttr_FixedSize);
+ }
+
+ template
+ struct RawDataOffsetCalculator;
+
+ template
+ struct RawDataOffsetCalculator> {
+ private:
+ template
+ struct LayoutHelper {
+ static_assert(GetArgumentType == ArgumentType::InData, "LayoutHelper InData");
+ static constexpr size_t Alignment = alignof(T);
+ static constexpr size_t Size = sizeof(T);
+ };
+ template
+ struct LayoutHelper> {
+ static_assert(GetArgumentType == ArgumentType::InData, "LayoutHelper OutData");
+ static constexpr size_t Alignment = alignof(T);
+ static constexpr size_t Size = sizeof(T);
+ };
+
+ static constexpr void StableSort(std::array &map, const std::array &values) {
+ /* Use insertion sort, which is stable and optimal for small numbers of parameters. */
+ for (size_t i = 1; i < sizeof...(Ts); i++) {
+ for (size_t j = i; j > 0 && values[j-1] > values[j]; j--) {
+ /* std::swap is not constexpr until c++20 :( */
+ /* TODO: std::swap(map[j], map[j-1]); */
+ const size_t tmp = map[j];
+ map[j] = map[j-1];
+ map[j-1] = tmp;
+ }
+ }
+ }
+ public:
+ static constexpr std::array Offsets = [] {
+ std::array offsets{};
+ offsets[0] = 0;
+ if constexpr (sizeof...(Ts) > 0) {
+ /* Get size, alignment for each type. */
+ const std::array sizes = { LayoutHelper::Size... };
+ const std::array aligns = { LayoutHelper::Alignment... };
+
+ /* We want to sort...by alignment. */
+ std::array map = {};
+ for (size_t i = 0; i < sizeof...(Ts); i++) { map[i] = i; }
+ StableSort(map, aligns);
+
+ /* Iterate over sorted sizes. */
+ size_t cur_offset = 0;
+ for (size_t i : map) {
+ cur_offset = util::AlignUp(cur_offset, aligns[i]);
+ offsets[i] = cur_offset;
+ cur_offset += sizes[i];
+ }
+ offsets[sizeof...(Ts)] = cur_offset;
+ }
+ return offsets;
+ }();
+
+ };
+
+ struct ArgumentSerializationInfo {
+ /* Type used to select from below fields. */
+ ArgumentType arg_type;
+
+ /* Raw data indexing. */
+ size_t in_raw_data_index;
+ size_t out_raw_data_index;
+
+ /* Buffer indexing. */
+ size_t buffer_index;
+ size_t send_map_alias_index;
+ size_t recv_map_alias_index;
+ size_t send_pointer_index;
+ size_t recv_pointer_index;
+ size_t unfixed_recv_pointer_index;
+ size_t fixed_size;
+
+ /* Handle indexing. */
+ size_t in_move_handle_index;
+ size_t in_copy_handle_index;
+ size_t out_move_handle_index;
+ size_t out_copy_handle_index;
+
+ /* Object indexing. */
+ size_t in_object_index;
+ size_t out_object_index;
+ };
+
+ template
+ struct CommandMetaInfo {
+ private:
+ template
+ static R GetReturnTypeImpl(R(C::*)(A...));
+
+ template
+ static C GetClassTypeImpl(R(C::*)(A...));
+
+ template
+ static std::tuple::type...> GetArgsImpl(R(C::*)(A...));
+ public:
+ using ReturnType = decltype(GetReturnTypeImpl(MemberFunction));
+ using ClassType = decltype(GetClassTypeImpl(MemberFunction));
+ using ArgsType = decltype(GetArgsImpl(MemberFunction));
+
+ static constexpr bool ReturnsResult = std::is_same::value;
+ static constexpr bool ReturnsVoid = std::is_same::value;
+ static_assert(ReturnsResult || ReturnsVoid, "Service Commands must return Result or void.");
+
+ using InDatas = TupleFilter::FilteredType;
+ using OutDatas = TupleFilter::FilteredType;
+ using Buffers = TupleFilter::FilteredType;
+ using InHandles = TupleFilter::FilteredType;
+ using OutHandles = TupleFilter::FilteredType;
+ using InObjects = TupleFilter::FilteredType;
+ using OutObjects = TupleFilter::FilteredType;
+
+ /* Not kept separate from InDatas when processing, for reasons. */
+ using InProcessIdHolders = TupleFilter::FilteredType;
+ /* TODO: Support OutProcessIdHolders? */
+
+ static constexpr size_t NumInDatas = std::tuple_size::value;
+ static constexpr size_t NumOutDatas = std::tuple_size::value;
+ static constexpr size_t NumBuffers = std::tuple_size::value;
+ static constexpr size_t NumInHandles = std::tuple_size::value;
+ static constexpr size_t NumOutHandles = std::tuple_size::value;
+ static constexpr size_t NumInObjects = std::tuple_size::value;
+ static constexpr size_t NumOutObjects = std::tuple_size::value;
+
+ static constexpr size_t NumInProcessIdHolders = std::tuple_size::value;
+ static constexpr bool HasInProcessIdHolder = NumInProcessIdHolders >= 1;
+
+ template
+ static constexpr bool IsInProcessIdHolder = GetArgumentType == ArgumentType::InData && std::is_base_of::value;
+
+ template
+ static constexpr size_t GetInProcessIdHolderIndex(std::index_sequence) {
+ static_assert(sizeof...(Is) == std::tuple_size::value, "Invalid InProcessIdHolderIndexGetter invocation");
+ constexpr std::array::value> is_in_process_id_holder = { IsInProcessIdHolder::type>... };
+ for (size_t i = 0; i < std::tuple_size::value; i++) {
+ if (is_in_process_id_holder[i]) {
+ return i;
+ }
+ }
+ return std::numeric_limits::max();
+ }
+
+ static constexpr size_t InProcessIdHolderIndex = GetInProcessIdHolderIndex(std::make_index_sequence::value>());
+
+ static_assert(NumInProcessIdHolders <= 1, "Methods must take in 0 or 1 ProcessIdHolders");
+ static_assert(!HasInProcessIdHolder || InProcessIdHolderIndex != std::numeric_limits::max(), "Unable to identify InProcessIdHolder Index");
+ static_assert(NumBuffers <= 8, "Methods must take in <= 8 Buffers");
+ static_assert(NumInHandles <= 8, "Methods must take in <= 8 Handles");
+ static_assert(NumOutHandles + NumOutObjects <= 8, "Methods must output <= 8 Handles");
+
+ /* Buffer marshalling. */
+ static constexpr std::array BufferAttributes = BufferAttributeArrayGetter::value;
+ static constexpr size_t NumInHipcMapAliasBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
+ static constexpr size_t NumOutHipcMapAliasBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
+ static constexpr size_t NumInHipcPointerBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
+ static constexpr size_t NumOutHipcPointerBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
+ static constexpr size_t NumFixedSizeOutHipcPointerBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
+ static constexpr size_t NumUnfixedSizeOutHipcPointerBuffers = NumOutHipcPointerBuffers - NumFixedSizeOutHipcPointerBuffers;
+
+ /* In/Out data marshalling. */
+ static constexpr std::array InDataOffsets = RawDataOffsetCalculator::Offsets;
+ static constexpr size_t InDataRawUnfixedOutPointerSizeOffset = util::AlignUp(InDataOffsets[NumInDatas] + 0x10, alignof(u16));
+ static constexpr size_t InDataSize = util::AlignUp(util::AlignUp(InDataOffsets[NumInDatas], alignof(u16)) + sizeof(u16) * NumUnfixedSizeOutHipcPointerBuffers, alignof(u32));
+
+ static constexpr std::array OutDataOffsets = RawDataOffsetCalculator::Offsets;
+ static constexpr size_t OutDataSize = util::AlignUp(OutDataOffsets[NumOutDatas], alignof(u32));
+ static constexpr size_t OutDataAlign = [] {
+ if constexpr (std::tuple_size::value) {
+ return alignof(typename std::tuple_element<0, OutDatas>::type);
+ }
+ return size_t();
+ }();
+
+ /* Handle marshalling. */
+ static constexpr size_t NumInMoveHandles = std::tuple_size::FilteredType>::value;
+ static constexpr size_t NumInCopyHandles = std::tuple_size::FilteredType>::value;
+
+ static constexpr size_t NumOutMoveHandles = std::tuple_size::FilteredType>::value;
+ static constexpr size_t NumOutCopyHandles = std::tuple_size::FilteredType>::value;
+
+ static_assert(NumInMoveHandles + NumInCopyHandles == NumInHandles, "NumInMoveHandles + NumInCopyHandles == NumInHandles");
+ static_assert(NumOutMoveHandles + NumOutCopyHandles == NumOutHandles, "NumOutMoveHandles + NumOutCopyHandles == NumOutHandles");
+
+ /* Construction of argument serialization structs. */
+ private:
+ template
+ struct ArgumentSerializationInfoConstructor;
+
+ template
+ struct ArgumentSerializationInfoConstructor> {
+
+ template
+ static constexpr ArgumentSerializationInfo ProcessUpdate(ArgumentSerializationInfo ¤t_info) {
+ /* Save a copy of the current state to return. */
+ ArgumentSerializationInfo returned_info = current_info;
+
+ /* Clear previous iteration's fixed size. */
+ returned_info.fixed_size = 0;
+ current_info.fixed_size = 0;
+
+ constexpr auto arg_type = GetArgumentType;
+ returned_info.arg_type = arg_type;
+ if constexpr (arg_type == ArgumentType::InData) {
+ /* New rawdata, so increment index. */
+ current_info.in_raw_data_index++;
+ } else if constexpr (arg_type == ArgumentType::OutData) {
+ /* New rawdata, so increment index. */
+ current_info.out_raw_data_index++;
+ } else if constexpr (arg_type == ArgumentType::InHandle) {
+ /* New InHandle, increment the appropriate index. */
+ if constexpr (std::is_same::value) {
+ current_info.in_move_handle_index++;
+ } else if constexpr (std::is_same::value) {
+ current_info.in_copy_handle_index++;
+ } else {
+ static_assert(!std::is_same::value, "Invalid InHandle kind");
+ }
+ } else if constexpr (arg_type == ArgumentType::OutHandle) {
+ /* New OutHandle, increment the appropriate index. */
+ if constexpr (std::is_same>::value) {
+ current_info.out_move_handle_index++;
+ } else if constexpr (std::is_same>::value) {
+ current_info.out_copy_handle_index++;
+ } else {
+ static_assert(!std::is_same::value, "Invalid OutHandle kind");
+ }
+ } else if constexpr (arg_type == ArgumentType::InObject) {
+ /* New InObject, increment the appropriate index. */
+ current_info.in_object_index++;
+ } else if constexpr (arg_type == ArgumentType::OutObject) {
+ /* New OutObject, increment the appropriate index. */
+ current_info.out_object_index++;
+ } else if constexpr (arg_type == ArgumentType::Buffer) {
+ /* New Buffer, increment the appropriate index. */
+ const auto attributes = BufferAttributes[current_info.buffer_index];
+ current_info.buffer_index++;
+
+ if (attributes & SfBufferAttr_HipcMapAlias) {
+ if (attributes & SfBufferAttr_In) {
+ current_info.send_map_alias_index++;
+ } else if (attributes & SfBufferAttr_Out) {
+ current_info.recv_map_alias_index++;
+ }
+ } else if (attributes & SfBufferAttr_HipcPointer) {
+ if (attributes & SfBufferAttr_In) {
+ current_info.send_pointer_index++;
+ } else if (attributes & SfBufferAttr_Out) {
+ current_info.recv_pointer_index++;
+ if (!(attributes & SfBufferAttr_FixedSize)) {
+ current_info.unfixed_recv_pointer_index++;
+ } else {
+ returned_info.fixed_size = LargeDataSize;
+ }
+ }
+ } else if (attributes & SfBufferAttr_HipcAutoSelect) {
+ if (attributes & SfBufferAttr_In) {
+ current_info.send_map_alias_index++;
+ current_info.send_pointer_index++;
+ } else if (attributes & SfBufferAttr_Out) {
+ current_info.recv_map_alias_index++;
+ current_info.recv_pointer_index++;
+ if (!(attributes & SfBufferAttr_FixedSize)) {
+ current_info.unfixed_recv_pointer_index++;
+ } else {
+ returned_info.fixed_size = LargeDataSize;
+ }
+ }
+ }
+ } else {
+ static_assert(!std::is_same::value, "Invalid ArgumentType");
+ }
+
+ return returned_info;
+ }
+
+ static constexpr std::array ArgumentSerializationInfos = [] {
+ ArgumentSerializationInfo current_info = {};
+
+ return std::array{ ProcessUpdate(current_info)... };
+ }();
+
+ };
+ public:
+ static constexpr std::array