From 33b1915b835ebd9bf1fe0dd58531ea1abaf77629 Mon Sep 17 00:00:00 2001 From: mykola2312 <49044616+mykola2312@users.noreply.github.com> Date: Fri, 8 Sep 2023 23:28:58 +0300 Subject: [PATCH] Player attributes decoding is complete and fully working --- src/fot/attributes.rs | 207 +++++++++++++++++++++++++++++++++++++----- src/fot/stream.rs | 8 ++ src/fot/world.rs | 25 ++--- 3 files changed, 199 insertions(+), 41 deletions(-) diff --git a/src/fot/attributes.rs b/src/fot/attributes.rs index 574df2b..7eb6151 100644 --- a/src/fot/attributes.rs +++ b/src/fot/attributes.rs @@ -1,8 +1,8 @@ +use super::esh::{ESHValue, ESH}; use super::stream::{ReadStream, WriteStream}; -use super::esh::{ESH, ESHValue}; use super::tag::Tag; +use anyhow::{anyhow, Result}; use indexmap::IndexMap; -use anyhow::{Result, anyhow}; const STATS: [&str; 7] = [ "strength", @@ -11,7 +11,7 @@ const STATS: [&str; 7] = [ "charisma", "intelligence", "agility", - "luck" + "luck", ]; const TRAITS: [&str; 11] = [ @@ -25,7 +25,7 @@ const TRAITS: [&str; 11] = [ "age", "bonusAC", "sex", - "race" + "race", ]; const DERIVED: [&str; 26] = [ @@ -54,7 +54,7 @@ const DERIVED: [&str; 26] = [ "meleeDamage", "bonusDamage", "skillPerLevel", - "levelsPerPerk" + "levelsPerPerk", ]; const SKILLS: [&str; 18] = [ @@ -75,7 +75,7 @@ const SKILLS: [&str; 18] = [ "pilot", "barter", "gambling", - "outdoorsman" + "outdoorsman", ]; const OPT_TRAITS: [&str; 38] = [ @@ -116,10 +116,138 @@ const OPT_TRAITS: [&str; 38] = [ "doDieHard", "doHthEvade", "doDrunkenMaster", - "doNightPerson" + "doNightPerson", ]; -pub struct Attributes { +const PERKS: [&str; 111] = [ + "awareness", + "bonusHtHAttacks", + "bonusHtHDamage", + "bonusMove", + "bonusRangedDamage", + "bonusRateofFire", + "fasterHealing", + "moreCriticals", + "nightVision", + "radResistance", + "toughness", + "strongBack", + "sharpshooter", + "silentRunning", + "survivalist", + "masterTrader", + "educated", + "healer", + "fortuneFinder", + "betterCriticals", + "slayer", + "sniper", + "silentDeath", + "actionBoy", + "lifegiver", + "dodger", + "snakeater", + "mrFixit", + "medic", + "masterThief", + "heaveHo", + "pickpocket", + "ghost", + "explorer", + "flowerChild", + "pathfinder", + "scout", + "mysteriousStranger", + "ranger", + "quickPockets", + "swiftLearner", + "tag", + "mutate", + "adrenalineRush", + "cautiousNature", + "comprehension", + "demolitionExpert", + "gambler", + "gainStrenght", + "gainPerception", + "gainEndurance", + "gainCharisma", + "gainIntelligence", + "gainAgility", + "gainLuck", + "harmless", + "hereandNow", + "hthEvade", + "lightStep", + "livingAnatomy", + "negotiator", + "packRat", + "pyromaniac", + "quickRecovery", + "salesman", + "stonewall", + "thief", + "weaponHandling", + "stuntMan", + "crazyBomber", + "roadWarrior", + "gunner", + "leadFoot", + "tunnelRat", + "bracing", + "flexible", + "bendTheRules", + "breakTheRules", + "loner", + "teamPlayer", + "leader", + "hitTheDeck", + "boneHead", + "brownNoser", + "dieHard", + "drunkenMaster", + "stat", + "radChild", + "cancerousGrowth", + "bonsai", + "steadyArm", + "psychotic", + "toughHige", + "deathSense", + "brutishHulk", + "talonOfFear", + "hideOfScars", + "wayOfTheFruit", + "twitchGamer", + "bluffMaster", + "divineFavour", + "unk1", + "unk2", + "unk3", + "unk4", + "unk5", + "unk6", + "unk7", + "unk8", + "unk9", + "unk10", +]; + +const ADDICTIONS: [&str; 10] = [ + "buffoutAddiction", + "afterburnerAddiction", + "mentatsAddiction", + "psychoAddiction", + "radAwayAddiction", + "voodooAddiction", + "nukaColaAddiction", + "boozeAddiction", + "withdrawal", + "drunk", +]; + +#[derive(Debug)] +pub struct Attributes { esh: ESH, pub stats: IndexMap<&'static str, u32>, pub traits: IndexMap<&'static str, u32>, @@ -128,37 +256,72 @@ pub struct Attributes { pub skill_tags: IndexMap<&'static str, bool>, pub opt_traits: IndexMap<&'static str, bool>, pub perks: IndexMap<&'static str, u32>, - pub addictions: IndexMap<&'static str, u32> + pub addictions: IndexMap<&'static str, u32>, } impl Attributes { - fn from_binary(bin: &[u8]) -> Result { + pub fn from_binary(bin: &[u8]) -> Result { let mut rd = ReadStream::new(bin, 0); - + let _ = rd.read_u32()?; let esh: ESH = rd.read()?; if esh.props["Binary"] == ESHValue::Bool(false) { return Err(anyhow!("Attributes Binary == false")); } - let mut stats: IndexMap<&'static str, u32>; - let mut traits: IndexMap<&'static str, u32>; - let mut derived: IndexMap<&'static str, u32>; - let mut skills: IndexMap<&'static str, u32>; - let mut skill_tags: IndexMap<&'static str, bool>; - let mut opt_traits: IndexMap<&'static str, bool>; - let mut perks: IndexMap<&'static str, u32>; - let mut addictions: IndexMap<&'static str, u32>; + let mut stats: IndexMap<&'static str, u32> = IndexMap::with_capacity(7); + let mut traits: IndexMap<&'static str, u32> = IndexMap::with_capacity(11); + let mut derived: IndexMap<&'static str, u32> = IndexMap::with_capacity(26); + let mut skills: IndexMap<&'static str, u32> = IndexMap::with_capacity(18); + let mut skill_tags: IndexMap<&'static str, bool> = IndexMap::with_capacity(18); + let mut opt_traits: IndexMap<&'static str, bool> = IndexMap::with_capacity(38); + let mut perks: IndexMap<&'static str, u32> = IndexMap::with_capacity(111); + let mut addictions: IndexMap<&'static str, u32> = IndexMap::with_capacity(10); if let ESHValue::Binary(binary) = &esh.props["esbin"] { let mut rd = ReadStream::new(&binary, 0); - + let _ = rd.read_u32()?; let _: Tag = rd.read()?; + + for i in 0..7 { + stats.insert(STATS[i], rd.read_u32()?); + } + for i in 0..11 { + traits.insert(TRAITS[i], rd.read_u32()?); + } + for i in 0..26 { + derived.insert(DERIVED[i], rd.read_u32()?); + } + for i in 0..18 { + skills.insert(SKILLS[i], rd.read_u32()?); + } + for i in 0..18 { + skill_tags.insert(SKILLS[i], rd.read_bool()?); + } + for i in 0..38 { + opt_traits.insert(OPT_TRAITS[i], rd.read_bool()?); + } + for i in 0..111 { + perks.insert(PERKS[i], rd.read_u32()?); + } + for i in 0..10 { + addictions.insert(ADDICTIONS[i], rd.read_u32()?); + } + + Ok(Attributes { + esh, + stats, + traits, + derived, + skills, + skill_tags, + opt_traits, + perks, + addictions, + }) } else { return Err(anyhow!("Attributes has no esbin")); } - - todo!() } } diff --git a/src/fot/stream.rs b/src/fot/stream.rs index 4c3813d..03b63cc 100644 --- a/src/fot/stream.rs +++ b/src/fot/stream.rs @@ -70,6 +70,10 @@ impl<'a> ReadStream<'a> { Ok(self.rdr.read_u8()?) } + pub fn read_bool(&mut self) -> Result { + Ok(self.read_u8()? != 0) + } + pub fn read_u16(&mut self) -> Result { Ok(self.rdr.read_u16::()?) } @@ -148,6 +152,10 @@ impl WriteStream { Ok(self.buf.write_u8(val)?) } + pub fn write_bool(&mut self, val: bool) -> Result<()> { + self.write_u8(val as u8) + } + pub fn write_u16(&mut self, val: u16) -> Result<()> { Ok(self.buf.write_u16::(val)?) } diff --git a/src/fot/world.rs b/src/fot/world.rs index eeaeeb8..580d632 100644 --- a/src/fot/world.rs +++ b/src/fot/world.rs @@ -1,3 +1,5 @@ +use crate::fot::attributes; + use super::decoder::DecoderCtx; use super::entitylist::{EntityEncoding, EntityList}; use super::fstring::FString; @@ -12,6 +14,7 @@ use deflate::deflate_bytes_zlib; use inflate::inflate_bytes_zlib; use super::esh::{ESHValue, ESH}; +use super::attributes::Attributes; use std::path::Path; pub struct World { @@ -43,25 +46,9 @@ impl World { //self.entlist.dump_to_entfile(ent, Path::new("D:\\actor.ent"))?; println!(""); - if let ESHValue::Binary(attributes) = &esh.props["Attributes"] { - let mut rd = ReadStream::new(&attributes, 0); - - let size = rd.read_u32()?; - let attrs_esh: ESH = rd.read()?; - for (name, value) in &attrs_esh.props { - println!("{} {}", name, value); - } - } - - println!(""); - if let ESHValue::Binary(attributes) = &esh.props["Modifiers"] { - let mut rd = ReadStream::new(&attributes, 0); - - let size = rd.read_u32()?; - let attrs_esh: ESH = rd.read()?; - for (name, value) in &attrs_esh.props { - println!("{} {}", name, value); - } + if let ESHValue::Binary(binary) = &esh.props["Attributes"] { + let attributes = Attributes::from_binary(&binary)?; + dbg!(attributes); } Ok(())