1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
/* * This file is part of libloadorder * * Copyright (C) 2017 Oliver Hamlet * * libloadorder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * libloadorder is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with libloadorder. If not, see <http://www.gnu.org/licenses/>. */ //! # libloadorder-ffi //! //! libloadorder-ffi provides a C API wrapper around libloadorder, a free software library for //! manipulating the load order and active status of plugins for the following games: //! //! * TES III: Morrowind //! * TES IV: Oblivion //! * TES V: Skyrim //! * TES V: Skyrim Special Edition //! * Fallout 3 //! * Fallout: New Vegas //! * Fallout 4 //! //! ## Variable Types //! //! libloadorder-ffi uses character strings and integers for information input/output. //! //! - All strings are null-terminated byte character strings encoded in UTF-8. //! - All return, game and load order method codes are unsigned integers at least 16 bits in size. //! - All array sizes are unsigned integers at least 16 bits in size. //! - File paths are case-sensitive if and only if the underlying file system is case-sensitive. //! //! ## Thread Safety //! //! libloadorder-ffi is thread-safe. Reading and writing data for a single game handle is protected //! by mutual exclusion, and error messages are stored thread-locally. //! //! Game handles operate independently, so using more than one game handle for a single game across //! multiple threads is not advised, as filesystem changes made when writing data are not atomic //! and data races may occur under such usage. //! //! ## Data Caching //! //! libloadorder caches plugin data to improve performance. Each game handle has its own unique //! cache, and change detection is performed whenever an API function that takes a game handle is //! called. If changes are detected, the necessary data are reloaded before the function operates //! on the data. //! //! Edits made to a plugin will only be detected if they that plugin's timestamp changes. If edits //! are made and the timestamp is unchanged, the changes can only be detected by destroying the //! existing game handle and creating a new game handle to use. //! //! ## Plugin Validity //! //! Where libloadorder functions take one or more plugin filenames, it checks that these filenames //! correspond to valid plugins. libloadorder defines a valid plugin as one that: //! //! - Ends with `.esp`, `.esm`, `.esp.ghost` or `.esm.ghost`. //! - Contains a header record with: //! //! - The correct type (`TES3` for Morrowind, `TES4` otherwise). //! - A size that is not larger than the total file size. //! - Subrecords with sizes that do not together sum to larger than the expected total //! subrecords size. //! //! This definition is substantially more permissive than games or other utilities may be for //! performance reasons, and because libloadorder uses no plugin data beyond the header record, so //! later corruption or invalid data would not affect its behaviour. //! //! This permissivity does allow more strictly invalid plugins to be positioned in the load order //! and activated, which may cause game issues, but protecting against such circumstances is beyond //! the scope of libloadorder. //! //! ## Valid Active Plugin Lists //! //! Any active plugin list that is set using libloadorder must be valid, //! ie. it must meet all the following conditions: //! //! - Contains only installed plugins. //! - Contains no duplicate entries. //! - Contains no more than 255 plugins. //! - If a Skyrim or Fallout 4 load order, contains `Skyrim.esm` or `Fallout4.esm` respectively. //! - If a Skyrim load order and `Update.esm` is installed, contains `Update.esm`. //! //! Libloadorder is less strict when loading active plugin lists. If loading a Skyrim or Fallout 4 //! list and the relevant main master file is missing, it will be inferred to load first. //! //! Similarly, if Update.esm is installed but not in the active list, it will be inferred to load //! after all other master files. //! //! ## Valid Load Orders //! //! Any load order that is set using libloadorder must be valid, ie. it must meet all the following //! conditions: //! //! - Contains only installed plugins. //! - Contains no duplicate entries. //! - Loads all master files before all plugin files. Master bit flag value, rather than file //! extension, is checked. //! - For Skyrim or Fallout 4, the first plugin in the load order must be `Skyrim.esm` or //! `Fallout4.esm` respectively. extern crate loadorder; extern crate libc; use std::cell::RefCell; use std::ffi::CString; use std::io; use std::ptr; use libc::{c_char, c_uint, size_t}; use loadorder::Error; mod active_plugins; mod constants; mod handle; mod helpers; mod load_order; pub use active_plugins::*; pub use constants::*; pub use handle::*; use helpers::error; pub use load_order::*; thread_local!(static ERROR_MESSAGE: RefCell<CString> = RefCell::new(CString::default())); /// Gets the library version. /// /// Outputs the major, minor and patch version numbers for the loaded libloadorder. The version /// numbering used is major.minor.patch. /// /// Returns `LIBLO_OK` if successful, otherwise a `LIBLO_ERROR_*` code is returned. #[no_mangle] pub unsafe extern "C" fn lo_get_version( major: *mut c_uint, minor: *mut c_uint, patch: *mut c_uint, ) -> c_uint { if major.is_null() || minor.is_null() || patch.is_null() { error(LIBLO_ERROR_INVALID_ARGS, "Null pointer(s) passed") } else { match env!("CARGO_PKG_VERSION_MAJOR").parse::<c_uint>() { Ok(x) => *major = x, Err(_) => { return error( LIBLO_ERROR_INVALID_ARGS, "Failed to parse major version number", ) } } match env!("CARGO_PKG_VERSION_MINOR").parse::<c_uint>() { Ok(x) => *minor = x, Err(_) => { return error( LIBLO_ERROR_INVALID_ARGS, "Failed to parse minor version number", ) } } match env!("CARGO_PKG_VERSION_PATCH").parse::<c_uint>() { Ok(x) => *patch = x, Err(_) => { return error( LIBLO_ERROR_INVALID_ARGS, "Failed to parse patch version number", ) } } LIBLO_OK } } /// Get the message for the last error or warning encountered. /// /// Outputs a string giving a message containing the details of the last error or warning /// encountered by a function. The message uses thread-local storage, and only one message is /// stored at any one time. /// /// Returns `LIBLO_OK` if successful, otherwise a `LIBLO_ERROR_*` code is returned. #[no_mangle] pub unsafe extern "C" fn lo_get_error_message(message: *mut *const c_char) -> c_uint { if message.is_null() { error(LIBLO_ERROR_INVALID_ARGS, "Null pointer passed") } else { ERROR_MESSAGE.with(|f| if f.borrow().as_bytes().is_empty() { *message = ptr::null(); } else { *message = f.borrow().as_ptr() as *const i8; }); LIBLO_OK } } /// Free memory allocated to string output. /// /// This function should be called to free memory allocated by any API function that outputs a /// string, excluding `lo_get_error_message()`. #[no_mangle] pub unsafe extern "C" fn lo_free_string(string: *mut c_char) { if !string.is_null() { CString::from_raw(string); } } /// Free memory allocated to string array output. /// /// This function should be called to free memory allocated by any API function that outputs an /// array of strings. #[no_mangle] pub unsafe extern "C" fn lo_free_string_array(array: *mut *mut c_char, size: size_t) { if array.is_null() || size == 0 { return; } let vec = Vec::from_raw_parts(array, size, size); for string in vec { lo_free_string(string); } }