mirror of
https://github.com/loot/loot-condition-interpreter.git
synced 2026-07-27 14:16:09 -07:00
Support reading file version from Windows executables
This commit is contained in:
@@ -3,3 +3,4 @@
|
|||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
/testing-plugins
|
/testing-plugins
|
||||||
|
/loot_api*
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ authors = ["Oliver Hamlet <oliver.hamlet@gmail.com>"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
crc = "1.0.0"
|
crc = "1.0.0"
|
||||||
nom = "4.0.0"
|
nom = "4.0.0"
|
||||||
|
pelite = "0.7.0"
|
||||||
regex = "1.0.5"
|
regex = "1.0.5"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ Currently only condition parsing is complete. Evaluation is partially done, the
|
|||||||
rest hasn't yet been started.
|
rest hasn't yet been started.
|
||||||
|
|
||||||
The tests need the [testing-plugins](https://github.com/WrinklyNinja/testing-plugins)
|
The tests need the [testing-plugins](https://github.com/WrinklyNinja/testing-plugins)
|
||||||
directory to be present in the repo root.
|
and the [LOOT API v0.13.8](https://github.com/loot/loot-api/releases/tag/0.13.8)
|
||||||
|
Windows archives to be extracted and present in the repo root.
|
||||||
|
|||||||
+8
-2
@@ -1,6 +1,7 @@
|
|||||||
extern crate crc;
|
extern crate crc;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate nom;
|
extern crate nom;
|
||||||
|
extern crate pelite;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -24,8 +25,7 @@ use function::Function;
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
ParsingIncomplete,
|
ParsingIncomplete,
|
||||||
ParsingError,
|
ParsingError,
|
||||||
InvalidPath(PathBuf),
|
PeParsingError,
|
||||||
InvalidRegex(String),
|
|
||||||
IoError(io::Error),
|
IoError(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +44,12 @@ impl From<io::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<pelite::resources::FindError> for Error {
|
||||||
|
fn from(_: pelite::resources::FindError) -> Self {
|
||||||
|
Error::PeParsingError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum GameType {
|
pub enum GameType {
|
||||||
Tes4,
|
Tes4,
|
||||||
Tes5,
|
Tes5,
|
||||||
|
|||||||
+75
-3
@@ -1,6 +1,10 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use pelite::resources::version_info::VersionInfo;
|
||||||
|
use pelite::resources::FindError;
|
||||||
|
use pelite::FileMap;
|
||||||
|
|
||||||
use Error;
|
use Error;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
@@ -25,8 +29,40 @@ pub struct Version {
|
|||||||
|
|
||||||
impl Version {
|
impl Version {
|
||||||
pub fn read_file_version(file_path: &Path) -> Result<Self, Error> {
|
pub fn read_file_version(file_path: &Path) -> Result<Self, Error> {
|
||||||
// TODO: Actually read the file's File Version field.
|
let file_map = FileMap::open(file_path)?;
|
||||||
Ok(Version::from(format!("{}", file_path.display()).as_str()))
|
let version_info = get_pe_version_info(file_map.as_ref())?;
|
||||||
|
|
||||||
|
if let Some(fixed_file_info) = version_info.fixed() {
|
||||||
|
let version = format!(
|
||||||
|
"{}.{}.{}.{}",
|
||||||
|
fixed_file_info.dwFileVersion.Major,
|
||||||
|
fixed_file_info.dwFileVersion.Minor,
|
||||||
|
fixed_file_info.dwFileVersion.Patch,
|
||||||
|
fixed_file_info.dwFileVersion.Build
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Version::from(version.as_str()))
|
||||||
|
} else {
|
||||||
|
Ok(Version::from(""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pe_version_info(bytes: &[u8]) -> Result<VersionInfo, FindError> {
|
||||||
|
use pelite;
|
||||||
|
use pelite::pe64;
|
||||||
|
match pe64::PeFile::from_bytes(bytes) {
|
||||||
|
Ok(file) => {
|
||||||
|
use pelite::pe64::Pe;
|
||||||
|
|
||||||
|
file.resources()?.version_info()
|
||||||
|
}
|
||||||
|
Err(pelite::Error::PeMagic) => {
|
||||||
|
use pelite::pe32::{Pe, PeFile};
|
||||||
|
|
||||||
|
PeFile::from_bytes(bytes)?.resources()?.version_info()
|
||||||
|
}
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +86,7 @@ impl<'a> From<&'a str> for Version {
|
|||||||
Version {
|
Version {
|
||||||
release_ids: release.split('.').map(Identifier::from).collect(),
|
release_ids: release.split('.').map(Identifier::from).collect(),
|
||||||
pre_release_ids: pre_release
|
pre_release_ids: pre_release
|
||||||
.split(is_pre_release_separator)
|
.split_terminator(is_pre_release_separator)
|
||||||
.map(Identifier::from)
|
.map(Identifier::from)
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
@@ -108,6 +144,42 @@ mod tests {
|
|||||||
mod empty {
|
mod empty {
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_read_file_version_should_read_the_file_version_field_of_a_32_bit_executable() {
|
||||||
|
let version = Version::read_file_version(Path::new(
|
||||||
|
"loot_api-0.13.8-0-g47797cc_dev-win32/loot_api.dll",
|
||||||
|
)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
version.release_ids,
|
||||||
|
vec![
|
||||||
|
Identifier::Numeric(0),
|
||||||
|
Identifier::Numeric(13),
|
||||||
|
Identifier::Numeric(8),
|
||||||
|
Identifier::Numeric(0),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert!(version.pre_release_ids.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_read_file_version_should_read_the_file_version_field_of_a_64_bit_executable() {
|
||||||
|
let version = Version::read_file_version(Path::new(
|
||||||
|
"loot_api-0.13.8-0-g47797cc_dev-win64/loot_api.dll",
|
||||||
|
)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
version.release_ids,
|
||||||
|
vec![
|
||||||
|
Identifier::Numeric(0),
|
||||||
|
Identifier::Numeric(13),
|
||||||
|
Identifier::Numeric(8),
|
||||||
|
Identifier::Numeric(0),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert!(version.pre_release_ids.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn version_eq_an_empty_string_should_equal_an_empty_string() {
|
fn version_eq_an_empty_string_should_equal_an_empty_string() {
|
||||||
assert_eq!(Version::from(""), Version::from(""));
|
assert_eq!(Version::from(""), Version::from(""));
|
||||||
|
|||||||
Reference in New Issue
Block a user