Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions include/RE/B/BSInputDevice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include "RE/B/BSFixedString.h"

namespace RE
{
class BSInputDevice
{
public:
SF_RTTI_VTABLE(BSInputDevice);

virtual ~BSInputDevice(); // 00

// add
virtual void Initialize() = 0; // 01
virtual void Poll(float a_pollDelta) = 0; // 02
virtual void Shutdown() = 0; // 03
virtual bool GetKeyNameFromCode(std::uint32_t a_keyCode, BSFixedStringCS& a_name) const; // 04
virtual std::uint32_t GetKeyCodeFromName(const char* a_name) const; // 05
virtual bool GetMappedKeyCode(std::uint32_t a_keyCode, std::uint32_t& a_mappedKeyCode) const; // 06
virtual bool IsEnabled() const { return true; } // 07
virtual void ClearInputState() = 0; // 08

// members
std::uint8_t unk08[0x78]; // 08
};
static_assert(sizeof(BSInputDevice) == 0x80);
}
32 changes: 32 additions & 0 deletions include/RE/B/BSInputDeviceManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include "RE/B/BSInputEventSingleUser.h"
#include "RE/B/BSKeyboardDevice.h"
#include "RE/B/BSTSingleton.h"

namespace RE
{
class BSInputDeviceManager :
public BSTSingletonSDM<BSInputDeviceManager>, // 00
public BSInputEventSingleUser // 10
{
public:
SF_RTTI_VTABLE(BSInputDeviceManager);

~BSInputDeviceManager() override = default;

[[nodiscard]] static BSInputDeviceManager* GetSingleton();

[[nodiscard]] BSKeyboardDevice* GetKeyboard() const
{
return static_cast<BSKeyboardDevice*>(devices[0]);
}

// members
std::uint64_t unk60; // 60
BSInputDevice* devices[5]; // 68
std::uint8_t unk90[0x48]; // 90
};
static_assert(offsetof(BSInputDeviceManager, devices) == 0x68);
static_assert(sizeof(BSInputDeviceManager) == 0xD8);
}
21 changes: 21 additions & 0 deletions include/RE/B/BSInputEventSingleUser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include "RE/B/BSInputEventReceiver.h"
#include "RE/B/BSInputEventUser.h"

namespace RE
{
class BSInputEventSingleUser :
public BSInputEventReceiver, // 00
public BSInputEventUser // 10
{
public:
SF_RTTI_VTABLE(BSInputEventSingleUser);

virtual ~BSInputEventSingleUser() = default; // 00

// override (BSInputEventReceiver)
virtual void PerformInputProcessing(const InputEvent* a_queueHead) override; // 01
};
static_assert(sizeof(BSInputEventSingleUser) == 0x50);
}
21 changes: 17 additions & 4 deletions include/RE/B/BSInputEventUser.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ namespace RE
virtual ~InputEvent() = default; // 00

// add
virtual bool HasIDCode() const { return false; }
virtual const BSFixedString QUserEvent() const { return ""; }
virtual bool HasIDCode() const { return false; }
virtual const BSFixedString& QUserEvent() const
{
static const BSFixedString empty;
return empty;
}

// members
DeviceType deviceType{ DeviceType::kNone }; // 08
Expand All @@ -76,9 +80,11 @@ namespace RE
// override (InputEvent)
virtual bool HasIDCode() const override { return true; }

virtual const BSFixedString QUserEvent() const override
virtual const BSFixedString& QUserEvent() const override
{
return disabled ? "DISABLED" : strUserEvent;
using func_t = const BSFixedString& (*)(const IDEvent*);
static REL::Relocation<func_t> func{ ID::IDEvent::QUserEvent };
return func(this);
}

// members
Expand Down Expand Up @@ -150,6 +156,13 @@ namespace RE

SF_HEAP_REDEFINE_NEW(BSInputEventUser);

void DispatchEvent(const InputEvent* a_event)
{
using func_t = decltype(&BSInputEventUser::DispatchEvent);
static REL::Relocation<func_t> func{ ID::BSInputEventUser::DispatchEvent };
func(this, a_event);
}

// members
std::uint8_t pad08[0x30]; // 08
bool inputEventHandlingEnabled{ true }; // 38
Expand Down
16 changes: 16 additions & 0 deletions include/RE/B/BSKeyboardDevice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "RE/B/BSInputDevice.h"

namespace RE
{
class BSKeyboardDevice :
public BSInputDevice // 00
{
public:
SF_RTTI_VTABLE(BSKeyboardDevice);

virtual ~BSKeyboardDevice() = default; // 00
};
static_assert(sizeof(BSKeyboardDevice) == 0x80);
}
152 changes: 152 additions & 0 deletions include/RE/C/ControlMap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#pragma once

#include "RE/B/BSFixedString.h"
#include "RE/B/BSInputEventUser.h"
#include "RE/B/BSTArray.h"
#include "RE/B/BSTSingleton.h"
#include "RE/U/UserEvents.h"

#include <array>
#include <span>
#include <string>
#include <string_view>

