commonlibsse_ng\rel\id\id_database/
bin_loader.rs
1use std::path::Path;
2
3use crate::rel::id::Mapping;
4use crate::rel::id::id_database::header::Header;
5use crate::rel::id::id_database::unpack::unpack_file;
6use crate::rel::id::id_database::{DataBaseError, FailedUnpackFileSnafu};
7use crate::rel::version::Version;
8use shared_rwlock::SharedRwLock;
9use snafu::ResultExt as _;
10
11pub(super) fn load_bin_file<P>(
22 path: P,
23 version: Version,
24 expected_fmt_ver: u8,
25) -> Result<SharedRwLock<Mapping>, DataBaseError>
26where
27 P: AsRef<Path>,
28{
29 load_bin_file_inner(path.as_ref(), version, expected_fmt_ver)
30}
31
32fn load_bin_file_inner(
33 path: &Path,
34 version: Version,
35 expected_fmt_ver: u8,
36) -> Result<SharedRwLock<Mapping>, DataBaseError> {
37 use std::fs::File;
38 use std::io;
39
40 let mut reader = {
41 let file = File::open(path)
42 .map_err(|_| DataBaseError::AddressLibraryNotFound { path: path.to_path_buf() })?;
43 io::BufReader::new(file)
44 };
45
46 let header = Header::from_reader(&mut reader, expected_fmt_ver)?;
47 if header.version != version {
48 return Err(DataBaseError::VersionMismatch { expected: version, actual: header.version });
49 }
50
51 let (mem_map, is_created) = {
52 let shared_id =
53 windows::core::HSTRING::from(format!("CommonLibSSEOffsets-rs-v2-{version}"));
54 SharedRwLock::new(&shared_id, header.address_count())
55 }
56 .map_err(|err| DataBaseError::MemoryMapError { source: err })?;
57
58 if is_created {
59 let mut mem_map = mem_map.write().map_err(|_| DataBaseError::Poisoned)?;
60 unpack_file(&mut mem_map, &mut reader, header.pointer_size())
61 .context(FailedUnpackFileSnafu)?;
62 }
63
64 Ok(mem_map)
65}
66
67#[cfg(feature = "test_on_local")]
68#[cfg(test)]
69mod local_tests {
70 use core::ffi::c_void;
71 use core::num::NonZeroUsize;
72 use core::ptr::NonNull;
73
74 use super::*;
75 use crate::rel::ResolvableAddress;
76 use crate::rel::id::IDDatabase;
77 use crate::rel::module::{ModuleStateError, Runtime, get_skyrim_dir};
78 use crate::rel::version::Version;
79
80 const VERSION: Version = Version::new(1, 6, 353, 0);
83 const MODULE_BASE: usize = 0x1000;
84 #[derive(Debug)]
87 struct TestRelocation {
88 db: IDDatabase,
89 current_id: u64,
90 }
91
92 impl TestRelocation {
93 const fn new(mem_map: SharedRwLock<Mapping>) -> Self {
94 Self { db: IDDatabase { mem_map }, current_id: 0 }
95 }
96
97 const fn set_id(&mut self, id: u64) {
98 self.current_id = id;
99 }
100 }
101
102 impl ResolvableAddress for TestRelocation {
103 fn offset(&self) -> Result<NonZeroUsize, DataBaseError> {
104 self.db.id_to_offset(self.current_id)
105 }
106
107 fn address(&self) -> Result<NonNull<c_void>, DataBaseError> {
108 let offset = self.offset()?;
109 Ok(unsafe { Self::base()?.byte_add(offset.get()) })
110 }
111
112 fn base() -> Result<NonNull<c_void>, ModuleStateError> {
114 NonNull::new(core::ptr::without_provenance_mut(MODULE_BASE))
115 .ok_or(ModuleStateError::ModuleLockIsPoisoned)
116 }
117 }
118
119 #[allow(unused)]
120 fn write_debug_value(value: impl core::fmt::Debug) -> std::io::Result<()> {
121 const TARGET: &str = env!("CARGO_MANIFEST_DIR");
122 std::fs::write(format!("{TARGET}/address_dump.log"), format!("{value:#?}"))
123 }
124
125 #[test]
127 fn test_load_bin() {
128 let mut test_rel = {
129 let runtime = Runtime::from_version(&VERSION);
130 let ver_suffix = if runtime.is_ae() { "lib" } else { "" };
131 let path = get_skyrim_dir(runtime).unwrap().join(format!(
132 "Data/SKSE/Plugins/version{ver_suffix}-{}.bin",
133 VERSION.to_address_library_string(),
134 ));
135 TestRelocation::new(load_bin_file(&path, VERSION, 2).unwrap())
136 };
137
138 test_rel.set_id(11483);
140 assert_eq!(test_rel.offset().unwrap().get(), 0x10f7a0);
141 assert_eq!(test_rel.address().unwrap().addr().get(), MODULE_BASE + 0x10f7a0);
142 }
143}