docs: add plugin setup guide

This commit is contained in:
2026-05-30 18:33:24 +12:00
parent f3db41c402
commit b61043a3fe
1776 changed files with 122386 additions and 2 deletions
+282
View File
@@ -0,0 +1,282 @@
#include "F4SE/API.h"
#include "F4SE/Interfaces.h"
#include "REL/FHookStore.h"
#include "REL/Trampoline.h"
#include "REX/TSingleton.h"
#include "REX/W32/OLE32.h"
#include "REX/W32/SHELL32.h"
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/msvc_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/spdlog.h>
namespace F4SE
{
namespace Impl
{
struct API :
public REX::TSingleton<API>
{
void Init(InitInfo, const F4SE::QueryInterface* a_intfc);
void InitLog();
void InitTrampoline();
void InitHook(REL::EHookStep a_step);
InitInfo info;
std::string pluginName{};
std::string pluginAuthor{};
REL::Version pluginVersion{};
REL::Version f4seVersion{};
PluginHandle pluginHandle{ static_cast<PluginHandle>(-1) };
std::uint32_t releaseIndex{ 0 };
std::function<const void*(const char*)> pluginInfoAccessor;
std::string_view saveFolderName{};
MessagingInterface* messagingInterface{ nullptr };
ScaleformInterface* scaleformInterface{ nullptr };
PapyrusInterface* papyrusInterface{ nullptr };
SerializationInterface* serializationInterface{ nullptr };
TaskInterface* taskInterface{ nullptr };
ObjectInterface* objectInterface{ nullptr };
TrampolineInterface* trampolineInterface{ nullptr };
};
void API::Init(InitInfo a_info, const F4SE::QueryInterface* a_intfc)
{
info = a_info;
static std::once_flag once;
std::call_once(once, [&]() {
if (const auto data = PluginVersionData::GetSingleton()) {
pluginName = data->GetPluginName();
pluginAuthor = data->GetAuthorName();
pluginVersion = data->GetPluginVersion();
} else {
std::vector<char> buf(REX::W32::MAX_PATH, '\0');
const auto size = REX::W32::GetModuleFileNameA(REX::W32::GetCurrentModule(), buf.data(), REX::W32::MAX_PATH);
if (size) {
std::filesystem::path p(buf.begin(), buf.begin() + size);
pluginName = p.stem().string();
}
}
f4seVersion = a_intfc->F4SEVersion();
pluginHandle = a_intfc->GetPluginHandle();
releaseIndex = a_intfc->GetReleaseIndex();
pluginInfoAccessor = reinterpret_cast<const Impl::F4SEInterface*>(a_intfc)->GetPluginInfo;
saveFolderName = a_intfc->GetSaveFolderName();
});
}
void API::InitLog()
{
if (info.log) {
static std::once_flag once;
std::call_once(once, [&]() {
if (saveFolderName.empty())
return;
wchar_t* knownBuffer{ nullptr };
const auto knownResult = REX::W32::SHGetKnownFolderPath(REX::W32::FOLDERID_Documents, REX::W32::KF_FLAG_DEFAULT, nullptr, std::addressof(knownBuffer));
std::unique_ptr<wchar_t[], decltype(&REX::W32::CoTaskMemFree)> knownPath(knownBuffer, REX::W32::CoTaskMemFree);
if (!knownPath || knownResult != 0) {
REX::ERROR("failed to get known folder path");
return;
}
std::filesystem::path path = knownPath.get();
path /= std::format("My Games/{}/F4SE/{}.log", GetSaveFolderName(), info.logName ? info.logName : GetPluginName());
std::vector<spdlog::sink_ptr> sinks{
std::make_shared<spdlog::sinks::msvc_sink_mt>()
};
if (info.logRotate > 0) {
constexpr auto maxSize = std::numeric_limits<std::size_t>::max();
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>(path.string(), maxSize, info.logRotate, true));
} else {
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(path.string(), true));
}
auto logger = std::make_shared<spdlog::logger>("global", sinks.begin(), sinks.end());
logger->set_level(static_cast<spdlog::level::level_enum>(info.logLevel));
logger->flush_on(static_cast<spdlog::level::level_enum>(info.logLevel));
spdlog::set_default_logger(std::move(logger));
spdlog::set_pattern(info.logPattern ? info.logPattern : "[%T.%e] [%=5t] [%L] %v");
REX::INFO("{} v{}", GetPluginName(), GetPluginVersion());
});
}
}
void API::InitTrampoline()
{
if (info.trampoline) {
static std::once_flag once;
std::call_once(once, [&]() {
if (!info.trampolineSize) {
const auto hookStore = REL::FHookStore::GetSingleton();
info.trampolineSize += hookStore->GetSizeTrampoline();
}
auto& trampoline = REL::GetTrampoline();
if (const auto intfc = GetTrampolineInterface()) {
if (const auto mem = intfc->AllocateFromBranchPool(info.trampolineSize))
trampoline.set_trampoline(mem, info.trampolineSize);
else
trampoline.create(info.trampolineSize);
}
});
}
}
void API::InitHook(REL::EHookStep a_step)
{
if (info.hook) {
const auto hookStore = REL::FHookStore::GetSingleton();
hookStore->Init();
hookStore->Enable(a_step);
}
}
}
void Init(const PreLoadInterface* a_intfc, InitInfo a_info) noexcept
{
static std::once_flag once;
std::call_once(once, [&]() {
auto api = Impl::API::GetSingleton();
api->Init(a_info, a_intfc);
api->InitLog();
api->trampolineInterface = a_intfc->QueryInterface<TrampolineInterface>(PreLoadInterface::kTrampoline);
api->InitTrampoline();
api->InitHook(REL::EHookStep::PreLoad);
});
}
void Init(const LoadInterface* a_intfc, InitInfo a_info) noexcept
{
static std::once_flag once;
std::call_once(once, [&]() {
auto api = Impl::API::GetSingleton();
api->Init(a_info, a_intfc);
api->InitLog();
api->messagingInterface = a_intfc->QueryInterface<MessagingInterface>(LoadInterface::kMessaging);
api->scaleformInterface = a_intfc->QueryInterface<ScaleformInterface>(LoadInterface::kScaleform);
api->papyrusInterface = a_intfc->QueryInterface<PapyrusInterface>(LoadInterface::kPapyrus);
api->serializationInterface = a_intfc->QueryInterface<SerializationInterface>(LoadInterface::kSerialization);
api->taskInterface = a_intfc->QueryInterface<TaskInterface>(LoadInterface::kTask);
api->objectInterface = a_intfc->QueryInterface<ObjectInterface>(LoadInterface::kObject);
api->trampolineInterface = a_intfc->QueryInterface<TrampolineInterface>(LoadInterface::kTrampoline);
api->InitTrampoline();
api->InitHook(REL::EHookStep::Load);
});
}
REL::Version GetF4SEVersion() noexcept
{
return Impl::API::GetSingleton()->f4seVersion;
}
std::string_view GetPluginName() noexcept
{
return Impl::API::GetSingleton()->pluginName;
}
std::string_view GetPluginAuthor() noexcept
{
return Impl::API::GetSingleton()->pluginAuthor;
}
REL::Version GetPluginVersion() noexcept
{
return Impl::API::GetSingleton()->pluginVersion;
}
PluginHandle GetPluginHandle() noexcept
{
return Impl::API::GetSingleton()->pluginHandle;
}
const PluginInfo* GetPluginInfo(std::string_view a_plugin) noexcept
{
if (const auto& accessor = Impl::API::GetSingleton()->pluginInfoAccessor) {
if (const auto result = accessor(a_plugin.data())) {
return static_cast<const PluginInfo*>(result);
}
}
REX::ERROR("failed to get plugin info for {}", a_plugin);
return nullptr;
}
std::uint32_t GetReleaseIndex() noexcept
{
return Impl::API::GetSingleton()->releaseIndex;
}
std::string_view GetSaveFolderName() noexcept
{
return Impl::API::GetSingleton()->saveFolderName;
}
const MessagingInterface* GetMessagingInterface() noexcept
{
return Impl::API::GetSingleton()->messagingInterface;
}
const ScaleformInterface* GetScaleformInterface() noexcept
{
return Impl::API::GetSingleton()->scaleformInterface;
}
const PapyrusInterface* GetPapyrusInterface() noexcept
{
return Impl::API::GetSingleton()->papyrusInterface;
}
const SerializationInterface* GetSerializationInterface() noexcept
{
return Impl::API::GetSingleton()->serializationInterface;
}
const TaskInterface* GetTaskInterface() noexcept
{
return Impl::API::GetSingleton()->taskInterface;
}
const ObjectInterface* GetObjectInterface() noexcept
{
return Impl::API::GetSingleton()->objectInterface;
}
const TrampolineInterface* GetTrampolineInterface() noexcept
{
return Impl::API::GetSingleton()->trampolineInterface;
}
}
namespace F4SE
{
void Init(const LoadInterface* a_intfc, const bool a_log) noexcept
{
Init(a_intfc, { .log = a_log });
}
void AllocTrampoline(std::size_t a_size) noexcept
{
auto api = Impl::API::GetSingleton();
api->info.trampoline = true;
api->info.trampolineSize = a_size;
api->InitTrampoline();
}
}
@@ -0,0 +1 @@
#include "F4SE/Impl/PCH.h"
@@ -0,0 +1,193 @@
#include "F4SE/InputMap.h"
#include "RE/C/ControlMap.h"
#include "RE/P/PC_GAMEPAD_TYPE.h"
#include "REX/PS4/SCEPAD.h"
#include "REX/W32/DINPUT.h"
#include "REX/W32/USER32.h"
#include "REX/W32/XINPUT.h"
namespace F4SE
{
std::uint32_t InputMap::XInputToScePadOffset(std::uint32_t keyMask)
{
switch (keyMask) {
case REX::W32::XINPUT_GAMEPAD_DPAD_UP:
return REX::PS4::SCE_PAD_BUTTON_UP;
case REX::W32::XINPUT_GAMEPAD_DPAD_DOWN:
return REX::PS4::SCE_PAD_BUTTON_DOWN;
case REX::W32::XINPUT_GAMEPAD_DPAD_LEFT:
return REX::PS4::SCE_PAD_BUTTON_LEFT;
case REX::W32::XINPUT_GAMEPAD_DPAD_RIGHT:
return REX::PS4::SCE_PAD_BUTTON_RIGHT;
case REX::W32::XINPUT_GAMEPAD_START:
return REX::PS4::SCE_PAD_BUTTON_OPTIONS;
case REX::W32::XINPUT_GAMEPAD_BACK:
return REX::PS4::SCE_PAD_BUTTON_TOUCH_PAD;
case REX::W32::XINPUT_GAMEPAD_LEFT_THUMB:
return REX::PS4::SCE_PAD_BUTTON_L3;
case REX::W32::XINPUT_GAMEPAD_RIGHT_THUMB:
return REX::PS4::SCE_PAD_BUTTON_R3;
case REX::W32::XINPUT_GAMEPAD_LEFT_SHOULDER:
return REX::PS4::SCE_PAD_BUTTON_L1;
case REX::W32::XINPUT_GAMEPAD_RIGHT_SHOULDER:
return REX::PS4::SCE_PAD_BUTTON_R1;
case REX::W32::XINPUT_GAMEPAD_A:
return REX::PS4::SCE_PAD_BUTTON_CROSS;
case REX::W32::XINPUT_GAMEPAD_B:
return REX::PS4::SCE_PAD_BUTTON_CIRCLE;
case REX::W32::XINPUT_GAMEPAD_X:
return REX::PS4::SCE_PAD_BUTTON_SQUARE;
case REX::W32::XINPUT_GAMEPAD_Y:
return REX::PS4::SCE_PAD_BUTTON_TRIANGLE;
default:
return keyMask;
}
}
std::uint32_t InputMap::ScePadOffsetToXInput(std::uint32_t keyMask)
{
switch (keyMask) {
case REX::PS4::SCE_PAD_BUTTON_UP:
return REX::W32::XINPUT_GAMEPAD_DPAD_UP;
case REX::PS4::SCE_PAD_BUTTON_DOWN:
return REX::W32::XINPUT_GAMEPAD_DPAD_DOWN;
case REX::PS4::SCE_PAD_BUTTON_LEFT:
return REX::W32::XINPUT_GAMEPAD_DPAD_LEFT;
case REX::PS4::SCE_PAD_BUTTON_RIGHT:
return REX::W32::XINPUT_GAMEPAD_DPAD_RIGHT;
case REX::PS4::SCE_PAD_BUTTON_OPTIONS:
return REX::W32::XINPUT_GAMEPAD_START;
case REX::PS4::SCE_PAD_BUTTON_TOUCH_PAD:
return REX::W32::XINPUT_GAMEPAD_BACK;
case REX::PS4::SCE_PAD_BUTTON_L3:
return REX::W32::XINPUT_GAMEPAD_LEFT_THUMB;
case REX::PS4::SCE_PAD_BUTTON_R3:
return REX::W32::XINPUT_GAMEPAD_RIGHT_THUMB;
case REX::PS4::SCE_PAD_BUTTON_L1:
return REX::W32::XINPUT_GAMEPAD_LEFT_SHOULDER;
case REX::PS4::SCE_PAD_BUTTON_R1:
return REX::W32::XINPUT_GAMEPAD_RIGHT_SHOULDER;
case REX::PS4::SCE_PAD_BUTTON_CROSS:
return REX::W32::XINPUT_GAMEPAD_A;
case REX::PS4::SCE_PAD_BUTTON_CIRCLE:
return REX::W32::XINPUT_GAMEPAD_B;
case REX::PS4::SCE_PAD_BUTTON_SQUARE:
return REX::W32::XINPUT_GAMEPAD_X;
case REX::PS4::SCE_PAD_BUTTON_TRIANGLE:
return REX::W32::XINPUT_GAMEPAD_Y;
default:
return keyMask;
}
}
std::uint32_t InputMap::GamepadMaskToKeycode(std::uint32_t keyMask)
{
if (RE::ControlMap::GetSingleton()->pcGamePadMapType == RE::PC_GAMEPAD_TYPE::kOrbis) {
keyMask = ScePadOffsetToXInput(keyMask);
}
switch (keyMask) {
case REX::W32::XINPUT_GAMEPAD_DPAD_UP:
return kGamepadButtonOffset_DPAD_UP;
case REX::W32::XINPUT_GAMEPAD_DPAD_DOWN:
return kGamepadButtonOffset_DPAD_DOWN;
case REX::W32::XINPUT_GAMEPAD_DPAD_LEFT:
return kGamepadButtonOffset_DPAD_LEFT;
case REX::W32::XINPUT_GAMEPAD_DPAD_RIGHT:
return kGamepadButtonOffset_DPAD_RIGHT;
case REX::W32::XINPUT_GAMEPAD_START:
return kGamepadButtonOffset_START;
case REX::W32::XINPUT_GAMEPAD_BACK:
return kGamepadButtonOffset_BACK;
case REX::W32::XINPUT_GAMEPAD_LEFT_THUMB:
return kGamepadButtonOffset_LEFT_THUMB;
case REX::W32::XINPUT_GAMEPAD_RIGHT_THUMB:
return kGamepadButtonOffset_RIGHT_THUMB;
case REX::W32::XINPUT_GAMEPAD_LEFT_SHOULDER:
return kGamepadButtonOffset_LEFT_SHOULDER;
case REX::W32::XINPUT_GAMEPAD_RIGHT_SHOULDER:
return kGamepadButtonOffset_RIGHT_SHOULDER;
case REX::W32::XINPUT_GAMEPAD_A:
return kGamepadButtonOffset_A;
case REX::W32::XINPUT_GAMEPAD_B:
return kGamepadButtonOffset_B;
case REX::W32::XINPUT_GAMEPAD_X:
return kGamepadButtonOffset_X;
case REX::W32::XINPUT_GAMEPAD_Y:
return kGamepadButtonOffset_Y;
case 0x9: // Left Trigger game-defined ID
return kGamepadButtonOffset_LT;
case 0xA: // Right Trigger game-defined ID
return kGamepadButtonOffset_RT;
default:
return kMaxMacros; // Invalid
}
}
std::uint32_t InputMap::GamepadKeycodeToMask(std::uint32_t keyCode)
{
std::uint32_t keyMask;
switch (keyCode) {
case kGamepadButtonOffset_DPAD_UP:
keyMask = REX::W32::XINPUT_GAMEPAD_DPAD_UP;
break;
case kGamepadButtonOffset_DPAD_DOWN:
keyMask = REX::W32::XINPUT_GAMEPAD_DPAD_DOWN;
break;
case kGamepadButtonOffset_DPAD_LEFT:
keyMask = REX::W32::XINPUT_GAMEPAD_DPAD_LEFT;
break;
case kGamepadButtonOffset_DPAD_RIGHT:
keyMask = REX::W32::XINPUT_GAMEPAD_DPAD_RIGHT;
break;
case kGamepadButtonOffset_START:
keyMask = REX::W32::XINPUT_GAMEPAD_START;
break;
case kGamepadButtonOffset_BACK:
keyMask = REX::W32::XINPUT_GAMEPAD_BACK;
break;
case kGamepadButtonOffset_LEFT_THUMB:
keyMask = REX::W32::XINPUT_GAMEPAD_LEFT_THUMB;
break;
case kGamepadButtonOffset_RIGHT_THUMB:
keyMask = REX::W32::XINPUT_GAMEPAD_RIGHT_THUMB;
break;
case kGamepadButtonOffset_LEFT_SHOULDER:
keyMask = REX::W32::XINPUT_GAMEPAD_LEFT_SHOULDER;
break;
case kGamepadButtonOffset_RIGHT_SHOULDER:
keyMask = REX::W32::XINPUT_GAMEPAD_RIGHT_SHOULDER;
break;
case kGamepadButtonOffset_A:
keyMask = REX::W32::XINPUT_GAMEPAD_A;
break;
case kGamepadButtonOffset_B:
keyMask = REX::W32::XINPUT_GAMEPAD_B;
break;
case kGamepadButtonOffset_X:
keyMask = REX::W32::XINPUT_GAMEPAD_X;
break;
case kGamepadButtonOffset_Y:
keyMask = REX::W32::XINPUT_GAMEPAD_Y;
break;
case kGamepadButtonOffset_LT:
keyMask = 0x9; // Left Trigger game-defined ID
break;
case kGamepadButtonOffset_RT:
keyMask = 0xA; // Right Trigger game-defined ID
break;
default:
keyMask = 0xFF; // Invalid
break;
}
if (RE::ControlMap::GetSingleton()->pcGamePadMapType == RE::PC_GAMEPAD_TYPE::kOrbis) {
keyMask = XInputToScePadOffset(keyMask);
}
return keyMask;
}
}
@@ -0,0 +1,133 @@
#include "F4SE/Interfaces.h"
#include "F4SE/API.h"
#include "REX/W32/KERNEL32.h"
namespace F4SE
{
bool MessagingInterface::RegisterListener(EventCallback* a_handler, std::string_view a_sender) const
{
const auto success = GetProxy().RegisterListener(GetPluginHandle(), a_sender.data(), reinterpret_cast<void*>(a_handler));
if (!success) {
REX::ERROR("failed to register listener for {}", a_sender);
}
return success;
}
bool MessagingInterface::Dispatch(std::uint32_t a_messageType, void* a_data, std::uint32_t a_dataLen, const char* a_receiver) const
{
const auto success = GetProxy().Dispatch(GetPluginHandle(), a_messageType, a_data, a_dataLen, a_receiver);
if (!success) {
REX::ERROR("failed to dispatch to {}", (a_receiver ? a_receiver : "all listeners"));
}
return success;
}
bool ScaleformInterface::Register(std::string_view a_name, RegisterCallback* a_callback) const
{
const auto success = GetProxy().Register(a_name.data(), reinterpret_cast<void*>(a_callback));
if (!success) {
REX::ERROR("failed to register {}", a_name);
}
return success;
}
void SerializationInterface::SetUniqueID(std::uint32_t a_uid) const
{
GetProxy().SetUniqueID(GetPluginHandle(), a_uid);
}
void SerializationInterface::SetRevertCallback(EventCallback* a_callback) const
{
GetProxy().SetRevertCallback(GetPluginHandle(), reinterpret_cast<void*>(a_callback));
}
void SerializationInterface::SetSaveCallback(EventCallback* a_callback) const
{
GetProxy().SetSaveCallback(GetPluginHandle(), reinterpret_cast<void*>(a_callback));
}
void SerializationInterface::SetLoadCallback(EventCallback* a_callback) const
{
GetProxy().SetLoadCallback(GetPluginHandle(), reinterpret_cast<void*>(a_callback));
}
void SerializationInterface::SetFormDeleteCallback(FormDeleteCallback* a_callback) const
{
GetProxy().SetFormDeleteCallback(GetPluginHandle(), reinterpret_cast<void*>(a_callback));
}
bool SerializationInterface::WriteRecord(std::uint32_t a_type, std::uint32_t a_version, const void* a_buf, std::uint32_t a_length) const
{
const auto success = GetProxy().WriteRecord(a_type, a_version, a_buf, a_length);
if (!success) {
REX::ERROR("failed to write record");
}
return success;
}
bool SerializationInterface::OpenRecord(std::uint32_t a_type, std::uint32_t a_version) const
{
const auto success = GetProxy().OpenRecord(a_type, a_version);
if (!success) {
REX::ERROR("failed to open record");
}
return success;
}
bool SerializationInterface::WriteRecordData(const void* a_buf, std::uint32_t a_length) const
{
const auto success = GetProxy().WriteRecordData(a_buf, a_length);
if (!success) {
REX::ERROR("failed to write record data");
}
return success;
}
bool SerializationInterface::GetNextRecordInfo(std::uint32_t& a_type, std::uint32_t& a_version, std::uint32_t& a_length) const
{
return GetProxy().GetNextRecordInfo(std::addressof(a_type), std::addressof(a_version), std::addressof(a_length));
}
std::uint32_t SerializationInterface::ReadRecordData(void* a_buf, std::uint32_t a_length) const
{
const auto read = GetProxy().ReadRecordData(a_buf, a_length);
if (read != a_length) {
REX::ERROR("failed to read full record data {}B of {}B", read, a_length);
}
return read;
}
bool PapyrusInterface::Register(RegisterFunctions* a_callback) const
{
const auto success = GetProxy().Register(reinterpret_cast<void*>(a_callback));
if (!success) {
REX::ERROR("failed to register callback");
}
return success;
}
void* TrampolineInterface::AllocateFromBranchPool(std::size_t a_size) const
{
const auto mem = GetProxy().AllocateFromBranchPool(GetPluginHandle(), a_size);
if (!mem) {
REX::ERROR("failed to allocate from branch pool");
}
return mem;
}
void* TrampolineInterface::AllocateFromLocalPool(std::size_t a_size) const
{
const auto mem = GetProxy().AllocateFromLocalPool(GetPluginHandle(), a_size);
if (!mem) {
REX::ERROR("failed to allocate from local pool");
}
return mem;
}
const PluginVersionData* PluginVersionData::GetSingleton() noexcept
{
return reinterpret_cast<const PluginVersionData*>(REX::W32::GetProcAddress(REX::W32::GetCurrentModule(), "F4SEPlugin_Version"));
}
}
+14
View File
@@ -0,0 +1,14 @@
#include "RE/A/Actor.h"
#include "RE/T/TESBoundObject.h"
#include "RE/T/TESNPC.h"
namespace RE
{
TESNPC* Actor::GetNPC() const noexcept
{
const auto objRef = GetObjectReference();
assert(objRef->GetFormType() == ENUM_FORM_ID::kNPC_);
return static_cast<TESNPC*>(objRef);
}
}
@@ -0,0 +1,13 @@
#include "RE/B/BGSCreatedObjectManager.h"
#include "RE/A/AlchemyItem.h"
#include "RE/T/TESForm.h"
namespace RE
{
template class BGSCreatedObjectManager::BSTCreatedObjectSmartPointerPolicy<AlchemyItem>;
static_assert(std::is_empty_v<BGSCreatedObjectManager::BSTCreatedObjectSmartPointerPolicy<AlchemyItem>>);
template class BGSCreatedObjectManager::BSTCreatedObjectSmartPointerPolicy<TESForm>;
static_assert(std::is_empty_v<BGSCreatedObjectManager::BSTCreatedObjectSmartPointerPolicy<TESForm>>);
}
@@ -0,0 +1,13 @@
#include "RE/B/BGSInventoryItem.h"
namespace RE
{
std::uint32_t BGSInventoryItem::GetCount() const noexcept
{
std::uint32_t count = 0;
for (auto iter = stackData.get(); iter; iter = iter->nextStack.get()) {
count += iter->GetCount();
}
return count;
}
}
@@ -0,0 +1,17 @@
#include "RE/B/BGSKeyword.h"
namespace RE
{
namespace detail
{
BGSKeyword* BGSKeywordGetTypedKeywordByIndex(KeywordType a_type, std::uint16_t a_index)
{
return BGSKeyword::GetTypedKeywordByIndex(a_type, a_index);
}
std::uint16_t BGSKeywordGetIndexForTypedKeyword(BGSKeyword* a_keyword, KeywordType a_type)
{
return BGSKeyword::GetIndexForTypedKeyword(a_keyword, a_type);
}
}
}
@@ -0,0 +1,77 @@
#include "RE/B/BGSKeywordForm.h"
namespace RE
{
void BGSKeywordForm::CopyKeywords(const std::vector<BGSKeyword*>& a_copiedData)
{
const auto oldData = keywords;
const auto newSize = a_copiedData.size();
const auto newData = calloc<BGSKeyword*>(newSize);
std::ranges::copy(a_copiedData, newData);
numKeywords = static_cast<std::uint32_t>(newSize);
keywords = newData;
free(oldData);
}
bool BGSKeywordForm::AddKeywords(const std::vector<BGSKeyword*>& a_keywords)
{
std::vector<BGSKeyword*> copiedData{ keywords, keywords + numKeywords };
std::ranges::remove_copy_if(a_keywords, std::back_inserter(copiedData), [&](auto& keyword) {
return std::ranges::find(copiedData, keyword) != copiedData.end();
});
CopyKeywords(copiedData);
return true;
}
bool BGSKeywordForm::ContainsKeywordString(std::string_view a_editorID) const
{
bool result = false;
ForEachKeyword([&](const BGSKeyword* a_keyword) {
if (a_keyword->formEditorID.contains(a_editorID)) {
result = true;
return BSContainer::ForEachResult::kStop;
}
return BSContainer::ForEachResult::kContinue;
});
return result;
}
bool BGSKeywordForm::HasKeywordID(TESFormID a_formID) const
{
bool result = false;
ForEachKeyword([&](const BGSKeyword* a_keyword) {
if (a_keyword->GetFormID() == a_formID) {
result = true;
return BSContainer::ForEachResult::kStop;
}
return BSContainer::ForEachResult::kContinue;
});
return result;
}
bool BGSKeywordForm::HasKeywordString(std::string_view a_editorID) const
{
bool result = false;
ForEachKeyword([&](const BGSKeyword* a_keyword) {
if (a_keyword->formEditorID == a_editorID) {
result = true;
return BSContainer::ForEachResult::kStop;
}
return BSContainer::ForEachResult::kContinue;
});
return result;
}
bool BGSKeywordForm::RemoveKeywords(const std::vector<BGSKeyword*>& a_keywords)
{
std::vector<BGSKeyword*> copiedData{ keywords, keywords + numKeywords };
if (std::erase_if(copiedData, [&](auto& keyword) { return std::ranges::find(a_keywords, keyword) != a_keywords.end(); }) > 0) {
CopyKeywords(copiedData);
return true;
}
return false;
}
}
@@ -0,0 +1,28 @@
#include "RE/B/BGSLocation.h"
namespace RE
{
bool BGSLocation::IsChild(const BGSLocation* a_possibleChild) const
{
if (a_possibleChild) {
for (auto it = a_possibleChild->parentLoc; it; it = it->parentLoc) {
if (this == it) {
return true;
}
}
}
return false;
}
bool BGSLocation::IsParent(const BGSLocation* a_possibleParent) const
{
if (a_possibleParent) {
for (auto it = parentLoc; it; it = it->parentLoc) {
if (a_possibleParent == it) {
return true;
}
}
}
return false;
}
}
@@ -0,0 +1,19 @@
#include "RE/B/BGSObjectInstanceExtra.h"
#include "RE/B/BGSMod.h"
#include "RE/T/TESBoundObject.h"
namespace RE
{
void BGSObjectInstanceExtra::CreateBaseInstanceData(const TESBoundObject& a_object, BSTSmartPointer<TBO_InstanceData>& a_instanceData) const
{
if (values && itemIndex != static_cast<std::uint16_t>(-1)) {
a_object.ApplyMods(a_instanceData, this);
}
}
std::span<BGSMod::ObjectIndexData> BGSObjectInstanceExtra::GetIndexData() const noexcept
{
return values->GetBuffer<BGSMod::ObjectIndexData>(0);
}
}
@@ -0,0 +1,13 @@
#include "RE/B/BSCRC32.h"
namespace RE
{
template struct BSCRC32<std::int8_t>;
template struct BSCRC32<std::uint8_t>;
template struct BSCRC32<std::int16_t>;
template struct BSCRC32<std::uint16_t>;
template struct BSCRC32<std::int32_t>;
template struct BSCRC32<std::uint32_t>;
template struct BSCRC32<std::int64_t>;
template struct BSCRC32<std::uint64_t>;
}
@@ -0,0 +1,24 @@
#include "RE/B/BSFixedString.h"
namespace RE
{
namespace detail
{
template class BSFixedString<char, false>;
static_assert(sizeof(BSFixedString<char, false>) == 0x8);
template class BSFixedString<char, true>;
static_assert(sizeof(BSFixedString<char, true>) == 0x8);
template class BSFixedString<wchar_t, false>;
static_assert(sizeof(BSFixedString<wchar_t, false>) == 0x8);
template class BSFixedString<wchar_t, true>;
static_assert(sizeof(BSFixedString<wchar_t, true>) == 0x8);
}
template struct BSCRC32<BSFixedString>;
template struct BSCRC32<BSFixedStringCS>;
template struct BSCRC32<BSFixedStringW>;
template struct BSCRC32<BSFixedStringWCS>;
}
@@ -0,0 +1,29 @@
#include "RE/B/BSPointerHandle.h"
#include "RE/A/Actor.h"
#include "RE/P/Projectile.h"
#include "RE/T/TESObjectREFR.h"
namespace RE
{
template class BSPointerHandle<Actor>;
static_assert(sizeof(BSPointerHandle<Actor>) == 0x4);
template class BSPointerHandle<Projectile>;
static_assert(sizeof(BSPointerHandle<Projectile>) == 0x4);
template class BSPointerHandle<TESObjectREFR>;
static_assert(sizeof(BSPointerHandle<TESObjectREFR>) == 0x4);
template class BSUntypedPointerHandle<>;
static_assert(sizeof(BSUntypedPointerHandle<>) == 0x4);
template class BSPointerHandleManagerInterface<Actor>;
static_assert(std::is_empty_v<BSPointerHandleManagerInterface<Actor>>);
template class BSPointerHandleManagerInterface<Projectile>;
static_assert(std::is_empty_v<BSPointerHandleManagerInterface<Projectile>>);
template class BSPointerHandleManagerInterface<TESObjectREFR>;
static_assert(std::is_empty_v<BSPointerHandleManagerInterface<TESObjectREFR>>);
}
@@ -0,0 +1,11 @@
#include "RE/B/BSResource.h"
namespace RE::BSResource
{
ErrorCode GetOrCreateStream(const char* a_fileName, BSTSmartPointer<Stream>& a_result, bool a_writable, Location* a_optionalStart)
{
using func_t = decltype(&GetOrCreateStream);
static REL::Relocation<func_t> func{ ID::BSResource::GetOrCreateStream };
return func(a_fileName, a_result, a_writable, a_optionalStart);
}
}
@@ -0,0 +1,58 @@
#include "RE/B/BSResourceNiBinaryStream.h"
namespace RE
{
BSResourceNiBinaryStream::BSResourceNiBinaryStream() :
NiBinaryStream()
{}
BSResourceNiBinaryStream::BSResourceNiBinaryStream(const char* a_file, bool a_writeable, BSResource::Location* a_optionalStart, bool a_fullReadHint) :
NiBinaryStream()
{
using func_t = void (*)(BSResourceNiBinaryStream*, const char*, bool, BSResource::Location*, bool);
static REL::Relocation<func_t> func{ ID::BSResourceNiBinaryStream::Ctor };
func(this, a_file, a_writeable, a_optionalStart, a_fullReadHint);
}
BSResourceNiBinaryStream::~BSResourceNiBinaryStream()
{
using func_t = void (*)(BSResourceNiBinaryStream*);
static REL::Relocation<func_t> func{ ID::BSResourceNiBinaryStream::Dtor };
func(this);
}
void BSResourceNiBinaryStream::Seek(std::ptrdiff_t a_numBytes)
{
using func_t = decltype(&BSResourceNiBinaryStream::Seek);
static REL::Relocation<func_t> func{ ID::BSResourceNiBinaryStream::Seek };
return func(this, a_numBytes);
}
void BSResourceNiBinaryStream::GetBufferInfo(BufferInfo& a_buf)
{
using func_t = decltype(&BSResourceNiBinaryStream::GetBufferInfo);
static REL::Relocation<func_t> func{ ID::BSResourceNiBinaryStream::GetBufferInfo };
return func(this, a_buf);
}
std::size_t BSResourceNiBinaryStream::DoRead(void* a_buf, std::size_t a_toRead)
{
using func_t = decltype(&BSResourceNiBinaryStream::DoRead);
static REL::Relocation<func_t> func{ ID::BSResourceNiBinaryStream::DoRead };
return func(this, a_buf, a_toRead);
}
std::size_t BSResourceNiBinaryStream::DoWrite(const void* a_buf, std::size_t a_toWrite)
{
using func_t = decltype(&BSResourceNiBinaryStream::DoWrite);
static REL::Relocation<func_t> func{ ID::BSResourceNiBinaryStream::DoWrite };
return func(this, a_buf, a_toWrite);
}
[[nodiscard]] BSResourceNiBinaryStream* BSResourceNiBinaryStream::BinaryStreamWithRescan(const char* a_fileName)
{
using func_t = decltype(&BSResourceNiBinaryStream::BinaryStreamWithRescan);
static REL::Relocation<func_t> func{ ID::BSResourceNiBinaryStream::BinaryStreamWithRescan };
return func(a_fileName);
}
}
@@ -0,0 +1,197 @@
#include "RE/B/BSResource_Stream.h"
namespace RE::BSResource
{
Stream::Stream() :
Stream(0, false)
{}
Stream::Stream(std::uint32_t a_totalSize, bool writable) :
StreamBase(a_totalSize, writable)
{}
Stream::Stream(const Stream& a_rhs) :
StreamBase(a_rhs)
{}
Stream::Stream(Stream&& a_rhs) :
StreamBase(std::move(a_rhs))
{}
ErrorCode Stream::DoSetEndOfStream()
{
return ErrorCode::kUnsupported;
}
ErrorCode Stream::DoPrefetchAt([[maybe_unused]] std::uint64_t a_v, [[maybe_unused]] std::uint64_t b_v, [[maybe_unused]] std::uint32_t c_v) const
{
return ErrorCode::kUnsupported;
}
ErrorCode Stream::DoPrefetchAll([[maybe_unused]] std::uint32_t a_v) const
{
return ErrorCode::kUnsupported;
}
ErrorCode Stream::DoStartTaggedPrioritizedRead([[maybe_unused]] void* a_buf,
[[maybe_unused]] std::uint64_t a_v,
[[maybe_unused]] std::uint64_t b_v,
[[maybe_unused]] std::uint32_t c_v,
[[maybe_unused]] std::uint32_t volatile* d_v,
[[maybe_unused]] std::uint32_t& e_v,
[[maybe_unused]] BSEventFlag* event_flag) const
{
return ErrorCode::kUnsupported;
}
bool Stream::DoGetName(BSFixedString& a_dst) const
{
a_dst = "";
return false;
}
ErrorCode Stream::DoCreateAsync(BSTSmartPointer<BSResource::AsyncStream>&) const
{
return ErrorCode::kUnsupported;
}
bool Stream::DoGetIsFromArchive() const
{
return false;
}
ErrorCode Stream::DoWaitTags([[maybe_unused]] volatile std::uint32_t* a_completionTag,
[[maybe_unused]] std::uint32_t a_completionTagWaitValue,
[[maybe_unused]] BSEventFlag* a_eventFlag) const
{
return ErrorCode::kUnsupported;
}
bool Stream::DoQTaggedPrioritizedReadSupported() const
{
return false;
}
ErrorCode Stream::DoCreateOp([[maybe_unused]] BSTSmartPointer<ICacheDriveOp>& p_op, [[maybe_unused]] char const* name) const
{
return ErrorCode::kUnsupported;
}
ErrorCode Stream::DoReadAt([[maybe_unused]] void* a_buffer,
[[maybe_unused]] std::uint64_t a_offset,
[[maybe_unused]] std::uint64_t a_toRead,
[[maybe_unused]] std::uint64_t& a_read) const
{
return ErrorCode::kUnsupported;
}
// interface functions
ErrorCode Stream::Read(void* a_buffer, std::uint64_t a_toRead, std::uint64_t& a_read) const
{
if ((flags & kWritable) != 0) {
return ErrorCode::kUnsupported;
}
return DoRead(a_buffer, a_toRead, a_read);
}
ErrorCode Stream::ReadAt(void* a_buffer, std::uint64_t a_offset, std::uint64_t a_toRead, std::uint64_t& a_read) const
{
if ((flags & kWritable) != 0) {
return ErrorCode::kUnsupported;
}
return DoReadAt(a_buffer, a_offset, a_toRead, a_read);
}
bool Stream::GetName(BSFixedString& a_dst) const
{
return DoGetName(a_dst);
}
ErrorCode Stream::Open(bool buffered, bool readFullHint)
{
uint32_t _flags = kUnk3;
if (buffered) {
_flags |= kBuffered;
}
if (readFullHint) {
_flags |= kBuffered | kFullReadHint;
}
static std::atomic_ref myflags{ flags };
uint32_t old_flags = flags;
while (!myflags.compare_exchange_strong(old_flags, (old_flags | _flags))) {
old_flags = flags;
}
return DoOpen();
}
ErrorCode Stream::Seek(std::uint64_t a_toSeek, SeekMode a_mode, std::uint64_t& a_sought) const
{
return DoSeek(a_toSeek, a_mode, a_sought);
}
ErrorCode Stream::Write(const void* a_buffer, std::uint64_t a_toWrite, std::uint64_t& a_written) const
{
if ((flags & kWritable) != 0) {
return DoWrite(a_buffer, a_toWrite, a_written);
}
return ErrorCode::kUnsupported;
}
ErrorCode Stream::CreateOp(BSTSmartPointer<ICacheDriveOp>& a_opOut, const char* name) const
{
return DoCreateOp(a_opOut, name);
}
ErrorCode Stream::CreateAsync(BSTSmartPointer<AsyncStream>& a_streamOut) const
{
return DoCreateAsync(a_streamOut);
}
ErrorCode Stream::PrefetchAll(std::uint32_t a_v) const
{
if ((flags & kWritable) != 0) {
return ErrorCode::kUnsupported;
}
return DoPrefetchAll(a_v);
}
ErrorCode Stream::PrefetchAt(std::uint64_t a_v, std::uint64_t b_v, std::uint32_t c_v) const
{
if ((flags & kWritable) != 0) {
return ErrorCode::kUnsupported;
}
return DoPrefetchAt(a_v, b_v, c_v);
}
std::uint32_t Stream::QFullReadHint() const
{
return flags & kFullReadHint;
}
bool Stream::QTaggedPrioritizedReadSupported() const
{
return DoQTaggedPrioritizedReadSupported();
}
ErrorCode Stream::StartTaggedPrioritizedRead(void* buf, std::uint64_t a_v, std::uint64_t b_v, std::uint32_t c_V, std::uint32_t volatile* d_v, std::uint32_t& e_v, BSEventFlag* event_flag) const
{
if ((flags & kWritable) != 0) {
return ErrorCode::kUnsupported;
}
return DoStartTaggedPrioritizedRead(buf, a_v, b_v, c_V, d_v, e_v, event_flag);
}
ErrorCode Stream::WaitTags(std::uint32_t volatile* a_v, std::uint32_t b_v, BSEventFlag* event_flag) const
{
return DoWaitTags(a_v, b_v, event_flag);
}
std::uint32_t Stream::QBuffered() const
{
return flags & kBuffered;
}
ErrorCode Stream::SetEndOfStream()
{
return DoSetEndOfStream();
}
}
@@ -0,0 +1,62 @@
#include "RE/B/BSResource_StreamBase.h"
namespace RE
{
namespace BSResource
{
StreamBase::StreamBase() :
totalSize(0),
flags(0)
{}
StreamBase::StreamBase(const StreamBase& a_rhs) :
totalSize(a_rhs.totalSize),
flags(a_rhs.flags & ~kRefCountMask)
{}
StreamBase::StreamBase(StreamBase&& a_rhs) :
totalSize(a_rhs.totalSize),
flags(a_rhs.flags & ~kRefCountMask)
{}
StreamBase::StreamBase(std::uint32_t a_totalSize, bool writable) :
totalSize(a_totalSize),
flags(writable ? 1 : 0)
{}
std::uint64_t StreamBase::DoGetKey() const
{
return 0;
}
ErrorCode StreamBase::DoGetInfo(Info&)
{
return ErrorCode::kUnsupported;
}
std::uint32_t StreamBase::DecRef()
{
REX::TAtomicRef myFlags{ flags };
std::uint32_t expected;
do {
expected = myFlags;
} while (!myFlags.compare_exchange_weak(expected, expected - kRefCountBeg));
return (expected - kRefCountBeg) & kRefCountMask;
}
std::uint32_t StreamBase::IncRef()
{
REX::TAtomicRef myFlags{ flags };
std::uint32_t expected;
do {
expected = myFlags;
} while (!myFlags.compare_exchange_weak(expected, expected + kRefCountBeg));
return (expected - kRefCountBeg) & kRefCountMask;
}
bool StreamBase::IsWritable() const
{
return static_cast<bool>(flags & kWritable);
}
}
}
@@ -0,0 +1,54 @@
#include "RE/B/BSScaleformManager.h"
#include "RE/B/BSSystemFileStreamer.h"
#include "RE/I/IMenu.h"
#include "RE/S/Setting.h"
#include "Scaleform/G/GFx_Loader.h"
#include "Scaleform/G/GFx_MovieDef.h"
#include "Scaleform/P/Ptr.h"
namespace RE
{
bool BSScaleformManager::LoadMovieEx(
IMenu& a_menu,
std::string_view a_filePath,
std::string_view a_menuObjPath,
ScaleModeType a_scaleMode,
float a_backgroundAlpha)
{
static REL::Relocation<SettingT<INISettingCollection>*> fileUncacheOnMenuOpen{ ID::BSScaleformManager::FileUncacheOnMenuOpen };
if (fileUncacheOnMenuOpen && fileUncacheOnMenuOpen->GetBinary()) {
BSSystemFileStreamer::UncacheAll(true);
}
REX::TEnumSet loadConstants{
Scaleform::GFx::Loader::LoadConstants::kKeepBindData,
Scaleform::GFx::Loader::LoadConstants::kWaitFrame1
};
const auto movieDef = Scaleform::Ptr{ loader->CreateMovie(a_filePath.data(), loadConstants.get()) };
if (!movieDef) {
return false;
}
movieDef->Release();
auto& movie = a_menu.uiMovie;
movie.reset(movieDef->CreateInstance(true));
if (!movie) {
return false;
}
movie->Release();
movie->SetViewScaleMode(a_scaleMode);
movie->SetBackgroundAlpha(a_backgroundAlpha);
a_menu.DoAdvanceMovie(0.0);
if (!a_menuObjPath.empty()) {
a_menu.SetMenuCodeObject(*movie, a_menuObjPath);
}
a_menu.RefreshPlatform();
InitMovieViewport(*movie, 1.0F, 1.0F);
a_menu.OnSetSafeRect();
return true;
}
}
@@ -0,0 +1,183 @@
#include "RE/B/BSScript_Array.h"
namespace RE
{
namespace BSScript
{
Array::~Array()
{
}
// Array* Array::ctor(const TypeInfo *type_info, std::uint32_t initial_size)
// {
// using func_t = decltype(&Array::ctor);
// REL::Relocation<func_t> func{ ID::BSScript_Array::ctor };
// return func(this, type_info, initial_size);
// }
[[nodiscard]] auto Array::operator[](size_type a_pos)
-> reference
{
assert(a_pos < size());
return elements[a_pos];
}
[[nodiscard]] auto Array::operator[](size_type a_pos) const
-> const_reference
{
assert(a_pos < size());
return elements[a_pos];
}
[[nodiscard]] auto Array::front()
-> reference
{
return operator[](0);
}
[[nodiscard]] auto Array::front() const
-> const_reference
{
return operator[](0);
}
[[nodiscard]] auto Array::back()
-> reference
{
return operator[](size() - 1);
}
[[nodiscard]] auto Array::back() const
-> const_reference
{
return operator[](size() - 1);
}
[[nodiscard]] auto Array::data() noexcept
-> pointer
{
return size() > 0 ? elements.data() : nullptr;
}
[[nodiscard]] auto Array::data() const noexcept
-> const_pointer
{
return size() > 0 ? elements.data() : nullptr;
}
[[nodiscard]] auto Array::begin() noexcept
-> iterator
{
return elements.begin();
}
[[nodiscard]] auto Array::begin() const noexcept
-> const_iterator
{
return elements.begin();
}
[[nodiscard]] auto Array::cbegin() const noexcept
-> const_iterator
{
return elements.begin();
}
[[nodiscard]] auto Array::end() noexcept
-> iterator
{
return size() > 0 ? elements.end() : nullptr;
}
[[nodiscard]] auto Array::end() const noexcept
-> const_iterator
{
return size() > 0 ? elements.end() : nullptr;
}
[[nodiscard]] auto Array::cend() const noexcept
-> const_iterator
{
return elements.end();
}
[[nodiscard]] auto Array::rbegin() noexcept
-> reverse_iterator
{
return reverse_iterator(end());
}
[[nodiscard]] auto Array::rbegin() const noexcept
-> const_reverse_iterator
{
return const_reverse_iterator(end());
}
[[nodiscard]] auto Array::crbegin() const noexcept
-> const_reverse_iterator
{
return rbegin();
}
[[nodiscard]] auto Array::rend() noexcept
-> reverse_iterator
{
return reverse_iterator(begin());
}
[[nodiscard]] auto Array::rend() const noexcept
-> const_reverse_iterator
{
return const_reverse_iterator(begin());
}
[[nodiscard]] auto Array::crend() const noexcept
-> const_reverse_iterator
{
return rend();
}
[[nodiscard]] bool Array::empty() const noexcept
{
return size() > 0;
}
[[nodiscard]] auto Array::size() const noexcept
-> size_type
{
return elements.size();
}
[[nodiscard]] auto Array::max_size() const noexcept
-> size_type
{
return elements.max_size();
}
[[nodiscard]] TypeInfo& Array::type_info()
{
return elementType;
}
[[nodiscard]] const TypeInfo& Array::type_info() const
{
return elementType;
}
[[nodiscard]] TypeInfo::RawType Array::type() const
{
const REX::TEnumSet typeID = elementType.GetRawType();
switch (*typeID) {
case TypeInfo::RawType::kNone:
case TypeInfo::RawType::kObject:
case TypeInfo::RawType::kString:
case TypeInfo::RawType::kInt:
case TypeInfo::RawType::kFloat:
case TypeInfo::RawType::kBool:
return *(typeID + TypeInfo::RawType::kArrayStart);
default:
return *(typeID + TypeInfo::RawType::kObject);
}
}
}
}
@@ -0,0 +1,18 @@
#include "RE/B/BSScript_Internal_VirtualMachine.h"
#include "RE/G/GameScript.h"
namespace RE
{
namespace BSScript
{
namespace Internal
{
VirtualMachine* VirtualMachine::GetSingleton()
{
auto vm = GameVM::GetSingleton();
return vm ? static_cast<VirtualMachine*>(vm->impl.get()) : nullptr;
}
}
}
}
@@ -0,0 +1,68 @@
#include "RE/B/BSScript_Object.h"
#include "RE/B/BSScript_IObjectHandlePolicy.h"
#include "RE/B/BSScript_Internal_VirtualMachine.h"
namespace RE::BSScript
{
Object::~Object()
{
if (IsConstructed()) {
const std::uint32_t size = type ? type->GetVariableCount() : 0;
for (std::uint32_t i = 0; i < size; ++i) {
variables[i].reset();
}
constructed = false;
initialized = false;
}
auto lock = reinterpret_cast<std::uintptr_t>(lockStructure) & ~static_cast<std::uintptr_t>(1);
if (lock) {
REX::TAtomicRef l{ lock };
--l;
}
}
ObjectTypeInfo* Object::GetTypeInfo()
{
return type.get();
}
const ObjectTypeInfo* Object::GetTypeInfo() const
{
return type.get();
}
void* Object::Resolve(std::uint32_t a_typeID) const
{
auto vm = Internal::VirtualMachine::GetSingleton();
if (!vm) {
return nullptr;
}
IObjectHandlePolicy& policy = vm->GetObjectHandlePolicy();
auto myHandle = GetHandle();
if (policy.HandleIsType(a_typeID, myHandle) && policy.IsHandleObjectAvailable(myHandle)) {
return policy.GetObjectForHandle(a_typeID, myHandle);
} else {
return nullptr;
}
}
Variable* Object::GetProperty(const BSFixedString& a_name)
{
return const_cast<Variable*>(
const_cast<const Object*>(this)->GetProperty(a_name));
}
const Variable* Object::GetProperty(const BSFixedString& a_name) const
{
constexpr auto INVALID = static_cast<std::uint32_t>(-1);
auto idx = INVALID;
for (auto cls = type.get(); cls && idx == INVALID; cls = cls->GetParent()) {
idx = cls->GetPropertyIndex(a_name);
}
return idx != INVALID ? std::addressof(variables[idx]) : nullptr;
}
}
@@ -0,0 +1,187 @@
#include "RE/B/BSScript_ObjectTypeInfo.h"
namespace RE
{
namespace BSScript
{
BSFixedString ObjectTypeInfo::UserFlagInfo::GetUserFlag() const
{
auto sanitizedType = data & ~kSetOnObject;
return *reinterpret_cast<BSFixedString*>(&sanitizedType);
}
auto ObjectTypeInfo::NamedStateInfo::GetFuncIter()
-> Func*
{
return reinterpret_cast<Func*>((std::uintptr_t)this + memberFunctionOffset);
}
auto ObjectTypeInfo::NamedStateInfo::GetFuncIter() const
-> const Func*
{
return reinterpret_cast<const Func*>((std::uintptr_t)this + memberFunctionOffset);
}
ObjectTypeInfo::~ObjectTypeInfo()
{
dtor();
}
const char* ObjectTypeInfo::GetName() const
{
return name.c_str();
}
ObjectTypeInfo* ObjectTypeInfo::GetParent()
{
return parentTypeInfo.get();
}
const ObjectTypeInfo* ObjectTypeInfo::GetParent() const
{
return parentTypeInfo.get();
}
TypeInfo::RawType ObjectTypeInfo::GetRawType() const
{
return TypeInfo::RawType::kObject;
}
auto ObjectTypeInfo::GetUnlinkedFunctionIter()
-> UnlinkedNativeFunction*
{
return reinterpret_cast<UnlinkedNativeFunction*>(data);
}
auto ObjectTypeInfo::GetUnlinkedFunctionIter() const
-> const UnlinkedNativeFunction*
{
return reinterpret_cast<const UnlinkedNativeFunction*>(data);
}
auto ObjectTypeInfo::GetUserFlagIter()
-> UserFlagInfo*
{
return reinterpret_cast<UserFlagInfo*>(data);
}
auto ObjectTypeInfo::GetUserFlagIter() const
-> const UserFlagInfo*
{
return reinterpret_cast<const UserFlagInfo*>(data);
}
std::uint32_t ObjectTypeInfo::GetTotalNumVariables() const
{
auto numVars = GetNumVariables();
for (auto iter = GetParent(); iter; iter = iter->GetParent()) {
numVars += iter->GetNumVariables();
}
return numVars;
}
auto ObjectTypeInfo::GetVariableIter()
-> VariableInfo*
{
return reinterpret_cast<VariableInfo*>(GetUserFlagIter() + GetNumUserFlags());
}
auto ObjectTypeInfo::GetVariableIter() const
-> const VariableInfo*
{
return reinterpret_cast<const VariableInfo*>(GetUserFlagIter() + GetNumUserFlags());
}
auto ObjectTypeInfo::GetInitialValueIter()
-> InitialValueInfo*
{
return reinterpret_cast<InitialValueInfo*>(GetVariableIter() + GetNumVariables());
}
auto ObjectTypeInfo::GetInitialValueIter() const
-> const InitialValueInfo*
{
return reinterpret_cast<const InitialValueInfo*>(GetVariableIter() + GetNumVariables());
}
auto ObjectTypeInfo::GetPropertyIter()
-> PropertyInfo*
{
return reinterpret_cast<PropertyInfo*>(GetInitialValueIter() + GetNumInitalValues());
}
auto ObjectTypeInfo::GetPropertyIter() const
-> const PropertyInfo*
{
return reinterpret_cast<const PropertyInfo*>(GetInitialValueIter() + GetNumInitalValues());
}
auto ObjectTypeInfo::GetGlobalFuncIter()
-> GlobalFuncInfo*
{
return reinterpret_cast<GlobalFuncInfo*>(GetPropertyIter() + GetNumProperties());
}
auto ObjectTypeInfo::GetGlobalFuncIter() const
-> const GlobalFuncInfo*
{
return reinterpret_cast<const GlobalFuncInfo*>(GetPropertyIter() + GetNumProperties());
}
auto ObjectTypeInfo::GetMemberFuncIter()
-> MemberFuncInfo*
{
return reinterpret_cast<MemberFuncInfo*>(GetGlobalFuncIter() + GetNumGlobalFuncs());
}
auto ObjectTypeInfo::GetMemberFuncIter() const
-> const MemberFuncInfo*
{
return reinterpret_cast<const MemberFuncInfo*>(GetGlobalFuncIter() + GetNumGlobalFuncs());
}
auto ObjectTypeInfo::GetNamedStateIter()
-> NamedStateInfo*
{
return reinterpret_cast<NamedStateInfo*>(GetMemberFuncIter() + GetNumMemberFuncs());
}
auto ObjectTypeInfo::GetNamedStateIter() const
-> const NamedStateInfo*
{
return reinterpret_cast<const NamedStateInfo*>(GetMemberFuncIter() + GetNumMemberFuncs());
}
std::uint32_t ObjectTypeInfo::GetPropertyIndex(const BSFixedString& a_name) const
{
const auto props = GetPropertyIter();
if (props) {
for (std::uint32_t i = 0; i < GetNumProperties(); ++i) {
const auto& prop = props[i];
if (prop.name == a_name) {
return prop.info.autoVarIndex;
}
}
}
return static_cast<std::uint32_t>(-1);
}
constexpr bool ObjectTypeInfo::HasPropertyGroups() const
{
return propertyGroups.size();
}
auto ObjectTypeInfo::GetInitialState() const -> const BSFixedString*
{
BSFixedString thing = "hello";
return reinterpret_cast<const BSFixedString*>(GetNamedStateIter() + GetNumNamedStates());
}
void ObjectTypeInfo::dtor()
{
using func_t = decltype(&ObjectTypeInfo::dtor);
REL::Relocation<func_t> func{ ID::BSScript_ObjectTypeInfo::dtor };
return func(this);
}
}
}
@@ -0,0 +1,31 @@
#include "RE/B/BSScript_PackedInstructionStream.h"
namespace RE
{
namespace BSScript
{
namespace ByteCode
{
PackedInstructionStream::PackedInstructionStream() :
numInstructionBits(0),
jumpTargetBitCount(0),
localVariableBitCount(0),
memberVariableBitCount(0),
instructions(nullptr)
{}
PackedInstructionStream::PackedInstructionStream(
void* a_instructions,
std::uint32_t a_numInstrBits,
std::uint16_t a_jumpTargetBitCount,
std::int8_t a_localVariableBitCount,
std::int8_t a_memberVariableBitCount) :
numInstructionBits(a_numInstrBits),
jumpTargetBitCount(a_jumpTargetBitCount),
localVariableBitCount(a_localVariableBitCount),
memberVariableBitCount(a_memberVariableBitCount),
instructions(a_instructions)
{}
}
}
}
@@ -0,0 +1,19 @@
#include "RE/B/BSScript_StackFrame.h"
#include "RE/B/BSScript_Internal_Stack.h"
namespace RE
{
namespace BSScript
{
std::uint32_t StackFrame::GetPageForFrame() const
{
return parent->GetPageForFrame(this);
}
Variable& StackFrame::GetStackFrameVariable(std::uint32_t a_index, std::uint32_t a_pageHint) const
{
return parent->GetStackFrameVariable(this, a_index, a_pageHint);
}
}
}
@@ -0,0 +1,16 @@
#include "RE/B/BSScript_Struct.h"
namespace RE::BSScript
{
Struct::~Struct()
{
if (constructed) {
const std::uint32_t size = type ? type->variables.size() : 0;
for (std::uint32_t i = 0; i < size; ++i) {
variables[i].reset();
}
constructed = false;
}
}
}
@@ -0,0 +1,12 @@
#include "RE/B/BSScript_StructTypeInfo.h"
namespace RE
{
namespace BSScript
{
TypeInfo::RawType StructTypeInfo::GetRawType() const
{
return TypeInfo::RawType::kStruct;
}
}
}
@@ -0,0 +1,42 @@
#include "RE/B/BSScript_TypeInfo.h"
#include "RE/B/BSScript_IComplexType.h"
namespace RE::BSScript
{
auto TypeInfo::GetRawType() const
-> RawType
{
if (IsComplex()) {
const auto complex =
reinterpret_cast<IComplexType*>(
reinterpret_cast<std::uintptr_t>(data.complexTypeInfo) &
~static_cast<std::uintptr_t>(1));
uint32_t rtype = (uint32_t)complex->GetRawType();
if (IsArray()) {
rtype += 10;
}
return (RawType)rtype;
} else {
return *data.rawType;
}
}
IComplexType* TypeInfo::GetComplexType() const
{
return IsComplex() ? reinterpret_cast<IComplexType*>(
reinterpret_cast<std::uintptr_t>(data.complexTypeInfo) &
~static_cast<std::uintptr_t>(1)) :
nullptr;
}
ObjectTypeInfo* TypeInfo::GetObjectTypeInfo() const
{
return IsObject() || IsObjectArray() ? reinterpret_cast<ObjectTypeInfo*>(GetComplexType()) : nullptr;
}
StructTypeInfo* TypeInfo::GetStructTypeInfo() const
{
return IsStruct() || IsStructArray() ? reinterpret_cast<StructTypeInfo*>(GetComplexType()) : nullptr;
}
}
@@ -0,0 +1,139 @@
#include "RE/B/BSScript_Variable.h"
#include "RE/B/BSScript_Array.h"
#include "RE/B/BSScript_Object.h"
#include "RE/B/BSScript_Struct.h"
namespace RE::BSScript
{
Variable& Variable::operator=(BSTSmartPointer<Object> a_object)
{
reset();
if (a_object) {
value.o = std::move(a_object);
varType = value.o->type.get();
} else {
assert(false);
}
assert(is<Object>());
return *this;
}
Variable& Variable::operator=(BSTSmartPointer<Struct> a_struct)
{
reset();
if (a_struct) {
value.t = std::move(a_struct);
varType = value.t->type.get();
} else {
assert(false);
}
assert(is<Struct>());
return *this;
}
Variable& Variable::operator=(BSTSmartPointer<Array> a_array)
{
reset();
if (a_array) {
value.a = std::move(a_array);
varType = value.a->elementType;
varType.SetArray(true);
} else {
assert(false);
}
assert(is<Array>());
return *this;
}
void Variable::reset()
{
switch (varType.GetRawType()) {
case RawType::kObject:
value.o.reset();
break;
case RawType::kString:
value.s.~BSFixedString();
break;
case RawType::kVar:
delete value.v;
break;
case RawType::kStruct:
value.t.reset();
break;
case RawType::kArrayObject:
case RawType::kArrayString:
case RawType::kArrayInt:
case RawType::kArrayFloat:
case RawType::kArrayBool:
case RawType::kArrayVar:
case RawType::kArrayStruct:
value.a.reset();
break;
case RawType::kNone:
case RawType::kInt:
case RawType::kFloat:
case RawType::kBool:
break;
default:
assert(false); // unhandled type
break;
}
varType = RawType::kNone;
value.v = nullptr;
assert(is<std::nullptr_t>());
}
// NOLINTNEXTLINE(misc-no-recursion)
void Variable::copy(const Variable& a_rhs)
{
assert(varType.GetRawType() == RawType::kNone);
assert(value.v == nullptr);
switch (a_rhs.varType.GetRawType()) {
case RawType::kObject:
value.o = a_rhs.value.o;
break;
case RawType::kString:
value.s = a_rhs.value.s;
break;
case RawType::kVar:
if (a_rhs.value.v) {
value.v = new Variable(*a_rhs.value.v);
}
break;
case RawType::kStruct:
value.t = a_rhs.value.t;
break;
case RawType::kArrayObject:
case RawType::kArrayString:
case RawType::kArrayInt:
case RawType::kArrayFloat:
case RawType::kArrayBool:
case RawType::kArrayVar:
case RawType::kArrayStruct:
value.a = a_rhs.value.a;
break;
case RawType::kNone:
break;
case RawType::kInt:
value.u = a_rhs.value.u;
break;
case RawType::kFloat:
value.f = a_rhs.value.f;
break;
case RawType::kBool:
value.b = a_rhs.value.b;
break;
default:
assert(false); // unhandled type
break;
}
varType = a_rhs.varType;
}
}
@@ -0,0 +1,22 @@
#include "RE/B/BSSpinLock.h"
namespace RE
{
template class BSAutoLockReadLockPolicy<BSReadWriteLock>;
static_assert(std::is_empty_v<BSAutoLockReadLockPolicy<BSReadWriteLock>>);
template class BSAutoLockWriteLockPolicy<BSReadWriteLock>;
static_assert(std::is_empty_v<BSAutoLockWriteLockPolicy<BSReadWriteLock>>);
template class BSAutoLockDefaultPolicy<BSSpinLock>;
static_assert(std::is_empty_v<BSAutoLockDefaultPolicy<BSSpinLock>>);
template class BSAutoLock<BSReadWriteLock, BSAutoLockReadLockPolicy>;
static_assert(sizeof(BSAutoLock<BSReadWriteLock, BSAutoLockReadLockPolicy>) == 0x8);
template class BSAutoLock<BSReadWriteLock, BSAutoLockWriteLockPolicy>;
static_assert(sizeof(BSAutoLock<BSReadWriteLock, BSAutoLockWriteLockPolicy>) == 0x8);
template class BSAutoLock<BSSpinLock, BSAutoLockDefaultPolicy>;
static_assert(sizeof(BSAutoLock<BSSpinLock, BSAutoLockDefaultPolicy>) == 0x8);
}
@@ -0,0 +1,16 @@
#include "RE/B/BSSpring_SpringState.h"
namespace RE
{
namespace BSSpring
{
template class SpringState<float>;
static_assert(sizeof(SpringState<float>) == 0xC);
template class SpringState<NiPoint2>;
static_assert(sizeof(SpringState<NiPoint2>) == 0x14);
template class SpringState<NiPoint3>;
static_assert(sizeof(SpringState<NiPoint3>) == 0x1C);
}
}
@@ -0,0 +1,7 @@
#include "RE/B/BSTArray.h"
namespace RE
{
template class BSTAlignedHeapArrayAllocator<0x10>::Allocator;
static_assert(sizeof(BSTAlignedHeapArrayAllocator<0x10>::Allocator) == 0x10);
}
@@ -0,0 +1,16 @@
#include "RE/B/BSTAtomicValue.h"
namespace RE
{
template class BSTAtomicValue<std::int32_t>;
static_assert(sizeof(BSTAtomicValue<std::int32_t>) == 0x4);
template class BSTAtomicValue<std::uint32_t>;
static_assert(sizeof(BSTAtomicValue<std::uint32_t>) == 0x4);
template class BSTAtomicValue<std::int64_t>;
static_assert(sizeof(BSTAtomicValue<std::int64_t>) == 0x8);
template class BSTAtomicValue<std::uint64_t>;
static_assert(sizeof(BSTAtomicValue<std::uint64_t>) == 0x8);
}
@@ -0,0 +1,10 @@
#include "RE/B/BSTDataBuffer.h"
namespace RE
{
template class BSTDataBuffer<1>;
static_assert(sizeof(BSTDataBuffer<1>) == 0x10);
template class BSTDataBuffer<2>;
static_assert(sizeof(BSTDataBuffer<2>) == 0x10);
}
@@ -0,0 +1,7 @@
#include "RE/B/BSTInterpolator.h"
namespace RE
{
template class BSTInterpolator<float, EaseOutInterpolator, GetCurrentPositionFunctor>;
static_assert(sizeof(BSTInterpolator<float, EaseOutInterpolator, GetCurrentPositionFunctor>) == 0x18);
}
@@ -0,0 +1,124 @@
#include "RE/B/BSTPoint.h"
namespace RE
{
template class BSTPoint2<std::int8_t>;
static_assert(sizeof(BSTPoint2<std::int8_t>) == 0x2);
template class BSTPoint2<std::uint8_t>;
static_assert(sizeof(BSTPoint2<std::uint8_t>) == 0x2);
template class BSTPoint2<std::int16_t>;
static_assert(sizeof(BSTPoint2<std::int16_t>) == 0x4);
template class BSTPoint2<std::uint16_t>;
static_assert(sizeof(BSTPoint2<std::uint16_t>) == 0x4);
template class BSTPoint2<std::int32_t>;
static_assert(sizeof(BSTPoint2<std::int32_t>) == 0x8);
template class BSTPoint2<std::uint32_t>;
static_assert(sizeof(BSTPoint2<std::uint32_t>) == 0x8);
template class BSTPoint2<float>;
static_assert(sizeof(BSTPoint2<float>) == 0x8);
template class BSTPoint2<double>;
static_assert(sizeof(BSTPoint2<double>) == 0x10);
template class BSTPoint2Base<std::int8_t>;
static_assert(sizeof(BSTPoint2Base<std::int8_t>) == 0x2);
template class BSTPoint2Base<std::uint8_t>;
static_assert(sizeof(BSTPoint2Base<std::uint8_t>) == 0x2);
template class BSTPoint2Base<std::int16_t>;
static_assert(sizeof(BSTPoint2Base<std::int16_t>) == 0x4);
template class BSTPoint2Base<std::uint16_t>;
static_assert(sizeof(BSTPoint2Base<std::uint16_t>) == 0x4);
template class BSTPoint2Base<std::int32_t>;
static_assert(sizeof(BSTPoint2Base<std::int32_t>) == 0x8);
template class BSTPoint2Base<std::uint32_t>;
static_assert(sizeof(BSTPoint2Base<std::uint32_t>) == 0x8);
template class BSTPoint2Base<float>;
static_assert(sizeof(BSTPoint2Base<float>) == 0x8);
template class BSTPoint2Base<double>;
static_assert(sizeof(BSTPoint2Base<double>) == 0x10);
template class BSTPoint3<std::int8_t>;
static_assert(sizeof(BSTPoint3<std::int8_t>) == 0x3);
template class BSTPoint3<std::uint8_t>;
static_assert(sizeof(BSTPoint3<std::uint8_t>) == 0x3);
template class BSTPoint3<std::int16_t>;
static_assert(sizeof(BSTPoint3<std::int16_t>) == 0x6);
template class BSTPoint3<std::uint16_t>;
static_assert(sizeof(BSTPoint3<std::uint16_t>) == 0x6);
template class BSTPoint3<std::int32_t>;
static_assert(sizeof(BSTPoint3<std::int32_t>) == 0xC);
template class BSTPoint3<std::uint32_t>;
static_assert(sizeof(BSTPoint3<std::uint32_t>) == 0xC);
template class BSTPoint3<float>;
static_assert(sizeof(BSTPoint3<float>) == 0xC);
template class BSTPoint3<double>;
static_assert(sizeof(BSTPoint3<double>) == 0x18);
template class BSTPoint3Base<std::int8_t>;
static_assert(sizeof(BSTPoint3Base<std::int8_t>) == 0x3);
template class BSTPoint3Base<std::uint8_t>;
static_assert(sizeof(BSTPoint3Base<std::uint8_t>) == 0x3);
template class BSTPoint3Base<std::int16_t>;
static_assert(sizeof(BSTPoint3Base<std::int16_t>) == 0x6);
template class BSTPoint3Base<std::uint16_t>;
static_assert(sizeof(BSTPoint3Base<std::uint16_t>) == 0x6);
template class BSTPoint3Base<std::int32_t>;
static_assert(sizeof(BSTPoint3Base<std::int32_t>) == 0xC);
template class BSTPoint3Base<std::uint32_t>;
static_assert(sizeof(BSTPoint3Base<std::uint32_t>) == 0xC);
template class BSTPoint3Base<float>;
static_assert(sizeof(BSTPoint3Base<float>) == 0xC);
template class BSTPoint3Base<double>;
static_assert(sizeof(BSTPoint3Base<double>) == 0x18);
template class BSTPointDefaultOps<std::int8_t>;
static_assert(std::is_empty_v<BSTPointDefaultOps<std::int8_t>>);
template class BSTPointDefaultOps<std::uint8_t>;
static_assert(std::is_empty_v<BSTPointDefaultOps<std::uint8_t>>);
template class BSTPointDefaultOps<std::int16_t>;
static_assert(std::is_empty_v<BSTPointDefaultOps<std::int16_t>>);
template class BSTPointDefaultOps<std::uint16_t>;
static_assert(std::is_empty_v<BSTPointDefaultOps<std::uint16_t>>);
template class BSTPointDefaultOps<std::int32_t>;
static_assert(std::is_empty_v<BSTPointDefaultOps<std::int32_t>>);
template class BSTPointDefaultOps<std::uint32_t>;
static_assert(std::is_empty_v<BSTPointDefaultOps<std::uint32_t>>);
template class BSTPointDefaultOps<float>;
static_assert(std::is_empty_v<BSTPointDefaultOps<float>>);
template class BSTPointDefaultOps<double>;
static_assert(std::is_empty_v<BSTPointDefaultOps<double>>);
}
@@ -0,0 +1,66 @@
#include "RE/B/BSVisit.h"
#include "RE/N/NiAVObject.h"
#include "RE/N/NiNode.h"
namespace RE::BSVisit
{
BSVisitControl TraverseScenegraphGeometries(NiAVObject* a_object, std::function<BSVisitControl(BSGeometry*)> a_func)
{
if (a_object) {
if (auto geom = a_object->IsGeometry())
return a_func(geom);
if (const auto node = a_object->IsNode()) {
for (const auto& child : node->children) {
if (TraverseScenegraphGeometries(child.get(), a_func) == BSVisitControl::kStop) {
return BSVisitControl::kStop;
}
}
}
}
return BSVisitControl::kContinue;
}
BSVisitControl TraverseScenegraphObjects(NiAVObject* a_object, std::function<BSVisitControl(NiAVObject*)> a_func)
{
if (a_object) {
if (a_func(a_object) == BSVisitControl::kStop)
return BSVisitControl::kStop;
if (const auto node = a_object->IsNode()) {
for (const auto& child : node->children) {
if (TraverseScenegraphObjects(child.get(), a_func) == BSVisitControl::kStop) {
return BSVisitControl::kStop;
}
}
}
}
return BSVisitControl::kContinue;
}
BSVisitControl TraverseScenegraphCollision(const NiAVObject* a_object, std::function<BSVisitControl(bhkNPCollisionObject*)> a_func)
{
if (a_object) {
if (const auto collision = a_object->GetCollisionObject()) {
if (const auto collisionNP = collision->IsbhkNPCollisionObject()) {
if (a_func(collisionNP) == BSVisitControl::kStop) {
return BSVisitControl::kStop;
}
}
}
if (const auto node = a_object->IsNode()) {
for (const auto& child : node->children) {
if (TraverseScenegraphCollision(child.get(), a_func) == BSVisitControl::kStop) {
return BSVisitControl::kStop;
}
}
}
}
return BSVisitControl::kContinue;
}
}
@@ -0,0 +1,7 @@
#include "RE/B/ButtonEvent.h"
namespace RE
{
template ButtonEvent* InputEvent::As() noexcept;
template const ButtonEvent* InputEvent::As() const noexcept;
}
@@ -0,0 +1,15 @@
#include "RE/C/Calendar.h"
namespace RE
{
float Calendar::GetHoursPassed() const noexcept
{
const auto days = gameDaysPassed ? gameDaysPassed->GetValue() : 1.0F;
return days * 24.0F;
}
std::uint32_t Calendar::GetMonth() const noexcept
{
return gameMonth ? static_cast<std::uint32_t>(gameMonth->value) : 8;
}
}
@@ -0,0 +1,7 @@
#include "RE/C/CharacterEvent.h"
namespace RE
{
template CharacterEvent* InputEvent::As() noexcept;
template const CharacterEvent* InputEvent::As() const noexcept;
}
@@ -0,0 +1,7 @@
#include "RE/C/CursorMoveEvent.h"
namespace RE
{
template CursorMoveEvent* InputEvent::As() noexcept;
template const CursorMoveEvent* InputEvent::As() const noexcept;
}
@@ -0,0 +1,7 @@
#include "RE/D/DeviceConnectEvent.h"
namespace RE
{
template DeviceConnectEvent* InputEvent::As() noexcept;
template const DeviceConnectEvent* InputEvent::As() const noexcept;
}
@@ -0,0 +1,18 @@
#include "RE/E/ExtraInstanceData.h"
#include "RE/T/TBO_InstanceData.h"
namespace RE
{
ExtraInstanceData::ExtraInstanceData() :
ExtraInstanceData(nullptr, nullptr)
{}
ExtraInstanceData::ExtraInstanceData(const TESBoundObject* a_base, BSTSmartPointer<TBO_InstanceData> a_data) :
BSExtraData(TYPE),
base(a_base),
data(std::move(a_data))
{
REX::EMPLACE_VTABLE(this);
}
}
@@ -0,0 +1 @@
#include "RE/Fallout.h"
@@ -0,0 +1,7 @@
#include "RE/G/GetCurrentPositionFunctor.h"
namespace RE
{
template class GetCurrentPositionFunctor<float>;
static_assert(std::is_empty_v<GetCurrentPositionFunctor<float>>);
}
@@ -0,0 +1,33 @@
#include "RE/H/hkVector4.h"
namespace RE
{
hkVector4f hkVector4f::GetNormalized() const
{
hkVector4f norm = *this;
norm.Normalize();
return norm;
}
float hkVector4f::Length() const
{
return std::sqrt(x * x + y * y + z * z);
}
hkVector4f& hkVector4f::Normalize()
{
float l = Length();
if (l == 0) {
this->x = 0;
this->y = 0;
this->z = 0;
return *this;
}
this->x /= l;
this->y /= l;
this->z /= l;
return *this;
}
}
@@ -0,0 +1,7 @@
#include "RE/I/IDEvent.h"
namespace RE
{
template IDEvent* InputEvent::As<IDEvent>();
template const IDEvent* InputEvent::As<IDEvent>() const;
}
@@ -0,0 +1,9 @@
#include "RE/I/InventoryInterface.h"
namespace RE
{
std::uint32_t BSCRC32<InventoryInterface::Handle>::operator()(InventoryInterface::Handle a_data) const noexcept
{
return BSCRC32<std::uint32_t>()(a_data.id);
}
}
@@ -0,0 +1,7 @@
#include "RE/K/KinectEvent.h"
namespace RE
{
template KinectEvent* InputEvent::As() noexcept;
template const KinectEvent* InputEvent::As() const noexcept;
}
@@ -0,0 +1,24 @@
#include "RE/M/MenuCursor.h"
#include "RE/S/Setting.h"
namespace RE
{
void MenuCursor::ConstrainForPipboy()
{
static REL::Relocation<Setting*> TLX{ ID::MenuCursor::PipboyConstraintTLX };
static REL::Relocation<Setting*> TLY{ ID::MenuCursor::PipboyConstraintTLY };
static REL::Relocation<Setting*> Width{ ID::MenuCursor::PipboyConstraintWidth };
static REL::Relocation<Setting*> Height{ ID::MenuCursor::PipboyConstraintHeight };
SetCursorConstraintsRaw(TLX->GetUInt(), TLY->GetUInt(), Width->GetUInt(), Height->GetUInt());
}
void MenuCursor::ConstrainForPipboyPA()
{
static REL::Relocation<Setting*> TLX{ ID::MenuCursor::PipboyConstraintTLX_PowerArmor };
static REL::Relocation<Setting*> TLY{ ID::MenuCursor::PipboyConstraintTLY_PowerArmor };
static REL::Relocation<Setting*> Width{ ID::MenuCursor::PipboyConstraintWidth_PowerArmor };
static REL::Relocation<Setting*> Height{ ID::MenuCursor::PipboyConstraintHeight_PowerArmor };
SetCursorConstraintsRaw(TLX->GetUInt(), TLY->GetUInt(), Width->GetUInt(), Height->GetUInt());
}
}
@@ -0,0 +1,7 @@
#include "RE/M/MouseMoveEvent.h"
namespace RE
{
template MouseMoveEvent* InputEvent::As() noexcept;
template const MouseMoveEvent* InputEvent::As() const noexcept;
}
@@ -0,0 +1,44 @@
#include "RE/N/NiAVObject.h"
#include "RE/B/BSGeometry.h"
#include "RE/B/BSVisit.h"
#include "RE/N/NiNode.h"
#include "RE/N/NiUpdateData.h"
namespace RE
{
NiAVObject::NiAVObject()
{
REX::EMPLACE_VTABLE(this);
local.MakeIdentity();
world.MakeIdentity();
previousWorld.MakeIdentity();
flags.flags = 0xE;
}
NiAVObject::~NiAVObject() {} // NOLINT(modernize-use-equals-default)
void NiAVObject::CullGeometry(bool a_cull)
{
BSVisit::TraverseScenegraphGeometries(this, [&](BSGeometry* a_geo) -> BSVisitControl {
a_geo->SetAppCulled(a_cull);
return BSVisitControl::kContinue;
});
}
void NiAVObject::CullNode(bool a_cull)
{
BSVisit::TraverseScenegraphObjects(this, [&](NiAVObject* a_object) -> BSVisitControl {
a_object->SetAppCulled(a_cull);
return BSVisitControl::kContinue;
});
}
void NiAVObject::Update(NiUpdateData& a_data)
{
UpdateDownwardPass(a_data, 0);
if (parent && ((a_data.flags & 0x200) == 0)) {
parent->UpdateUpwardPass(a_data);
}
}
}
@@ -0,0 +1,28 @@
#include "RE/N/NiBinaryStream.h"
namespace RE
{
void NiBinaryStream::GetBufferInfo(BufferInfo& a_buf)
{
a_buf.buffer = nullptr;
a_buf.pos = 0;
a_buf.bufferAllocSize = 0;
a_buf.fileSize = 0;
a_buf.bufferReadSize = 0;
a_buf.absCurrentPos = 0;
}
std::size_t NiBinaryStream::binary_read(void* a_buffer, std::size_t a_totalBytes)
{
std::size_t bytesRead = DoRead(a_buffer, a_totalBytes);
absoluteCurrentPos += bytesRead;
return bytesRead;
}
std::size_t NiBinaryStream::binary_write(const void* a_buffer, std::size_t a_totalBytes)
{
std::size_t bytesWritten = DoWrite(a_buffer, a_totalBytes);
absoluteCurrentPos += bytesWritten;
return bytesWritten;
}
}
@@ -0,0 +1,27 @@
#include "RE/N/NiExtraData.h"
namespace RE
{
NiExtraData::NiExtraData()
{
REX::EMPLACE_VTABLE(this);
}
NiExtraData::NiExtraData(const BSFixedString& a_name) :
name(a_name)
{
REX::EMPLACE_VTABLE(this);
}
NiExtraData::~NiExtraData() = default;
const BSFixedString& NiExtraData::GetName() const
{
return name;
}
void NiExtraData::SetName(const BSFixedString& a_name)
{
name = a_name;
}
}
@@ -0,0 +1,58 @@
#include "RE/N/NiExtraDataContainer.h"
#include "RE/N/NiExtraData.h"
namespace RE
{
NiExtraDataContainer::NiExtraDataContainer(const std::uint32_t a_count) :
extra(a_count)
{}
void NiExtraDataContainer::Add(NiExtraData* a_extra)
{
const BSAutoWriteLock l(lock);
extra.push_back(a_extra);
}
NiExtraData* NiExtraDataContainer::FindExtra(const BSFixedString& a_key) const
{
const BSAutoReadLock l(lock);
for (auto& entry : extra)
if (entry->GetName() == a_key)
return entry.get();
return nullptr;
}
std::int32_t NiExtraDataContainer::FindIndex(const BSFixedString& a_key) const
{
const BSAutoReadLock l(lock);
for (auto i = 0u; i < extra.size(); i++)
if (extra[i]->GetName() == a_key)
return i;
return -1;
}
std::uint32_t NiExtraDataContainer::GetSize() const
{
const BSAutoReadLock l(lock);
return extra.size();
}
bool NiExtraDataContainer::RemoveExtra(const BSFixedString& a_key)
{
const BSAutoWriteLock l(lock);
const auto it = std::find_if(extra.begin(), extra.end(), [&](auto& entry) {
return entry->GetName() == a_key;
});
if (it != extra.end()) {
extra.erase(it);
return true;
}
return false;
}
}
@@ -0,0 +1,98 @@
#include "RE/N/NiMatrix3.h"
namespace RE
{
bool NiMatrix3::ToEulerAnglesXYZ(NiPoint3& a_point) const
{
return ToEulerAnglesXYZ(a_point.x, a_point.y, a_point.z);
}
bool NiMatrix3::ToEulerAnglesXYZ(float& a_x, float& a_y, float& a_z) const
{
using func_t = bool (*)(const NiMatrix3*, float&, float&, float&);
static REL::Relocation<func_t> func{ ID::NiMatrix3::ToEulerAnglesXYZ };
return func(this, a_x, a_y, a_z);
}
bool NiMatrix3::ToEulerAnglesXZY(float& a_x, float& a_z, float& a_y) const
{
using func_t = bool (*)(const NiMatrix3*, float&, float&, float&);
static REL::Relocation<func_t> func{ ID::NiMatrix3::ToEulerAnglesXZY };
return func(this, a_x, a_z, a_y);
}
bool NiMatrix3::ToEulerAnglesYXZ(float& a_y, float& a_x, float& a_z) const
{
using func_t = bool (*)(const NiMatrix3*, float&, float&, float&);
static REL::Relocation<func_t> func{ ID::NiMatrix3::ToEulerAnglesYXZ };
return func(this, a_y, a_x, a_z);
}
bool NiMatrix3::ToEulerAnglesYZX(float& a_y, float& a_z, float& a_x) const
{
using func_t = bool (*)(const NiMatrix3*, float&, float&, float&);
static REL::Relocation<func_t> func{ ID::NiMatrix3::ToEulerAnglesYZX };
return func(this, a_y, a_z, a_x);
}
bool NiMatrix3::ToEulerAnglesZYX(float& a_z, float& a_y, float& a_x) const
{
using func_t = bool (*)(const NiMatrix3*, float&, float&, float&);
static REL::Relocation<func_t> func{ ID::NiMatrix3::ToEulerAnglesZYX };
return func(this, a_z, a_y, a_x);
}
bool NiMatrix3::ToEulerAnglesZXY(float& a_z, float& a_x, float& a_y) const
{
using func_t = bool (*)(const NiMatrix3*, float&, float&, float&);
static REL::Relocation<func_t> func{ ID::NiMatrix3::ToEulerAnglesZXY };
return func(this, a_z, a_x, a_y);
}
void NiMatrix3::FromEulerAnglesXYZ(const NiPoint3& a_point)
{
FromEulerAnglesXYZ(a_point.x, a_point.y, a_point.z);
}
void NiMatrix3::FromEulerAnglesXYZ(float a_x, float a_y, float a_z)
{
using func_t = void (*)(NiMatrix3*, float, float, float);
static REL::Relocation<func_t> func{ ID::NiMatrix3::FromEulerAnglesXYZ };
return func(this, a_x, a_y, a_z);
}
void NiMatrix3::FromEulerAnglesXZY(float a_x, float a_z, float a_y)
{
using func_t = void (*)(NiMatrix3*, float, float, float);
static REL::Relocation<func_t> func{ ID::NiMatrix3::FromEulerAnglesXZY };
return func(this, a_x, a_z, a_y);
}
void NiMatrix3::FromEulerAnglesYXZ(float a_y, float a_x, float a_z)
{
using func_t = void (*)(NiMatrix3*, float, float, float);
static REL::Relocation<func_t> func{ ID::NiMatrix3::FromEulerAnglesYXZ };
return func(this, a_y, a_x, a_z);
}
void NiMatrix3::FromEulerAnglesYZX(float a_y, float a_z, float a_x)
{
using func_t = void (*)(NiMatrix3*, float, float, float);
static REL::Relocation<func_t> func{ ID::NiMatrix3::FromEulerAnglesYZX };
return func(this, a_y, a_z, a_x);
}
void NiMatrix3::FromEulerAnglesZYX(float a_z, float a_y, float a_x)
{
using func_t = void (*)(NiMatrix3*, float, float, float);
static REL::Relocation<func_t> func{ ID::NiMatrix3::FromEulerAnglesZYX };
return func(this, a_z, a_y, a_x);
}
void NiMatrix3::FromEulerAnglesZXY(float a_z, float a_x, float a_y)
{
using func_t = void (*)(NiMatrix3*, float, float, float);
static REL::Relocation<func_t> func{ ID::NiMatrix3::FromEulerAnglesZXY };
return func(this, a_z, a_x, a_y);
}
}
@@ -0,0 +1,78 @@
#include "RE/N/NiObjectNET.h"
#include "RE/N/NiExtraData.h"
#include "RE/N/NiRTTI.h"
#include "RE/N/NiTimeController.h"
namespace RE
{
NiObjectNET::NiObjectNET()
{
REX::EMPLACE_VTABLE(this);
}
NiObjectNET::~NiObjectNET() = default;
bool NiObjectNET::AddExtraData(const BSFixedString& a_key, NiExtraData* a_extra)
{
if (a_key.empty())
return false;
if (!a_extra)
return false;
if (!a_extra->GetName().empty())
a_extra->SetName(a_key);
else if (a_key != a_extra->GetName())
return false;
return InsertExtraData(a_extra);
}
bool NiObjectNET::AddExtraData(NiExtraData* a_extra)
{
if (!a_extra)
return false;
const auto& extraName = a_extra->GetName();
if (extraName.empty()) {
// TODO: Game handles nameless ExtraData by using the rtti name and a suffix?
assert(false);
}
return InsertExtraData(a_extra);
}
NiExtraData* NiObjectNET::GetExtraData(const BSFixedString& a_key) const
{
return extra ? extra->FindExtra(a_key) : nullptr;
}
std::uint16_t NiObjectNET::GetExtraDataSize() const
{
return extra ? static_cast<std::uint16_t>(extra->GetSize()) : 0;
}
bool NiObjectNET::HasExtraData(const BSFixedString& a_key) const
{
return extra ? extra->FindExtra(a_key) : false;
}
bool NiObjectNET::InsertExtraData(NiExtraData* a_extra)
{
if (extra) {
extra->Add(a_extra);
return true;
}
return false;
}
bool NiObjectNET::RemoveExtraData(const BSFixedString& a_key)
{
if (a_key.empty())
return false;
return extra && extra->RemoveExtra(a_key);
}
}
@@ -0,0 +1,45 @@
#include "RE/N/NiPoint3.h"
namespace RE
{
float NiPoint3::GetDistance(const NiPoint3& a_point) const noexcept
{
// std math functions are not constexpr yet
return std::sqrtf(GetSquaredDistance(a_point));
}
float NiPoint3::GetZAngleFromVector() const
{
using func_t = decltype(&NiPoint3::GetZAngleFromVector);
static REL::Relocation<func_t> func{ ID::NiPoint3::GetZAngleFromVector };
return func(this);
}
float NiPoint3::Length() const noexcept
{
return std::sqrtf(x * x + y * y + z * z);
}
NiPoint3 NiPoint3::UnitCross(const NiPoint3& a_point) const noexcept
{
auto cross = Cross(a_point);
cross.Unitize();
return cross;
}
float NiPoint3::Unitize() noexcept
{
auto length = Length();
if (length == 1.f) {
return length;
} else if (length > FLT_EPSILON) {
operator/=(length);
} else {
x = 0.0;
y = 0.0;
z = 0.0;
length = 0.0;
}
return length;
}
}
@@ -0,0 +1,13 @@
#include "RE/N/NiRect.h"
namespace RE
{
template class NiRect<float>;
static_assert(sizeof(NiRect<float>) == 0x10);
template class NiRect<std::int32_t>;
static_assert(sizeof(NiRect<std::int32_t>) == 0x10);
template class NiRect<std::uint32_t>;
static_assert(sizeof(NiRect<std::uint32_t>) == 0x10);
}
@@ -0,0 +1,53 @@
#include "RE/P/PlayerCharacter.h"
#include "RE/B/BGSEntryPoint.h"
#include "RE/C/Calendar.h"
#include "RE/S/Setting.h"
namespace RE
{
void PlayerCharacter::LockOutOfTerminal(ObjectRefHandle a_handle)
{
float TerminalLockoutTime{ 0.0f };
if (auto INISettingCollection = RE::INISettingCollection::GetSingleton()) {
if (auto setting = INISettingCollection->GetSetting("iTerminalLockoutTime:Gameplay")) {
TerminalLockoutTime = static_cast<float>(setting->GetInt());
}
}
BGSEntryPoint::HandleEntryPoint(RE::BGSEntryPoint::ENTRY_POINT::kModTerminalLockoutTime, this, &TerminalLockoutTime);
if (auto Calendar = RE::Calendar::GetSingleton()) {
auto TimeScale = Calendar->timeScale ? Calendar->timeScale->GetValue() : 0.0f;
TerminalLockoutTime *= TimeScale * 0.00027799999f;
TerminalLockoutTime += Calendar->GetHoursPassed();
}
lockedTerminals.emplace_back(a_handle, TerminalLockoutTime);
}
bool PlayerCharacter::IsLockedOutOfTerminal(ObjectRefHandle a_handle)
{
float HoursPassed{ 24.0f };
if (auto Calendar = RE::Calendar::GetSingleton()) {
HoursPassed = Calendar->GetHoursPassed();
}
for (std::uint32_t i = 0; i < lockedTerminals.size();) {
auto& iter = lockedTerminals.at(i);
if (HoursPassed <= iter.second) {
i++;
continue;
}
lockedTerminals.erase(&iter);
}
for (auto& iter : lockedTerminals) {
if (iter.first == a_handle) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,10 @@
#include "RE/S/SCRIPT_FUNCTION.h"
namespace RE
{
void SCRIPT_FUNCTION::SetParameters()
{
paramCount = 0;
parameters = nullptr;
}
}
@@ -0,0 +1,28 @@
#include "RE/S/Setting.h"
namespace RE
{
template class SettingT<GameSettingCollection>;
static_assert(sizeof(SettingT<GameSettingCollection>) == 0x18);
template class SettingT<INIPrefSettingCollection>;
static_assert(sizeof(SettingT<INIPrefSettingCollection>) == 0x18);
template class SettingT<INISettingCollection>;
static_assert(sizeof(SettingT<INISettingCollection>) == 0x18);
template class SettingT<LipSynchroSettingCollection>;
static_assert(sizeof(SettingT<LipSynchroSettingCollection>) == 0x18);
template class SettingT<RegSettingCollection>;
static_assert(sizeof(SettingT<RegSettingCollection>) == 0x18);
template class SettingCollection<Setting>;
static_assert(sizeof(SettingCollection<Setting>) == 0x118);
template class SettingCollectionList<Setting>;
static_assert(sizeof(SettingCollectionList<Setting>) == 0x128);
template class SettingCollectionMap<Setting>;
static_assert(sizeof(SettingCollectionMap<Setting>) == 0x138);
}
+92
View File
@@ -0,0 +1,92 @@
#include "RE/T/TES.h"
#include "RE/E/EXTERIOR_DATA.h"
#include "RE/G/GridCellArray.h"
#include "RE/T/TESObjectCELL.h"
#include "RE/T/TESObjectREFR.h"
#include "RE/T/TESWorldSpace.h"
namespace RE
{
void TES::ForEachReference(std::function<BSContainer::ForEachResult(TESObjectREFR* a_ref)> a_callback)
{
if (interiorCell) {
interiorCell->ForEachReference([&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
} else {
if (const auto gridLength = gridCells ? gridCells->dimension : 0; gridLength > 0) {
std::uint32_t x = 0;
do {
std::uint32_t y = 0;
do {
if (const auto cell = gridCells->GetCell(x, y); cell && cell->IsAttached()) {
cell->ForEachReference([&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
}
++y;
} while (y < gridLength);
++x;
} while (x < gridLength);
}
}
if (const auto skyCell = worldSpace ? worldSpace->GetSkyCell() : nullptr; skyCell) {
skyCell->ForEachReference([&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
}
}
void TES::ForEachReferenceInRange(const NiPoint3& a_origin, const float a_radius, std::function<BSContainer::ForEachResult(TESObjectREFR* a_ref)> a_callback)
{
if (a_radius > 0.0f) {
if (interiorCell) {
interiorCell->ForEachReferenceInRange(a_origin, a_radius, [&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
} else {
if (const auto gridLength = gridCells ? gridCells->dimension : 0; gridLength > 0) {
const float yPlus = a_origin.y + a_radius;
const float yMinus = a_origin.y - a_radius;
const float xPlus = a_origin.x + a_radius;
const float xMinus = a_origin.x - a_radius;
std::uint32_t x = 0;
do {
std::uint32_t y = 0;
do {
if (const auto cell = gridCells->GetCell(x, y); cell && cell->IsAttached()) {
if (const auto cellCoords = cell->GetCoordinates(); cellCoords) {
const NiPoint2 worldPos{ cellCoords->worldX, cellCoords->worldY };
if (worldPos.x < xPlus && (worldPos.x + 4096.0f) > xMinus && worldPos.y < yPlus && (worldPos.y + 4096.0f) > yMinus) {
cell->ForEachReferenceInRange(a_origin, a_radius, [&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
}
}
}
++y;
} while (y < gridLength);
++x;
} while (x < gridLength);
}
}
if (const auto skyCell = worldSpace ? worldSpace->GetSkyCell() : nullptr; skyCell) {
skyCell->ForEachReferenceInRange(a_origin, a_radius, [&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
}
} else {
ForEachReference([&](TESObjectREFR* a_ref) {
return a_callback(a_ref);
});
}
}
void TES::ForEachReferenceInRange(const TESObjectREFR* a_ref, const float a_radius, std::function<BSContainer::ForEachResult(TESObjectREFR* a_ref)> a_callback)
{
ForEachReferenceInRange(a_ref->GetPosition(), a_radius, a_callback);
}
}
@@ -0,0 +1,52 @@
#include "RE/T/TESFullName.h"
#include "RE/C/CHANGE_TYPES.h"
#include "RE/T/TESActorBase.h"
#include "RE/T/TESForm.h"
#include "RE/T/TESFormUtil.h"
#include "RE/T/TESObjectCELL.h"
namespace RE
{
std::string_view TESFullName::GetFullName(const TESForm& a_form, bool a_strict)
{
if (const auto fullName = a_form.As<TESFullName>()) {
const auto name = fullName->GetFullName();
return name ? name : ""sv;
} else {
if (a_strict) {
switch (a_form.GetFormType()) {
case ENUM_FORM_ID::kKYWD: // BGSKeyword
case ENUM_FORM_ID::kLCRT: // BGSLocationRefType
case ENUM_FORM_ID::kAACT: // BGSAction
case ENUM_FORM_ID::kLIGH: // TESObjectLIGH
case ENUM_FORM_ID::kSTAT: // TESObjectSTAT
case ENUM_FORM_ID::kSCOL: // BGSStaticCollection
case ENUM_FORM_ID::kMSTT: // BGSMovableStatic
case ENUM_FORM_ID::kFLST: // BGSListForm
break;
default:
return ""sv;
}
}
const auto& map = GetSparseFullNameMap();
const auto it = map.find(std::addressof(a_form));
return it != map.end() ? it->second : ""sv;
}
}
void TESFullName::SetFullName(TESForm& a_form, std::string_view a_fullName)
{
if (const auto full = a_form.As<TESFullName>()) {
full->fullName = a_fullName;
if (const auto actor = a_form.As<TESActorBase>()) {
actor->AddChange(CHANGE_TYPES::kActorBaseFullName);
} else if (const auto cell = a_form.As<TESObjectCELL>()) {
cell->AddChange(CHANGE_TYPES::kCellFullname);
} else {
a_form.AddChange(CHANGE_TYPES::kBaseObjectFullName);
}
}
}
}
@@ -0,0 +1,76 @@
#include "RE/T/TESNPC.h"
#include "RE/P/PlayerCharacter.h"
#include "RE/T/TESRace.h"
namespace RE
{
void TESNPC::CopyPerkRankArray(const std::vector<PerkRankData>& a_copiedData)
{
const auto oldData = perks;
const auto newSize = a_copiedData.size();
const auto newData = calloc<PerkRankData>(newSize);
std::ranges::copy(a_copiedData, newData);
perkCount = static_cast<std::uint32_t>(newSize);
perks = newData;
free(oldData);
}
bool TESNPC::AddPerks(const std::vector<BGSPerk*>& a_perks, std::int8_t a_rank)
{
std::vector<PerkRankData> copiedData{ perks, perks + perkCount };
std::ranges::for_each(a_perks, [&](auto& perk) {
if (!GetPerkIndex(perk)) {
const auto newPerk = new PerkRankData(perk, a_rank);
copiedData.push_back(*newPerk);
}
});
CopyPerkRankArray(copiedData);
return true;
}
bool TESNPC::ContainsKeyword(std::string_view a_editorID) const
{
if (ContainsKeywordString(a_editorID)) {
return true;
}
if (const auto npcRace = GetFormRace(); npcRace && npcRace->ContainsKeywordString(a_editorID)) {
return true;
}
return false;
}
bool TESNPC::HasApplicableKeywordString(std::string_view a_editorID) const
{
if (HasKeywordString(a_editorID)) {
return true;
}
if (const auto npcRace = GetFormRace(); npcRace && npcRace->HasKeywordString(a_editorID)) {
return true;
}
return false;
}
bool TESNPC::RemovePerks(const std::vector<BGSPerk*>& a_perks)
{
std::vector<PerkRankData> copiedData{ perks, perks + perkCount };
if (std::erase_if(copiedData, [&](auto& perkRank) { return std::ranges::find(a_perks, perkRank.perk) != a_perks.end(); }) > 0) {
CopyPerkRankArray(copiedData);
return true;
}
return false;
}
bool TESNPC::UsingAlternateHeadPartList() const
{
if (const auto player = PlayerCharacter::GetSingleton(); IsPlayer() && player) {
const auto& map = GetAlternateHeadPartListMap();
return player->charGenRace && player->charGenRace != formRace && map.contains(player->GetNPC());
} else {
return originalRace && originalRace != formRace;
}
}
}
@@ -0,0 +1,52 @@
#include "RE/T/TESObjectCELL.h"
#include "RE/E/EXTERIOR_DATA.h"
#include "RE/E/ExtraCellWaterType.h"
#include "RE/E/ExtraDataList.h"
#include "RE/T/TESObjectREFR.h"
#include "RE/T/TESWorldSpace.h"
namespace RE
{
void TESObjectCELL::ForEachReference(std::function<BSContainer::ForEachResult(TESObjectREFR*)> a_callback)
{
const BSAutoLock locker(spinLock);
for (const auto& ref : references) {
if (ref && a_callback(ref.get()) == BSContainer::ForEachResult::kStop) {
break;
}
}
}
void TESObjectCELL::ForEachReferenceInRange(const NiPoint3& a_origin, float a_radius, std::function<BSContainer::ForEachResult(TESObjectREFR*)> a_callback)
{
const float squaredRadius = a_radius * a_radius;
ForEachReference([&](TESObjectREFR* a_ref) {
const auto distance = a_origin.GetSquaredDistance(a_ref->GetPosition());
if (distance <= squaredRadius)
return a_callback(a_ref);
return BSContainer::ForEachResult::kContinue;
});
}
EXTERIOR_DATA* TESObjectCELL::GetCoordinates() const
{
return IsExterior() ? cellData.exterior : nullptr;
}
TESWaterForm* TESObjectCELL::GetWaterType() const noexcept
{
const auto xWater = extraList ? extraList->GetByType<ExtraCellWaterType>() : nullptr;
auto water = xWater ? xWater->water : nullptr;
if (!water) {
water = IsExterior() && worldSpace ? worldSpace->GetWaterType() : nullptr;
if (!water) {
static REL::Relocation<TESWaterForm**> defaultWater{ ID::TESObjectCELL::DefaultWater };
water = *defaultWater;
}
}
return water;
}
}
@@ -0,0 +1,15 @@
#include "RE/T/TESValueForm.h"
#include "RE/C/CHANGE_TYPES.h"
#include "RE/T/TESForm.h"
namespace RE
{
void TESValueForm::SetFormValue(TESForm& a_form, std::int32_t a_value)
{
if (const auto val = a_form.As<TESValueForm>()) {
val->value = a_value;
a_form.AddChange(CHANGE_TYPES::kBaseObjectValue);
}
}
}
@@ -0,0 +1,7 @@
#include "RE/T/ThumbstickEvent.h"
namespace RE
{
template ThumbstickEvent* InputEvent::As() noexcept;
template const ThumbstickEvent* InputEvent::As() const noexcept;
}
@@ -0,0 +1,12 @@
#include "RE/W/WorkbenchMenuBase.h"
#include "RE/P/PlayerCharacter.h"
namespace RE
{
WorkbenchMenuBase::InitParams::InitParams()
{
workbenchFurniture.reset();
inventorySource = RE::PlayerCharacter::GetPlayerHandle();
}
}
@@ -0,0 +1,46 @@
#include "Scaleform/G/GFx_Movie.h"
#include "Scaleform/G/GFx_ASMovieRootBase.h"
namespace Scaleform::GFx
{
void Movie::CreateArray(Value* a_value)
{
asMovieRoot->CreateArray(a_value);
}
void Movie::CreateFunction(Value* a_value, FunctionHandler* a_function, void* a_userData)
{
asMovieRoot->CreateFunction(a_value, a_function, a_userData);
}
void Movie::CreateObject(Value* a_value, const char* a_className, const GFx::Value* a_args, std::uint32_t a_numArgs)
{
asMovieRoot->CreateObject(a_value, a_className, a_args, a_numArgs);
}
bool Movie::GetVariable(Value* a_val, const char* a_pathToVar) const
{
return asMovieRoot->GetVariable(a_val, a_pathToVar);
}
bool Movie::SetVariable(const char* a_pathToVar, const Value& a_value, SetVarType a_setType)
{
return asMovieRoot->SetVariable(a_pathToVar, a_value, a_setType);
}
bool Movie::Invoke(const char* a_methodName, Value* a_result, const char* a_argFmt, ...)
{
return asMovieRoot->Invoke(a_methodName, a_result, a_argFmt);
}
bool Movie::Invoke(const char* a_methodName, Value* a_result, const Value* a_args, std::uint32_t a_numArgs)
{
return asMovieRoot->Invoke(a_methodName, a_result, a_args, a_numArgs);
}
bool Movie::InvokeArgs(const char* a_methodName, Value* a_result, const char* a_argFmt, std::va_list a_args)
{
return asMovieRoot->InvokeArgs(a_methodName, a_result, a_argFmt, a_args);
}
}
@@ -0,0 +1 @@
#include "Scaleform/Scaleform.h"