CommonLibSSE (powerof3)
Loading...
Searching...
No Matches
ID.h
Go to the documentation of this file.
1#pragma once
2
3#include "REL/Module.h"
4
5namespace REL
6{
7 namespace detail
8 {
10 {
11 public:
12 memory_map() noexcept = default;
13 memory_map(const memory_map&) = delete;
14
15 memory_map(memory_map&& a_rhs) noexcept :
16 _mapping(a_rhs._mapping),
17 _view(a_rhs._view)
18 {
19 a_rhs._mapping = nullptr;
20 a_rhs._view = nullptr;
21 }
22
24
25 memory_map& operator=(const memory_map&) = delete;
26
27 memory_map& operator=(memory_map&& a_rhs) noexcept
28 {
29 if (this != std::addressof(a_rhs)) {
30 _mapping = a_rhs._mapping;
31 a_rhs._mapping = nullptr;
32
33 _view = a_rhs._view;
34 a_rhs._view = nullptr;
35 }
36 return *this;
37 }
38
39 [[nodiscard]] void* data() noexcept { return _view; }
40
41 bool open(stl::zwstring a_name, std::size_t a_size);
42 bool create(stl::zwstring a_name, std::size_t a_size);
43 void close();
44
45 private:
46 void* _mapping{ nullptr };
47 void* _view{ nullptr };
48 };
49 }
50
52 {
53 private:
54 struct mapping_t
55 {
56 std::uint64_t id;
57 std::uint64_t offset;
58 };
59
60 public:
62 {
63 public:
64 using value_type = mapping_t;
65 using container_type = std::vector<value_type>;
66 using size_type = typename container_type::size_type;
67 using const_iterator = typename container_type::const_iterator;
68 using const_reverse_iterator = typename container_type::const_reverse_iterator;
69
70 template <class ExecutionPolicy>
71 explicit Offset2ID(ExecutionPolicy&& a_policy) //
72 requires(std::is_execution_policy_v<std::decay_t<ExecutionPolicy>>)
73 {
74 const std::span<const mapping_t> id2offset = IDDatabase::get()._id2offset;
75 _offset2id.reserve(id2offset.size());
76 _offset2id.insert(_offset2id.begin(), id2offset.begin(), id2offset.end());
77 std::sort(a_policy, _offset2id.begin(), _offset2id.end(), [](auto&& a_lhs, auto&& a_rhs) {
78 return a_lhs.offset < a_rhs.offset;
79 });
80 }
81
83 Offset2ID(std::execution::sequenced_policy{})
84 {}
85
86 [[nodiscard]] std::uint64_t operator()(std::size_t a_offset) const
87 {
88 const mapping_t elem{ 0, a_offset };
89 const auto it = std::lower_bound(
90 _offset2id.begin(),
91 _offset2id.end(),
92 elem,
93 [](auto&& a_lhs, auto&& a_rhs) {
94 return a_lhs.offset < a_rhs.offset;
95 });
96 if (it == _offset2id.end()) {
98 std::format(
99 "Failed to find the offset within the database: 0x{:08X}"sv,
100 a_offset));
101 }
102
103 return it->id;
104 }
105
106 [[nodiscard]] const_iterator begin() const noexcept { return _offset2id.begin(); }
107 [[nodiscard]] const_iterator cbegin() const noexcept { return _offset2id.cbegin(); }
108
109 [[nodiscard]] const_iterator end() const noexcept { return _offset2id.end(); }
110 [[nodiscard]] const_iterator cend() const noexcept { return _offset2id.cend(); }
111
112 [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return _offset2id.rbegin(); }
113 [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return _offset2id.crbegin(); }
114
115 [[nodiscard]] const_reverse_iterator rend() const noexcept { return _offset2id.rend(); }
116 [[nodiscard]] const_reverse_iterator crend() const noexcept { return _offset2id.crend(); }
117
118 [[nodiscard]] size_type size() const noexcept { return _offset2id.size(); }
119
120 private:
121 container_type _offset2id;
122 };
123
124 [[nodiscard]] static IDDatabase& get()
125 {
126 static IDDatabase singleton;
127 return singleton;
128 }
129
130 [[nodiscard]] inline std::size_t id2offset(std::uint64_t a_id) const
131 {
132 mapping_t elem{ a_id, 0 };
133 const auto it = std::lower_bound(
134 _id2offset.begin(),
135 _id2offset.end(),
136 elem,
137 [](auto&& a_lhs, auto&& a_rhs) {
138 return a_lhs.id < a_rhs.id;
139 });
140 if (it == _id2offset.end()) {
142 std::format(
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,
146 a_id));
147 }
148
149 return static_cast<std::size_t>(it->offset);
150 }
151
152 private:
153 friend Offset2ID;
154
155 class header_t
156 {
157 public:
158 void read(binary_io::file_istream& a_in)
159 {
160 const auto [format] = a_in.read<std::int32_t>();
161#ifdef SKYRIM_SUPPORT_AE
162 if (format != 2) {
163#else
164 if (format != 1) {
165#endif
167 std::format(
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 "
171 "support it."sv,
172 format));
173 }
174
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);
181
182 const auto [nameLen] = a_in.read<std::int32_t>();
183 a_in.seek_relative(nameLen);
184
185 a_in.read(_pointerSize, _addressCount);
186 }
187
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; }
191
192 private:
193 Version _version;
194 std::int32_t _pointerSize{ 0 };
195 std::int32_t _addressCount{ 0 };
196 };
197
198 IDDatabase() { load(); }
199
200 IDDatabase(const IDDatabase&) = delete;
201 IDDatabase(IDDatabase&&) = delete;
202
203 ~IDDatabase() = default;
204
205 IDDatabase& operator=(const IDDatabase&) = delete;
206 IDDatabase& operator=(IDDatabase&&) = delete;
207
208 void load()
209 {
210 const auto version = Module::get().version();
211 const auto filename =
213 std::format(
214#ifdef SKYRIM_SUPPORT_AE
215 "Data/SKSE/Plugins/versionlib-{}.bin"sv,
216#else
217 "Data/SKSE/Plugins/version-{}.bin"sv,
218#endif
219 version.string()))
220 .value_or(L"<unknown filename>"s);
221 load_file(filename, version);
222 }
223
224 void load_file(stl::zwstring a_filename, Version a_version)
225 {
226 try {
227 binary_io::file_istream in(a_filename);
228 header_t header;
229 header.read(in);
230 if (header.version() != a_version) {
231 stl::report_and_fail("version mismatch"sv);
232 }
233
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);
242 std::sort(
243 _id2offset.begin(),
244 _id2offset.end(),
245 [](auto&& a_lhs, auto&& a_rhs) {
246 return a_lhs.id < a_rhs.id;
247 });
248 } else {
249 stl::report_and_fail("failed to create shared mapping"sv);
250 }
251 } catch (const std::system_error&) {
253 std::format(
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,
259 stl::utf16_to_utf8(a_filename).value_or("<unknown filename>"s)));
260 }
261 }
262
263 void unpack_file(binary_io::file_istream& a_in, header_t a_header)
264 {
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) {
271 a_in.read(type);
272 const auto lo = static_cast<std::uint8_t>(type & 0xF);
273 const auto hi = static_cast<std::uint8_t>(type >> 4);
274
275 switch (lo) {
276 case 0:
277 a_in.read(id);
278 break;
279 case 1:
280 id = prevID + 1;
281 break;
282 case 2:
283 id = prevID + std::get<0>(a_in.read<std::uint8_t>());
284 break;
285 case 3:
286 id = prevID - std::get<0>(a_in.read<std::uint8_t>());
287 break;
288 case 4:
289 id = prevID + std::get<0>(a_in.read<std::uint16_t>());
290 break;
291 case 5:
292 id = prevID - std::get<0>(a_in.read<std::uint16_t>());
293 break;
294 case 6:
295 std::tie(id) = a_in.read<std::uint16_t>();
296 break;
297 case 7:
298 std::tie(id) = a_in.read<std::uint32_t>();
299 break;
300 default:
301 stl::report_and_fail("unhandled type"sv);
302 }
303
304 const std::uint64_t tmp = (hi & 8) != 0 ? (prevOffset / a_header.pointer_size()) : prevOffset;
305
306 switch (hi & 7) {
307 case 0:
308 a_in.read(offset);
309 break;
310 case 1:
311 offset = tmp + 1;
312 break;
313 case 2:
314 offset = tmp + std::get<0>(a_in.read<std::uint8_t>());
315 break;
316 case 3:
317 offset = tmp - std::get<0>(a_in.read<std::uint8_t>());
318 break;
319 case 4:
320 offset = tmp + std::get<0>(a_in.read<std::uint16_t>());
321 break;
322 case 5:
323 offset = tmp - std::get<0>(a_in.read<std::uint16_t>());
324 break;
325 case 6:
326 std::tie(offset) = a_in.read<std::uint16_t>();
327 break;
328 case 7:
329 std::tie(offset) = a_in.read<std::uint32_t>();
330 break;
331 default:
332 stl::report_and_fail("unhandled type"sv);
333 }
334
335 if ((hi & 8) != 0) {
336 offset *= a_header.pointer_size();
337 }
338
339 mapping = { id, offset };
340
341 prevOffset = offset;
342 prevID = id;
343 }
344 }
345
346 detail::memory_map _mmap;
347 std::span<mapping_t> _id2offset;
348 };
349
350 class ID
351 {
352 public:
353 constexpr ID() noexcept = default;
354
355 explicit constexpr ID(std::uint64_t a_id) noexcept :
356 _id(a_id)
357 {}
358
359 constexpr ID& operator=(std::uint64_t a_id) noexcept
360 {
361 _id = a_id;
362 return *this;
363 }
364
365 [[nodiscard]] std::uintptr_t address() const { return base() + offset(); }
366 [[nodiscard]] constexpr std::uint64_t id() const noexcept { return _id; }
367 [[nodiscard]] std::size_t offset() const { return IDDatabase::get().id2offset(_id); }
368
369 private:
370 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
371
372 std::uint64_t _id{ 0 };
373 };
374}
Definition ID.h:62
Offset2ID()
Definition ID.h:82
typename container_type::const_iterator const_iterator
Definition ID.h:67
typename container_type::const_reverse_iterator const_reverse_iterator
Definition ID.h:68
const_reverse_iterator crbegin() const noexcept
Definition ID.h:113
const_iterator begin() const noexcept
Definition ID.h:106
std::vector< value_type > container_type
Definition ID.h:65
size_type size() const noexcept
Definition ID.h:118
Offset2ID(ExecutionPolicy &&a_policy)
Definition ID.h:71
const_reverse_iterator rbegin() const noexcept
Definition ID.h:112
const_reverse_iterator crend() const noexcept
Definition ID.h:116
const_iterator cbegin() const noexcept
Definition ID.h:107
typename container_type::size_type size_type
Definition ID.h:66
const_iterator cend() const noexcept
Definition ID.h:110
std::uint64_t operator()(std::size_t a_offset) const
Definition ID.h:86
mapping_t value_type
Definition ID.h:64
const_reverse_iterator rend() const noexcept
Definition ID.h:115
const_iterator end() const noexcept
Definition ID.h:109
Definition ID.h:52
static IDDatabase & get()
Definition ID.h:124
std::size_t id2offset(std::uint64_t a_id) const
Definition ID.h:130
Definition ID.h:351
std::size_t offset() const
Definition ID.h:367
constexpr ID & operator=(std::uint64_t a_id) noexcept
Definition ID.h:359
constexpr std::uint64_t id() const noexcept
Definition ID.h:366
constexpr ID() noexcept=default
std::uintptr_t address() const
Definition ID.h:365
Version version() const noexcept
Definition Module.h:62
static Module & get()
Definition Module.h:54
Definition ID.h:10
~memory_map()
Definition ID.h:23
memory_map & operator=(memory_map &&a_rhs) noexcept
Definition ID.h:27
memory_map & operator=(const memory_map &)=delete
memory_map() noexcept=default
void * data() noexcept
Definition ID.h:39
bool create(stl::zwstring a_name, std::size_t a_size)
bool open(stl::zwstring a_name, std::size_t a_size)
Definition ID.h:6
void report_and_fail(std::string_view a_msg, std::source_location a_loc=std::source_location::current())
Definition PCH.h:397
auto utf16_to_utf8(std::wstring_view a_in) noexcept -> std::optional< std::string >
Definition PCH.h:369
auto utf8_to_utf16(std::string_view a_in) noexcept -> std::optional< std::wstring >
Definition PCH.h:343
basic_zstring< wchar_t > zwstring
Definition PCH.h:82
Definition EffectArchetypes.h:65