namespace RE
{
class ControlMap :
public BSTSingletonSDM<ControlMap> // 000
{
public:
SF_RTTI_VTABLE(ControlMap);

using InputContextID = UserEvents::INPUT_CONTEXT_ID;

enum class BindingSlot : std::uint8_t
{
kMain = 0,
kAlternate = 1,
kUnbound = 2
};

struct UserEventMapping
{
BSFixedString eventID; // 00
std::uint32_t keyCode; // 08
std::uint32_t modifierKeyCode; // 0C
BindingSlot bindingSlot; // 10
std::uint8_t unk11; // 11
std::uint16_t unk12; // 12
std::uint8_t sortIndex; // 14
std::byte pad15[3]; // 15
std::uint32_t contextMask; // 18
std::uint8_t metadata; // 1C
bool visibleInControls; // 1D
bool usesLinkedBinding; // 1E
std::uint8_t unk1F; // 1F
bool required; // 20
std::byte pad21[7]; // 21
};
static_assert(offsetof(UserEventMapping, keyCode) == 0x08);
static_assert(offsetof(UserEventMapping, bindingSlot) == 0x10);
static_assert(offsetof(UserEventMapping, sortIndex) == 0x14);
static_assert(offsetof(UserEventMapping, contextMask) == 0x18);
static_assert(offsetof(UserEventMapping, visibleInControls) == 0x1D);
static_assert(offsetof(UserEventMapping, required) == 0x20);
static_assert(sizeof(UserEventMapping) == 0x28);

// Partial layout.
struct InputContext
{
std::array<BSTArray<UserEventMapping>, 3> deviceMappings; // 00
};
static_assert(sizeof(InputContext) == 0x30);

struct ActiveInputContext
{
InputContextID inputContextID; // 00
std::byte pad01[0x0F]; // 01
};
static_assert(sizeof(ActiveInputContext) == 0x10);

static constexpr std::size_t MAPPABLE_INPUT_CONTEXT_COUNT = 0x4E;
static constexpr std::size_t INPUT_CONTEXT_NAME_COUNT = 0x51;

// Formats a defaults row; keyboard codes are virtual-key codes.
[[nodiscard]] static std::string FormatMappingRow(
const char* a_event, const char* a_keyboard, const char* a_mouse, const char* a_gamepad,
bool a_keyboardVisible, bool a_mouseVisible, bool a_gamepadVisible,
std::uint32_t a_controlMask, std::uint32_t a_groupMask, bool a_required);

[[nodiscard]] static std::string FormatMappingRow(
const char* a_event, std::uint32_t a_keyboard, std::uint32_t a_mouse, std::uint32_t a_gamepad,
bool a_keyboardVisible, bool a_mouseVisible, bool a_gamepadVisible,
std::uint32_t a_controlMask, std::uint32_t a_groupMask, bool a_required);

// Loading or parsing replaces mappings and invalidates borrowed views.
void LoadMappings();

// Parses complete defaults; saved overrides and link resolution are separate.
void ParseMappings(const char* a_text);

void ResolveLinkedMappings();

[[nodiscard]] static ControlMap* GetSingleton()
{
static REL::Relocation<ControlMap**> singleton{ ID::ControlMap::Singleton };
return *singleton;
}

[[nodiscard]] InputContext* GetInputContext(InputContextID a_id) noexcept
{
const auto index = static_cast<std::size_t>(a_id);
return index < inputContexts.size() ? inputContexts[index] : nullptr;
}

[[nodiscard]] const InputContext* GetInputContext(InputContextID a_id) const noexcept
{
const auto index = static_cast<std::size_t>(a_id);
return index < inputContexts.size() ? inputContexts[index] : nullptr;
}

// Borrowed on the game thread; invalidated by remapping.
[[nodiscard]] std::span<const UserEventMapping> GetMappings(
InputContextID a_id, InputEvent::DeviceType a_device) const noexcept
{
const auto device = static_cast<std::size_t>(a_device);
const auto* inputContext = GetInputContext(a_id);
if (!inputContext || device >= inputContext->deviceMappings.size()) {
return {};
}

const auto& mappings = inputContext->deviceMappings[device];
return { mappings.data(), mappings.size() };
}

// Invalidated by context-stack changes.
[[nodiscard]] std::span<const ActiveInputContext> GetActiveInputContexts() const noexcept
{
return { activeInputContexts.data(), activeInputContexts.size() };
}

[[nodiscard]] static std::string_view GetInputContextName(InputContextID a_id) noexcept
{
const auto index = static_cast<std::size_t>(a_id);
if (index >= INPUT_CONTEXT_NAME_COUNT) {
return {};
}

static REL::Relocation<const char* const*> names{ ID::ControlMap::InputContextNameTable };
const auto* name = names.get()[index];
return name ? std::string_view{ name } : std::string_view{};
}

// members
std::array<InputContext*, MAPPABLE_INPUT_CONTEXT_COUNT> inputContexts; // 010
std::byte pad280[0x28]; // 280
BSTArray<ActiveInputContext> activeInputContexts; // 2A8
std::byte pad2B8[0xE8]; // 2B8
};
static_assert(offsetof(ControlMap, inputContexts) == 0x10);
static_assert(offsetof(ControlMap, activeInputContexts) == 0x2A8);
static_assert(sizeof(ControlMap) == 0x3A0);
}
Loading
Loading