66 using size_type =
typename container_type::size_type;
70 template <
class ExecutionPolicy>
72 requires(std::is_execution_policy_v<std::decay_t<ExecutionPolicy>>)
77 std::sort(a_policy, _offset2id.begin(), _offset2id.end(), [](
auto&& a_lhs,
auto&& a_rhs) {
78 return a_lhs.offset < a_rhs.offset;
86 [[nodiscard]] std::uint64_t
operator()(std::size_t a_offset)
const
88 const mapping_t elem{ 0, a_offset };
89 const auto it = std::lower_bound(
93 [](
auto&& a_lhs,
auto&& a_rhs) {
94 return a_lhs.offset < a_rhs.offset;
96 if (it == _offset2id.end()) {
99 "Failed to find the offset within the database: 0x{:08X}"sv,
130 [[nodiscard]]
inline std::size_t
id2offset(std::uint64_t a_id)
const
132 mapping_t elem{ a_id, 0 };
133 const auto it = std::lower_bound(
137 [](
auto&& a_lhs,
auto&& a_rhs) {
138 return a_lhs.id < a_rhs.id;
140 if (it == _id2offset.end()) {
143 "Failed to find the id within the address library: {}\n"
144 "This means this script extender plugin is incompatible with the address "
145 "library for this version of the game, and thus does not support it."sv,
149 return static_cast<std::size_t
>(it->offset);
158 void read(binary_io::file_istream& a_in)
160 const auto [format] = a_in.read<std::int32_t>();
161#ifdef SKYRIM_SUPPORT_AE
168 "Unsupported address library format: {}\n"
169 "This means this script extender plugin is incompatible with the address "
170 "library available for this version of the game, and thus does not "
175 const auto [major, minor, patch, revision] =
176 a_in.read<std::int32_t, std::int32_t, std::int32_t, std::int32_t>();
177 _version[0] =
static_cast<std::uint16_t
>(major);
178 _version[1] =
static_cast<std::uint16_t
>(minor);
179 _version[2] =
static_cast<std::uint16_t
>(patch);
180 _version[3] =
static_cast<std::uint16_t
>(revision);
182 const auto [nameLen] = a_in.read<std::int32_t>();
183 a_in.seek_relative(nameLen);
185 a_in.read(_pointerSize, _addressCount);
188 [[nodiscard]] std::size_t address_count() const noexcept {
return static_cast<std::size_t
>(_addressCount); }
189 [[nodiscard]] std::uint64_t pointer_size() const noexcept {
return static_cast<std::uint64_t
>(_pointerSize); }
190 [[nodiscard]] Version version() const noexcept {
return _version; }
194 std::int32_t _pointerSize{ 0 };
195 std::int32_t _addressCount{ 0 };
198 IDDatabase() { load(); }
200 IDDatabase(
const IDDatabase&) =
delete;
201 IDDatabase(IDDatabase&&) =
delete;
203 ~IDDatabase() =
default;
205 IDDatabase& operator=(
const IDDatabase&) =
delete;
206 IDDatabase& operator=(IDDatabase&&) =
delete;
211 const auto filename =
214#ifdef SKYRIM_SUPPORT_AE
215 "Data/SKSE/Plugins/versionlib-{}.bin"sv,
217 "Data/SKSE/Plugins/version-{}.bin"sv,
220 .value_or(L
"<unknown filename>"s);
221 load_file(filename, version);
227 binary_io::file_istream in(a_filename);
230 if (header.version() != a_version) {
234 auto mapname = L
"CommonLibSSEOffsets-v2-"s;
235 mapname += a_version.wstring();
236 const auto byteSize =
static_cast<std::size_t
>(header.address_count()) *
sizeof(mapping_t);
237 if (_mmap.open(mapname, byteSize)) {
238 _id2offset = {
static_cast<mapping_t*
>(_mmap.data()), header.address_count() };
239 }
else if (_mmap.create(mapname, byteSize)) {
240 _id2offset = {
static_cast<mapping_t*
>(_mmap.data()), header.address_count() };
241 unpack_file(in, header);
245 [](
auto&& a_lhs,
auto&& a_rhs) {
246 return a_lhs.id < a_rhs.id;
251 }
catch (
const std::system_error&) {
254 "Failed to locate an appropriate address library with the path: {}\n"
255 "This means you are missing the address library for this specific version of "
256 "the game. Please continue to the mod page for address library to download "
257 "an appropriate version. If one is not available, then it is likely that "
258 "address library has not yet added support for this version of the game."sv,
263 void unpack_file(binary_io::file_istream& a_in, header_t a_header)
265 std::uint8_t type = 0;
266 std::uint64_t
id = 0;
267 std::uint64_t offset = 0;
268 std::uint64_t prevID = 0;
269 std::uint64_t prevOffset = 0;
270 for (
auto& mapping : _id2offset) {
272 const auto lo =
static_cast<std::uint8_t
>(type & 0xF);
273 const auto hi =
static_cast<std::uint8_t
>(type >> 4);
283 id = prevID + std::get<0>(a_in.read<std::uint8_t>());
286 id = prevID - std::get<0>(a_in.read<std::uint8_t>());
289 id = prevID + std::get<0>(a_in.read<std::uint16_t>());
292 id = prevID - std::get<0>(a_in.read<std::uint16_t>());
295 std::tie(
id) = a_in.read<std::uint16_t>();
298 std::tie(
id) = a_in.read<std::uint32_t>();
304 const std::uint64_t tmp = (hi & 8) != 0 ? (prevOffset / a_header.pointer_size()) : prevOffset;
314 offset = tmp + std::get<0>(a_in.read<std::uint8_t>());
317 offset = tmp - std::get<0>(a_in.read<std::uint8_t>());
320 offset = tmp + std::get<0>(a_in.read<std::uint16_t>());
323 offset = tmp - std::get<0>(a_in.read<std::uint16_t>());
326 std::tie(offset) = a_in.read<std::uint16_t>();
329 std::tie(offset) = a_in.read<std::uint32_t>();
336 offset *= a_header.pointer_size();
339 mapping = { id, offset };
346 detail::memory_map _mmap;
347 std::span<mapping_t> _id2offset;