- Author
- Required
- Language
- No translation required
- Resource type
- Utilities / Patch
- Distribution
- Free distribution
Описание
ВАЖНО! Теперь эта версия разделена на 2 версии: Special Edition V2 (1.5.x) и Anniversary Edition V11 (1.6.x). ID, указывающие на адреса, не будут совпадать между этими двумя версиями (исполняемый файл игры слишком отличается, чтобы совпасть, и даже если бы они совпали, код внутри этих функций все равно отличается).Эти библиотеки необходимые для всех современных модов. Потому, после установки SKSE обязательно их устанавливаем, иначе многие моды у вас просто не будут работать.
Для пользователей:
Здесь представлены все базы для поддержки SKSE64. Для игроков они необходимы для поддержки большинства модов.Установка:
1. Необходимо установить - Skyrim Script Extender (SKSE) в зависимости от вашей версии на Skyrim Script Extender (SKSE):- Aniversary Edition build 2.2.6 (game version 1.6.1170)
- GOG Anniversary Edition build 2.2.6 (game version 1.6.1179)
- Special Edition build 2.0.20 (game version 1.5.97)
.bin
должны находиться здесь: Data\SKSE\Plugins\Опции:
- ID and offset pair for 1.6.318.0 - Текстовый файл с парой ID и смещением для версии игры 1.6.318.0
- Header file for DLL plugin author (Special Edition) - Это только для Special Edition (1.5.x)! Главный файл C++, позволяющий загружать и запрашивать базу данных адресов.
- Header file for DLL plugin author (Anniversary Edition) - Это только для Anniversary Edition (1.6.x)! Главный файл C++, позволяющий загружать и запрашивать базу данных адресов.
Остальное вам читать не нужно. Оно для разработчиков.
Для авторов плагинов SKSE DLL:Это материал для мододелов (заголовочный файл). Вы можете загрузить базу данных, которая хранит смещения, чтобы ваш DLL-плагин мог быть независимым от версии без необходимости перекомпиляции. Заголовочный файл можно загрузить из дополнительной секции файлов.
Как использовать
Самый быстрый способ:
Code:
#include "versiondb.h"
void * MyAddress = NULL;
unsigned long long MyOffset = 0;
bool InitializeOffsets()
{
// Allocate on stack so it will be unloaded when we exit this function.
// No need to have the whole database loaded and using up memory for no reason.
VersionDb db;
// Load database with current executable version.
if (!db.Load())
{
_FATALERROR("Failed to load version database for current executable!");
return false;
}
else
{
// "SkyrimSE.exe", "1.5.97.0"
_MESSAGE("Loaded database for %s version %s.", db.GetModuleName().c_str(), db.GetLoadedVersionString().c_str());
}
// This address already includes the base address of module so we can use the address directly.
MyAddress = db.FindAddressById(123);
if (MyAddress == NULL)
{
_FATALERROR("Failed to find address!");
return false;
}
// This offset does not include base address. Actual address would be ModuleBase + MyOffset.
if (!db.FindOffsetById(123, MyOffset))
{
_FATALERROR("Failed to find offset for my thing!");
return false;
}
// Everything was successful.
return true;
}
Code:
#include "versiondb.h"
bool DumpSpecificVersion()
{
VersionDb db;
// Try to load database of version 1.5.62.0 regardless of running executable version.
if (!db.Load(1, 5, 62, 0))
{
_FATALERROR("Failed to load database for 1.5.62.0!");
return false;
}
// Write out a file called offsets-1.5.62.0.txt where each line is the ID and offset.
db.Dump("offsets-1.5.62.0.txt");
_MESSAGE("Dumped offsets for 1.5.62.0");
return true;
}
После этого в главном каталоге Skyrim должен появиться новый файл под названием "offsets-1.5.62.0.txt" или как угодно, что вы указали в качестве имени файла. Он будет в формате, в котором каждая строка:
Decimal ID<tab>Hex Offset<newline>
Например, если у вас есть адрес 142F4DEF8 (статический указатель персонажа игрока) в версии 1.5.62.0, который вы хотите сделать независимым от версии, вы должны сделать следующее:
1. Найдите 2F4DEF8 в файле смещений. Потому что это смещение без базы 140000000
2. Убедитесь, что ID 517014 (десятичный!).
3. Если вы хотите, чтобы этот адрес был в вашей DLL во время выполнения, сделайте следующее:
void* addressOf142F4DEF8 = db.FindAddressById(517014);
Вот и все.
Структура VersionDb имеет следующие функции:
Code:
bool Dump(const std::string& path); // Dump currently loaded database to file
bool Load(int major, int minor, int revision, int build); // Load a specific version if the db-major-minor-revision-build.bin exists in Data/SKSE/Plugins directory
bool Load(); // Load the version for current application
void Clear(); // Clear currently loaded database
void GetLoadedVersion(int& major, int& minor, int& revision, int& build) const; // Get the version of database file we have loaded right now
bool GetExecutableVersion(int& major, int& minor, int& revision, int& build) const; // Get the version of currently executing application
const std::string& GetModuleName() const; // Get the name of currently loaded database module, this should show "SkyrimSE.exe"
const std::string& GetLoadedVersionString() const; // Get the currently loaded version as string, e.g. "1.5.62.0"
const std::map<unsigned long long, unsigned long long>& GetOffsetMap() const; // Get the map of ID to offset if you need to iterate it manually
void* FindAddressById(unsigned long long id) const; // Find address by ID, this will already include base and be correct address. It will return NULL if not found!
bool FindOffsetById(unsigned long long id, unsigned long long& result) const; // Find offset by ID, this will just be offset without base included.
bool FindIdByAddress(void* ptr, unsigned long long& result) const; // Find ID by address, this will attempt a reverse lookup to convert address to ID
bool FindIdByOffset(unsigned long long offset, unsigned long long& result) const; // Find ID by offset, this will attempt a reverse lookup to convert offset to ID
То, что вы должны знать и иметь в виду:
1. Вы можете включить любой (или все) файлы базы данных в ваш плагин, но это может значительно увеличить размер файла (примерно на 2,5 мб). Я бы рекомендовал включать только последнюю версию (которая на момент написания статьи была 1.5.97.0), а если пользователям нужна более старая версия, вы можете указать им на эту страницу и позволить им самим скачать базу данных определенной версии. Хотя у меня нет никаких проблем, если вы решите включить все версии базы данных в свой плагин или даже загрузить их на другие сайты.
2. Вы ВСЕГДА должны загружать базу данных только один раз при запуске, инициализировать/кэшировать нужные вам адреса и позволить ей выгрузиться. Выгрузка означает, что структура VersionDb будет удалена или потеряна (если вы выделили ее в стеке). Это позволит вам не использовать ненужный объем памяти во время работы игры. Нет необходимости держать базу данных загруженной во время игры.
3. База данных содержит адреса функций, глобальных переменных, RTTI, vtables и всего остального, на что может быть ссылка. Она не содержит адресов, которые находятся в середине функций или середине глобальных переменных. Если вам нужен адрес в середине функции, вы должны найти базовый адрес функции и добавить дополнительное смещение самостоятельно.
4. Вы всегда должны проверять результат, чтобы убедиться, что база данных загрузилась успешно (bool Load returned true) и что запрашиваемые адреса действительно вернули действительный результат (не NULL). Если загрузка не удалась, это означает, что, скорее всего, отсутствует файл. Если запрос не выполняется, это означает, что адрес не может быть найден в данной версии. Это может означать, что либо код игры изменился настолько, что адрес больше не действителен для этой версии, либо сама база данных не смогла определить правильный адрес. Если произошло одно из этих событий, вам следует отменить инициализацию плагина, чтобы сообщить SKSE о неправильной загрузке. Или вручную показать сообщение об ошибке.
5. Также будет лучше, если вы проверите, существует ли адрес во всех версиях игры, прежде чем публиковать свой DLL-плагин. Для этого загрузите файл базы данных каждой версии и запросите один и тот же ID адреса в каждой из них, чтобы убедиться, что он существует:
Code:
bool LoadAll(std::vector<VersionDb*>& all)
{
static int versions[] = { 3, 16, 23, 39, 50, 53, 62, 73, 80, 97, -1 };
for (int i = 0; versions[i] >= 0; i++)
{
VersionDb * db = new VersionDb();
if (!db->Load(1, 5, versions[i], 0))
{
delete db;
return false;
}
all.push_back(db);
}
return true;
}
bool ExistsInAll(std::vector<VersionDb*>& all, unsigned long long id)
{
unsigned long long result = 0;
for (auto db : all)
{
if (!db->FindOffsetById(id, result))
return false;
}
return true;
}
void FreeAll(std::vector<VersionDb*>& all)
{
for (auto db : all)
delete db;
all.clear();
}
bool IsOk()
{
std::vector<VersionDb*> all;
if (!LoadAll(all))
{
_FATALERROR("Failed to load one or more version databases for current executable!");
FreeAll(all);
return false;
}
if (!ExistsInAll(all, 517014))
{
_FATALERROR("517014 does not exist in all versions of the database!");
FreeAll(all);
return false;
}
FreeAll(all);
// Ok!
return true;
}
6. Иногда вам нужно сделать что-то другое в зависимости от версии игры. Вы можете сделать это с помощью этого фрагмента кода:
Code:
int major = 0, minor = 0, revision = 0, build = 0;
if (!db.GetExecutableVersion(major, minor, revision, build))
{
_FATALERROR("Something went wrong!");
return false;
}
// Running game is 1.5.x and at least version 1.5.39.0
if (major == 1 && minor == 5 && revision >= 39)
{
// Stuff ... ?
}