Skip to content
Merged
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ graph_info.json

.idea
*ReSharper*

# Local message-box validation harness
/tests/message_box/
21 changes: 11 additions & 10 deletions include/RE/B/BGSPackIn.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ namespace RE

class BGSPackIn :
public TESBoundObject, // 000
public BGSKeywordForm, // 0E0
public BGSPropertySheet, // 110
public BGSPreviewTransform, // 120
public BGSForcedLocRefType, // 168
public BGSNativeTerminalForm // 180
public BGSKeywordForm, // 0E8
public BGSPropertySheet, // 118
public BGSPreviewTransform, // 128
public BGSForcedLocRefType, // 170
public BGSNativeTerminalForm // 188
{
public:
SF_RTTI_VTABLE(BGSPackIn);
Expand All @@ -28,11 +28,12 @@ namespace RE
~BGSPackIn() override; // 00

// members
TESObjectCELL* cell; // 190
std::uint32_t flags; // 198
BSStringT<char> filter; // 19C
BSTArray<BGSLayeredMaterialSwap*> materialSwaps; // 1B0
std::uint32_t unk1C0; // 1C0
TESObjectCELL* cell; // 198
std::uint32_t flags; // 1A0
BSString filter; // 1A4
BSTArray<BGSLayeredMaterialSwap*> materialSwaps; // 1B8
std::uint32_t unk1C0; // 1C8
};
static_assert(offsetof(BGSPackIn, filter) == 0x1A4);
static_assert(sizeof(BGSPackIn) == 0x1D0);
}
100 changes: 49 additions & 51 deletions include/RE/B/BSStringT.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "RE/M/MemoryManager.h"

namespace RE
{
template <class CharT, std::uint16_t>
Expand Down Expand Up @@ -33,89 +35,85 @@ namespace RE
using traits_type = std::char_traits<value_type>;

constexpr BSStringT() noexcept = default;

BSStringT(const BSStringT& a_rhs) { copy_from(a_rhs); }

BSStringT(BSStringT&& a_rhs) noexcept
{
if constexpr (allocator_type::propagate_on_container_move_assignment::value) {
move_from(std::move(a_rhs));
} else {
copy_from(a_rhs);
}
}

BSStringT(const_pointer a_string) { Assign(a_string); }
BSStringT(const BSStringT& a_rhs) { Assign(a_rhs.c_str()); }
BSStringT(BSStringT&& a_rhs) noexcept { move_from(std::move(a_rhs)); }
~BSStringT() { release(); }

BSStringT& operator=(const BSStringT& a_rhs)
{
if (this != std::addressof(a_rhs)) {
copy_from(a_rhs);
Assign(a_rhs.c_str());
}
return *this;
}

BSStringT& operator=(BSStringT&& a_rhs) noexcept
{
if (this != std::addressof(a_rhs)) {
if constexpr (allocator_type::propagate_on_container_move_assignment::value) {
move_from(std::move(a_rhs));
} else {
copy_from(a_rhs);
}
release();
move_from(std::move(a_rhs));
}
return *this;
}

[[nodiscard]] const_pointer data() const noexcept { return _data; }
[[nodiscard]] pointer data() noexcept { return _data; }

[[nodiscard]] const_pointer c_str() const noexcept { return data(); }
BSStringT& operator=(const_pointer a_string)
{
Assign(a_string);
return *this;
}

[[nodiscard]] operator std::basic_string_view<value_type, traits_type>() const noexcept { return { data(), size() }; }
bool Assign(const_pointer a_string, std::size_t a_reserveLength = 0)
{
using func_t = bool (*)(BSStringT*, const_pointer, std::size_t);
static REL::Relocation<func_t> func{ ID::BSStringT::Assign };
return func(this, a_string, a_reserveLength);
}

[[nodiscard]] bool empty() const noexcept { return size() == 0; }
[[nodiscard]] const_pointer data() const noexcept
{
if (_capacity <= kInlineCapacity) {
return _storage;
}
pointer result;
std::memcpy(&result, _storage, sizeof(result));
return result;
}

[[nodiscard]] pointer data() noexcept { return const_cast<pointer>(std::as_const(*this).data()); }
[[nodiscard]] const_pointer c_str() const noexcept { return data(); }
[[nodiscard]] operator std::basic_string_view<value_type, traits_type>() const noexcept { return { data(), size() }; }
[[nodiscard]] bool empty() const noexcept { return size() == 0; }
[[nodiscard]] size_type size() const noexcept { return length(); }
[[nodiscard]] size_type length() const noexcept { return _size == N ? static_cast<size_type>(traits_type::length(c_str())) : _size; }

[[nodiscard]] size_type length() const noexcept { return _size == std::numeric_limits<size_type>::max() ? static_cast<size_type>(traits_type::length(c_str())) : _size; }
[[nodiscard]] size_type capacity() const noexcept { return _capacity; }

private:
void release()
{
allocator_type::deallocate(_data, _capacity);
_data = nullptr;
_size = 0;
_capacity = 0;
}
static constexpr size_type kInlineCapacity = 0xC;

void copy_from(const BSStringT& a_rhs)
void release()
{
release();

_size = a_rhs._size;
_capacity = a_rhs._capacity;
_data = allocator_type::allocate(_capacity);

std::uninitialized_copy_n(a_rhs._data, _size, _data);
if (_capacity > kInlineCapacity) {
allocator_type::deallocate(data(), _capacity);
}
}

void move_from(BSStringT&& a_rhs)
void move_from(BSStringT&& a_rhs) noexcept
{
release();

_data = a_rhs._data;
std::memcpy(_storage, a_rhs._storage, sizeof(_storage));
_size = a_rhs._size;
_capacity = a_rhs._capacity;

a_rhs._data = nullptr;
a_rhs._storage[0] = '\0';
a_rhs._size = 0;
a_rhs._capacity = 0;
a_rhs._capacity = 1;
}

pointer _data{}; // ??
size_type _size{}; // ??
size_type _capacity{}; // ??
value_type _storage[kInlineCapacity]{}; // 00
size_type _capacity{ 1 }; // 0C (includes the terminator)
size_type _size{}; // 0E
};

