Enforce loading Enderal - Forgotten Stories.ini
This commit is contained in:
parent
7692789c38
commit
587076d219
@ -1,22 +1,162 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace PluginsTxtPatch
|
namespace PluginsTxtPatch
|
||||||
{
|
{
|
||||||
using PluginArray = RE::BSTArray<RE::BSFixedString>;
|
using PluginArray = RE::BSTArray<RE::BSFixedString>;
|
||||||
|
|
||||||
inline void PrependIfMissing(PluginArray* a_array, const char* a_name)
|
// Precached plugin lists (populated in Install)
|
||||||
|
inline std::vector<std::string> cachedPrependPlugins;
|
||||||
|
inline std::vector<std::string> cachedAppendPlugins;
|
||||||
|
|
||||||
|
inline std::vector<std::string> ReadCccFile()
|
||||||
{
|
{
|
||||||
for (const auto& entry : *a_array) {
|
std::vector<std::string> plugins;
|
||||||
if (_stricmp(entry.c_str(), a_name) == 0) {
|
std::ifstream file("Skyrim.ccc");
|
||||||
return;
|
if (!file.is_open()) {
|
||||||
|
return plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
// Trim whitespace
|
||||||
|
while (!line.empty() && (line.back() == '\r' || line.back() == '\n' || line.back() == ' ')) {
|
||||||
|
line.pop_back();
|
||||||
|
}
|
||||||
|
if (!line.empty()) {
|
||||||
|
plugins.push_back(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsInCccList(const std::vector<std::string>& a_cccList, const char* a_name)
|
||||||
|
{
|
||||||
|
if (!a_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const auto& entry : a_cccList) {
|
||||||
|
if (_stricmp(entry.c_str(), a_name) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsInList(PluginArray* a_array, const char* a_name)
|
||||||
|
{
|
||||||
|
if (!a_array || !a_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const auto& entry : *a_array) {
|
||||||
|
if (_stricmp(entry.c_str(), a_name) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void PrependIfMissing(PluginArray* a_array, const char* a_name)
|
||||||
|
{
|
||||||
|
if (!a_array || !a_name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
RE::BSFixedString newEntry(a_name);
|
RE::BSFixedString newEntry(a_name);
|
||||||
|
|
||||||
|
// Check if already at first position
|
||||||
|
if (!a_array->empty() && _stricmp((*a_array)[0].c_str(), a_name) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find and remove from current position if present
|
||||||
|
std::int32_t oldPosition = -1;
|
||||||
|
for (std::uint32_t i = 0; i < a_array->size(); ++i) {
|
||||||
|
if (_stricmp((*a_array)[i].c_str(), a_name) == 0) {
|
||||||
|
oldPosition = static_cast<std::int32_t>(i);
|
||||||
|
a_array->erase(a_array->begin() + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert at first position
|
||||||
a_array->push_back(a_array->empty() ? newEntry : a_array->back());
|
a_array->push_back(a_array->empty() ? newEntry : a_array->back());
|
||||||
for (std::uint32_t i = a_array->size() - 1; i > 0; --i) {
|
for (std::uint32_t i = a_array->size() - 1; i > 0; --i) {
|
||||||
(*a_array)[i] = (*a_array)[i - 1];
|
(*a_array)[i] = (*a_array)[i - 1];
|
||||||
}
|
}
|
||||||
(*a_array)[0] = newEntry;
|
(*a_array)[0] = newEntry;
|
||||||
|
|
||||||
|
if (oldPosition > 0) {
|
||||||
|
logger::info("PluginsTxt: Moved {} from position {} to position 0", a_name, oldPosition);
|
||||||
|
} else if (oldPosition < 0) {
|
||||||
|
logger::info("PluginsTxt: Added {} at position 0", a_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void AddIfMissing(PluginArray* a_array, const char* a_name)
|
||||||
|
{
|
||||||
|
if (!a_array || !a_name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (IsInList(a_array, a_name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
a_array->push_back(RE::BSFixedString(a_name));
|
||||||
|
logger::info("PluginsTxt: Added {} at position {}", a_name, a_array->size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void PrecachePluginLists()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
auto cccList = ReadCccFile();
|
||||||
|
|
||||||
|
// Plugins that must always be in plugins.txt (for INI loading)
|
||||||
|
const char* requiredPlugins[] = {
|
||||||
|
"Enderal - Forgotten Stories.esm",
|
||||||
|
"_ResourcePack.esl"
|
||||||
|
};
|
||||||
|
|
||||||
|
// CC plugins - only add if not in Skyrim.ccc
|
||||||
|
const char* ccPlugins[] = {
|
||||||
|
"ccBGSSSE025-AdvDSGS.esm",
|
||||||
|
"ccBGSSSE037-Curios.esl",
|
||||||
|
"ccQDRSSE001-SurvivalMode.esl",
|
||||||
|
"ccBGSSSE001-Fish.esm"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build prepend list in reverse order so first entry ends up at position 0
|
||||||
|
// First CC plugins (will be at higher positions)
|
||||||
|
for (const auto* plugin : ccPlugins) {
|
||||||
|
if (IsInCccList(cccList, plugin)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (std::filesystem::exists(std::format("Data\\{}", plugin))) {
|
||||||
|
cachedPrependPlugins.push_back(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then required plugins (will be at position 0)
|
||||||
|
for (const auto* plugin : requiredPlugins) {
|
||||||
|
if (std::filesystem::exists(std::format("Data\\{}", plugin))) {
|
||||||
|
cachedPrependPlugins.push_back(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append plugins
|
||||||
|
if (!IsInCccList(cccList, "SkyUI_SE.esp") && std::filesystem::exists("Data\\SkyUI_SE.esp")) {
|
||||||
|
cachedAppendPlugins.push_back("SkyUI_SE.esp");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger::info("PluginsTxt: Precached {} prepend and {} append plugins",
|
||||||
|
cachedPrependPlugins.size(), cachedAppendPlugins.size());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
logger::error("PluginsTxt: Exception during precaching: {}", e.what());
|
||||||
|
} catch (...) {
|
||||||
|
logger::error("PluginsTxt: Unknown exception during precaching");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LoadPluginsList
|
struct LoadPluginsList
|
||||||
@ -28,15 +168,13 @@ namespace PluginsTxtPatch
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* plugins[] = {
|
// Use precached lists (populated in Install)
|
||||||
"SkyUI_SE.esp",
|
for (const auto& plugin : cachedPrependPlugins) {
|
||||||
"Enderal - Forgotten Stories.esm" // Last = highest priority (index 0)
|
PrependIfMissing(a_array, plugin.c_str());
|
||||||
};
|
}
|
||||||
|
|
||||||
for (const auto* plugin : plugins) {
|
for (const auto& plugin : cachedAppendPlugins) {
|
||||||
if (std::filesystem::exists(std::format("Data\\{}", plugin))) {
|
AddIfMissing(a_array, plugin.c_str());
|
||||||
PrependIfMissing(a_array, plugin);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -61,16 +199,26 @@ namespace PluginsTxtPatch
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrecachePluginLists();
|
||||||
|
|
||||||
constexpr std::size_t PROLOG_SIZE = 20;
|
constexpr std::size_t PROLOG_SIZE = 20;
|
||||||
auto target = REL::RelocationID(13650, 13758).address();
|
auto target = REL::RelocationID(13650, 13758).address();
|
||||||
|
|
||||||
|
if (!target) {
|
||||||
|
logger::error("PluginsTxt: Failed to resolve target address");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate trampoline near target (within rel32 range)
|
// Allocate trampoline near target (within rel32 range)
|
||||||
void* mem = nullptr;
|
void* mem = nullptr;
|
||||||
for (std::uintptr_t off = 0x1000; off < 0x7FFF0000 && !mem; off += 0x1000) {
|
for (std::uintptr_t off = 0x1000; off < 0x7FFF0000 && !mem; off += 0x1000) {
|
||||||
mem = VirtualAlloc(reinterpret_cast<void*>(target - off), 64, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
mem = VirtualAlloc(reinterpret_cast<void*>(target - off), 64, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||||
if (!mem) mem = VirtualAlloc(reinterpret_cast<void*>(target + off), 64, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
if (!mem) mem = VirtualAlloc(reinterpret_cast<void*>(target + off), 64, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||||
}
|
}
|
||||||
if (!mem) return;
|
if (!mem) {
|
||||||
|
logger::error("PluginsTxt: Failed to allocate trampoline memory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Trampoline: copy prolog + absolute jump back to original
|
// Trampoline: copy prolog + absolute jump back to original
|
||||||
auto t = static_cast<std::uint8_t*>(mem);
|
auto t = static_cast<std::uint8_t*>(mem);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user