鼓掌之间 发表于 2024-11-14 09:24

SKSE 插件的地址库

本帖最后由 鼓掌之间 于 2024-11-14 09:26 编辑

转自:https://www.nexusmods.com/skyrimspecialedition/mods/32444

包含头文件和数据库,便于 SKSE DLL 插件的版本独立性。

重要!现在已分为两个版本:特别版 (1.5.x) 和周年纪念版 (1.6.x)。这两个版本之间的地址 ID 不匹配(游戏的可执行文件差别太大,即使地址相同,函数中的代码也不同)。

描述

对于普通 mod 用户:请从文件部分下载并安装“all-in-one”包。您可以使用 mod 管理器或手动安装。.bin 文件应放在以下位置: Data/SKSE/Plugins/ 无需阅读其他内容。

对于 SKSE DLL 插件作者: 这是一个 mod 资源文件(头文件)。您可以加载一个存储偏移的数据库,以便您的 DLL 插件可以独立于版本而无需重新编译。头文件可从文件的可选部分下载。对于周年纪念版,头文件称为 versionlibdb.h,而不是 versiondb.h!如果您使用 CommonLib,则所有内容已集成,无需在此处下载任何内容。

如何使用

最快的方法:

#include "versiondb.h"

void * MyAddress = NULL;
unsigned long long MyOffset = 0;

bool InitializeOffsets()
{
    // 在栈上分配,这样在退出此函数时它会被卸载。
    // 无需加载整个数据库以节省内存。
    VersionDb db;

    // 加载当前可执行文件版本的数据库。
    if (!db.Load())
    {
      _FATALERROR("无法加载当前可执行文件的版本数据库!");
      return false;
    }
    else
    {
      _MESSAGE("已加载 %s 版本的数据库 %s。", db.GetModuleName().c_str(), db.GetLoadedVersionString().c_str());
    }

    // 该地址已经包含模块的基地址,因此可以直接使用。
    MyAddress = db.FindAddressById(123);
    if (MyAddress == NULL)
    {
      _FATALERROR("找不到地址!");
      return false;
    }

    // 此偏移量不包含基地址,实际地址应为 ModuleBase + MyOffset。
    if (!db.FindOffsetById(123, MyOffset))
    {
      _FATALERROR("无法找到项目的偏移量!");
      return false;
    }

    // 成功。
    return true;
}


你可能会问那个“123”是什么值。它是地址的 ID。不同版本数据库会为同一地址使用相同的 ID,但指向不同的值。要获取特定版本的所有 ID 和值的列表,可以执行以下操作:

#include "versiondb.h"

bool DumpSpecificVersion()
{
    VersionDb db;

    // 尝试加载版本 1.5.62.0 的数据库,不论当前运行的可执行版本。
    if (!db.Load(1, 5, 62, 0))
    {
      _FATALERROR("无法加载版本 1.5.62.0 的数据库!");
      return false;
    }

    // 输出名为 offsets-1.5.62.0.txt 的文件,每行是 ID 和偏移量。
    db.Dump("offsets-1.5.62.0.txt");
    _MESSAGE("已导出版本 1.5.62.0 的偏移量");
    return true;
}

将 1, 5, 62, 0 替换为你要调试的版本。确保在 /Data/SKSE/Plugins 目录中有相应的数据库文件。
执行后,Skyrim 目录下应生成一个文件,如“offsets-1.5.62.0.txt”,其中每行为:十进制 ID <tab> 十六进制偏移量 <换行>。
例如,在 1.5.62.0 版本中有一个地址 142F4DEF8(玩家角色静态指针)要使其独立于版本,可以这样操作:

[*]在偏移文件中查找 2F4DEF8,因为它是去掉基地址 140000000 的偏移。
[*]确认 ID 是 517014(十进制!)
[*]如果想在运行时在 DLL 中使用此地址,执行以下操作:


void* addressOf142F4DEF8 = db.FindAddressById(517014);


VersionDb 结构体具有以下功能:显示内容:
要注意的事项:

1、可以将数据库文件与插件一起包含,但可能会显著增加文件大小(约 2.5 MB)。通常建议将此 mod 标记为依赖项。
2、在启动时只加载数据库一次,初始化或缓存所需地址并卸载它即可。卸载只是将 VersionDb 结构体删除或丢弃(如果在栈上分配)。这确保在游戏运行期间不会使用不必要的内存。使用 CommonLib 时,这种情况只会发生一次,而不是每个 DLL 都要加载一次。
3、数据库包含函数、全局变量、RTTI、虚表等的地址,但不包含函数或全局变量中间的地址。如果需要函数中间的地址,应查找函数基地址并自行添加偏移量。它也不包含无用信息,如对齐数据或 rdata 中的编译器生成的 SEH 信息。
4、确保数据库成功加载(Load 返回 true)且查询到的地址有效(非 NULL)。如果加载失败,可能是文件丢失或版本不匹配(例如 SE 头文件用于 AE)。如果查询失败,可能是该版本的地址无效或数据库未能检测到正确地址。若发生这些情况,插件初始化应失败,让 SKSE 知道未正确加载,或手动显示错误信息。
5、在发布 DLL 插件前,确保所有游戏版本中该地址都存在。为此,加载每个版本的数据库文件并在各版本中查询相同的地址 ID。


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 >= 0; i++)
    {
      VersionDb * db = new VersionDb();
      if (!db->Load(1, 5, versions, 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("无法加载一个或多个版本的数据库!");
      FreeAll(all);
      return false;
    }

    if (!ExistsInAll(all, 517014))
    {
      _FATALERROR("517014 在所有版本的数据库中都不存在!");
      FreeAll(all);
      return false;
    }

    FreeAll(all);
    return true;
}


这样可以确保你的 DLL mod 在所有版本中都能正常工作,或者在某些版本中不工作时可以在 mod 页中说明。

6、有时需要根据运行的游戏版本做不同的操作,可使用以下代码:


int major = 0, minor = 0, revision = 0, build = 0;
if (!db.GetExecutableVersion(major, minor, revision, build))
{
    _FATALERROR("出现问题!");
    return false;
}

// 运行的游戏版本为 1.5.x,且至少是 1.5.39.0 版本
if (major == 1 && minor == 5 && revision >= 39)
{
    // 执行操作
}



7、请注意:若在调试模式下编译 SKSE DLL,加载数据库的时间可能长达 14 秒!在发布模式下大约为 0.2 秒,这是由于标准库容器在调试模式下速度较慢(如 std::map)。


下载地址:

主文件

**** Hidden Message *****

bj1000y 发表于 2024-11-15 16:00

给楼主点赞,希望继续分享!这么好的东西!感谢楼主分享!感谢论坛!

无耻小贱人 发表于 2024-11-28 19:34

6666666666

caoxuxin1 发表于 2024-11-28 20:00


感谢分享


会爬树的企鹅 发表于 2024-12-2 01:40

66666666666666666

darkdark911 发表于 2024-12-2 21:49

先谢楼主分享

xiangweilong 发表于 2024-12-5 11:39

感觉你发给火箭能否给v就发给火箭股份

piaoenbin 发表于 2024-12-6 06:09

xiexiefenx

林中漫步RRR 发表于 2024-12-8 02:09

好好好

待业少年 发表于 2024-12-8 14:01

让我看看吧

渣闪闪 发表于 2024-12-18 18:13

好人一生平安

878123ok 发表于 2024-12-18 20:40

111111111111111

2091474104 发表于 2024-12-21 00:42

666666666666666

笙绳昇剩 发表于 2024-12-22 17:15

绿色安装的怎么看地址呀

索拉旺姆 发表于 2024-12-23 10:41

~~~~~~~~~~~~~~~~

bayunxiaohong 发表于 2024-12-23 22:01

66666666666666666

liuyibo0103 发表于 2024-12-26 23:25

66666666666666666

岚逻、 发表于 2024-12-27 09:12

6666666666666

l1097025883 发表于 2024-12-27 19:08

666666666666666666666666666

非洲人民 发表于 2024-12-30 13:51

6666666666666

忘了有点久 发表于 2024-12-30 14:31

2023202320232023202320232023

p3869037 发表于 2024-12-31 20:56

666666666666

qy8722256 发表于 2025-1-2 15:51

感谢分享

秦夏 发表于 2025-1-4 10:23

自行车v支持v自行车v自行车

zhang0gy 发表于 2025-1-4 17:50

666666666666666666666666666666666666666666

a2840939373 发表于 2025-1-4 20:02

感谢分享

忠滑人民 发表于 2025-1-5 15:58

试试看

3dm_24416184 发表于 2025-1-5 17:53

666666666

无双组合糖葫芦 发表于 2025-1-5 20:36

谢谢大佬的文件

JT021 发表于 2025-1-12 23:20

22222222222222
页: [1] 2 3
查看完整版本: SKSE 插件的地址库