using BSString = BSStringT<char>;
static_assert(sizeof(BSString) == 0x10);
static_assert(alignof(BSString) == 0x2);
}
12 changes: 11 additions & 1 deletion include/RE/IDs.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,11 @@ namespace RE::ID
inline constexpr REL::ID Unlock{ 35630 };
}

namespace BSStringT
{
inline constexpr REL::ID Assign{ 36345 };
}

namespace BSStringPool
{
namespace Entry
Expand Down Expand Up @@ -812,7 +817,6 @@ namespace RE::ID
{
inline constexpr REL::ID FormatMappingRow{ 124100 };
inline constexpr REL::ID FormatMappingRowWithKeyCodes{ 124101 };
inline constexpr REL::ID MappingRowStringDtor{ 35706 };
inline constexpr REL::ID Singleton{ 938003 };
inline constexpr REL::ID InputContextNameTable{ 360965 };
inline constexpr REL::ID LoadMappings{ 124116 };
Expand Down Expand Up @@ -1342,6 +1346,12 @@ namespace RE::ID
inline constexpr REL::ID GetEventSource{ 0 }; // 167118
}

namespace MessageMenuManager
{
inline constexpr REL::ID Singleton{ 938019 };
inline constexpr REL::ID CreateMessageBox{ 114231 };
}

namespace Misc
{
inline constexpr REL::ID DebugNotification{ 0 }; // 138728
Expand Down
5 changes: 5 additions & 0 deletions include/RE/M/Misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

namespace RE
{
// Queues a message with the game's localized OK button. Call on the game
// thread after data loads. A null message is empty; a null header uses DEBUG.
// Does nothing if the message manager is absent.
void DebugMessageBox(const char* a_message, const char* a_header = "DEBUG");

inline void DebugNotification(const char* a_notification, const char* a_soundToPlay = nullptr, bool a_cancelIfAlreadyQueued = true, bool a_arg4 = false)
{
using func_t = decltype(&DebugNotification);
Expand Down
7 changes: 4 additions & 3 deletions include/RE/S/Script.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,11 @@ namespace RE
struct ScriptVariable
{
// members
SCRIPT_LOCAL data; // 00
BSStringT<char> name; // 10
SCRIPT_LOCAL data; // 00
BSString name; // 0C
};
static_assert(sizeof(ScriptVariable) == 0x20);
static_assert(offsetof(ScriptVariable, name) == 0x0C);
static_assert(sizeof(ScriptVariable) == 0x1C);

class Script
{
Expand Down
23 changes: 4 additions & 19 deletions include/RE/T/TESFile.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "RE/B/BSStringT.h"

namespace RE
{
class TESFile
Expand Down Expand Up @@ -27,23 +29,6 @@ namespace RE
};
static_assert(sizeof(TypedIndex) == 0x4);

struct TextData
{
private:
static constexpr inline std::uint16_t kRawCapacity = 12;
// members
const char _data[kRawCapacity]; // 00
std::uint16_t _bufferSize; // 0C
std::uint16_t _size; // 0E
public:
const char* GetText() const
{
const char* result = _data;
return _size < kRawCapacity ? result : *std::bit_cast<const char**>(result);
}
};
static_assert(sizeof(TextData) == 0x10);

std::uint8_t pad0[0x38]; // 000
char fileName[260]; // 038
std::uint8_t pad13C[0x7B]; // 13C
Expand All @@ -52,8 +37,8 @@ namespace RE
std::uint8_t pad1BC[0x4]; // 1BC
std::uint8_t pad1C0[0x58]; // 1C0
TypedIndex fileIndex; // 218
TextData fileAuthor; // 21C
TextData fileDescription; // 22C
BSString fileAuthor; // 21C
BSString fileDescription; // 22C
};
static_assert(offsetof(TESFile, fileFlags) == 0x1B8);
static_assert(offsetof(TESFile, fileIndex) == 0x218);
Expand Down
38 changes: 5 additions & 33 deletions src/RE/C/ControlMap.cpp
Original file line number Diff line number Diff line change
@@ -1,46 +1,18 @@
#include "RE/C/ControlMap.h"

#include <cstring>
#include "RE/B/BSStringT.h"

namespace RE
{
namespace
{
struct alignas(8) MappingRowString
{
~MappingRowString()
{
static REL::Relocation<void (*)(MappingRowString*)> destroy{ ID::ControlMap::MappingRowStringDtor };
destroy(this);
}

[[nodiscard]] const char* c_str() const
{
if (capacity <= sizeof(storage)) {
return storage;
}
const char* data;
std::memcpy(&data, storage, sizeof(data));
return data;
}

char storage[0xC]{};
std::uint16_t capacity{ 1 };
std::uint16_t unk0E{};
};
static_assert(sizeof(MappingRowString) == 0x10);
static_assert(offsetof(MappingRowString, capacity) == 0xC);
}

std::string ControlMap::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)
{
using func_t = MappingRowString* (*)(MappingRowString*, const char*, const char*, const char*, const char*,
using func_t = BSString* (*)(BSString*, const char*, const char*, const char*, const char*,
bool, bool, bool, std::uint32_t, std::uint32_t, bool);
static REL::Relocation<func_t> func{ ID::ControlMap::FormatMappingRow };
MappingRowString text;
BSString text;
func(&text, a_event, a_keyboard, a_mouse, a_gamepad,
a_keyboardVisible, a_mouseVisible, a_gamepadVisible, a_controlMask, a_groupMask, a_required);
return text.c_str();
Expand All @@ -51,10 +23,10 @@ namespace RE
bool a_keyboardVisible, bool a_mouseVisible, bool a_gamepadVisible,
std::uint32_t a_controlMask, std::uint32_t a_groupMask, bool a_required)
{
using func_t = MappingRowString* (*)(MappingRowString*, const char*, std::uint32_t, std::uint32_t, std::uint32_t,
using func_t = BSString* (*)(BSString*, const char*, std::uint32_t, std::uint32_t, std::uint32_t,
bool, bool, bool, std::uint32_t, std::uint32_t, bool);
static REL::Relocation<func_t> func{ ID::ControlMap::FormatMappingRowWithKeyCodes };
MappingRowString text;
BSString text;
func(&text, a_event, a_keyboard, a_mouse, a_gamepad,
a_keyboardVisible, a_mouseVisible, a_gamepadVisible, a_controlMask, a_groupMask, a_required);
return text.c_str();
Expand Down
Loading
Loading