#pragma once #include "CheckInvalidForms.h" #include #include inline const SKSE::LoadInterface* GetLoadInterface(const SKSE::LoadInterface* loadInterface = nullptr) { const static SKSE::LoadInterface* singleton; if (loadInterface) { singleton = loadInterface; } return singleton; } inline uint8_t NewGameCount(bool increment = false) { static uint8_t g_NewGameStarted = 0; if (increment) { g_NewGameStarted++; } return g_NewGameStarted; } inline void CheckIncompatibleMods() { bool bPrinted = false; if (std::filesystem::exists("Data\\SKSE\\Plugins\\EnderalVersion.ini")) { CSimpleIniA ini; ini.SetUnicode(false); ini.SetMultiKey(false); ini.LoadFile("Data/SKSE/Plugins/EnderalVersion.ini"); const char* version = ini.GetValue("", "version", "2.0.x"); std::regex version_expr("^[\\d\\.]+$"); if (std::regex_match(version, version_expr)) { RE::ConsoleLog::GetSingleton()->Print(std::format("Loaded SureAI's Enderal: Forgotten Stories | Special Edition v{} by Eddoursul and contributors", version).c_str()); bPrinted = true; } } if (!bPrinted) { RE::ConsoleLog::GetSingleton()->Print("Loaded SureAI's Enderal: Forgotten Stories | Special Edition v2.0.x by Eddoursul and contributors"); } CheckWorldspaces(); CheckUnconvertedMap(); CheckSkyrimCells(); CheckEnderalCells(); CheckEnderalContainers(); CheckEnderalNPCs(); CheckEnderalActivators(); CheckEnderalStatics(); } inline bool PapyrusGlobalFunctionExists(const char* scriptName, const char* funcName) { RE::BSTSmartPointer typeInfoPtr; RE::BSScript::Internal::VirtualMachine::GetSingleton()->GetScriptObjectType(RE::BSFixedString(scriptName), typeInfoPtr); auto functionCount = typeInfoPtr->GetNumGlobalFuncs(); auto functions = typeInfoPtr->GetGlobalFuncIter(); for (uint32_t i = 0; i < functionCount; i++) { if (functions[i].func->GetName().c_str() == funcName) { return true; } } return false; } inline void CheckScriptVersions() { class ScriptVersionCallback : public RE::BSScript::IStackCallbackFunctor { private: std::uint32_t expectedVersion; const RE::BSFixedString scriptName; const RE::BSFixedString funcName = "_GetScriptVersion"; public: ScriptVersionCallback(const RE::BSFixedString a_scriptName, int a_version) : scriptName(a_scriptName), expectedVersion(a_version) { if (PapyrusGlobalFunctionExists(scriptName.c_str(), funcName.c_str())) { const auto vm = RE::BSScript::Internal::VirtualMachine::GetSingleton(); auto callbackPtr = RE::BSTSmartPointer(this); vm->DispatchStaticCall(scriptName, funcName, RE::MakeFunctionArguments(), callbackPtr); } else { auto a_script = scriptName; auto a_version = expectedVersion; SKSE::GetTaskInterface()->AddTask([a_script, a_version]() { RE::DebugMessageBox(std::format("Script {} is overwritten by a mod, incompatible with current version of Enderal. Expected version: {}, installed version: N/A.", a_script.c_str(), a_version).c_str()); }); } } void operator()(RE::BSScript::Variable a_result) { if (a_result.GetUInt() != expectedVersion) { RE::DebugMessageBox(std::format("Script {} is overwritten by a mod, incompatible with current version of Enderal. Expected version: {}, installed version: {}.", scriptName.c_str(), expectedVersion, a_result.GetUInt()).c_str()); } } bool CanSave() { return false; } void SetObject(const RE::BSTSmartPointer&) {} }; RE::BSTSmartPointer{ new ScriptVersionCallback("_00E_PlayerSetUpScript", 1) }; RE::BSTSmartPointer{ new ScriptVersionCallback("_00E_QuestFunctions", 1) }; RE::BSTSmartPointer{ new ScriptVersionCallback("_00E_Game_SkillmenuSC", 1) }; RE::BSTSmartPointer{ new ScriptVersionCallback("_00E_Theriantrophist_AlchemyControl", 1) }; } inline void LoadINI(std::map* settings, const char* iniPath) { for (auto it = settings->begin(); it != settings->end(); it++) { logger::info("[DEFAULT] {} = {}", it->first, it->second); } if (!std::filesystem::exists(iniPath)) { logger::warn("{} does not exist, using default values.", iniPath); return; } try { CSimpleIniA ini; ini.SetUnicode(false); ini.SetMultiKey(false); ini.LoadFile(iniPath); std::list keysList; ini.GetAllKeys("", keysList); bool bUpdateINI = false; for (auto it = settings->begin(); it != settings->end(); it++) { bool bExists = false; for (const auto& k : keysList) { if (it->first == k.pItem) { settings->insert_or_assign(k.pItem, ini.GetBoolValue("", k.pItem, settings->at(k.pItem))); logger::info("[INI] {} = {}", k.pItem, settings->at(k.pItem)); bExists = true; break; } } if (!bExists) { ini.SetBoolValue("", it->first.c_str(), it->second); bUpdateINI = true; } } if (bUpdateINI) { logger::info("New settings detected, adding to EnderalSE.ini"); ini.SaveFile(iniPath); } } catch (const std::exception& e) { logger::error("{}", e.what()); } } // When opening from dialogue menu, a_bool must be false to not break framerate of the UI inline void OpenJournal(bool a_bool) { using func_t = decltype(&OpenJournal); REL::Relocation func{ REL::RelocationID(52428, 53327) }; return func(a_bool); } inline void CloseTweenMenu() { if (RE::UI::GetSingleton()->IsMenuOpen(RE::TweenMenu::MENU_NAME)) { using func_t = decltype(&CloseTweenMenu); REL::Relocation func{ REL::RelocationID(51839, 52711) }; return func(); } }