Enforce loading Enderal - Forgotten Stories.ini
This commit is contained in:
parent
7692789c38
commit
587076d219
@ -1,22 +1,162 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace PluginsTxtPatch
|
||||
{
|
||||
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) {
|
||||
if (_stricmp(entry.c_str(), a_name) == 0) {
|
||||
return;
|
||||
std::vector<std::string> plugins;
|
||||
std::ifstream file("Skyrim.ccc");
|
||||
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);
|
||||
|
||||
// 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());
|
||||
for (std::uint32_t i = a_array->size() - 1; i > 0; --i) {
|
||||
(*a_array)[i] = (*a_array)[i - 1];
|
||||
}
|
||||
(*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
|
||||
@ -28,15 +168,13 @@ namespace PluginsTxtPatch
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* plugins[] = {
|
||||
"SkyUI_SE.esp",
|
||||
"Enderal - Forgotten Stories.esm" // Last = highest priority (index 0)
|
||||
};
|
||||
// Use precached lists (populated in Install)
|
||||
for (const auto& plugin : cachedPrependPlugins) {
|
||||
PrependIfMissing(a_array, plugin.c_str());
|
||||
}
|
||||
|
||||
for (const auto* plugin : plugins) {
|
||||
if (std::filesystem::exists(std::format("Data\\{}", plugin))) {
|
||||
PrependIfMissing(a_array, plugin);
|
||||
}
|
||||
for (const auto& plugin : cachedAppendPlugins) {
|
||||
AddIfMissing(a_array, plugin.c_str());
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -61,16 +199,26 @@ namespace PluginsTxtPatch
|
||||
return;
|
||||
}
|
||||
|
||||
PrecachePluginLists();
|
||||
|
||||
constexpr std::size_t PROLOG_SIZE = 20;
|
||||
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)
|
||||
void* mem = nullptr;
|
||||
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);
|
||||
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
|
||||
auto t = static_cast<std::uint8_t*>(mem);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user