Skip to main content

genie_dat/
civ.rs

1use crate::unit_type::{UnitType, UnitTypeID};
2use crate::GameVersion;
3use arrayvec::ArrayString;
4use byteorder::{ReadBytesExt, WriteBytesExt, LE};
5use encoding_rs::WINDOWS_1252;
6use genie_support::{fallible_try_from, infallible_try_into, read_opt_u16};
7use std::convert::TryInto;
8use std::io::{Read, Result, Write};
9
10/// An ID identifying a civilization
11#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
12pub struct CivilizationID(u8);
13
14impl From<u8> for CivilizationID {
15    fn from(n: u8) -> Self {
16        CivilizationID(n)
17    }
18}
19
20impl From<CivilizationID> for u8 {
21    fn from(n: CivilizationID) -> Self {
22        n.0
23    }
24}
25
26impl From<CivilizationID> for u16 {
27    fn from(n: CivilizationID) -> Self {
28        n.0.into()
29    }
30}
31
32impl From<CivilizationID> for u32 {
33    fn from(n: CivilizationID) -> Self {
34        n.0.into()
35    }
36}
37
38impl From<CivilizationID> for usize {
39    fn from(n: CivilizationID) -> Self {
40        n.0.into()
41    }
42}
43
44infallible_try_into!(CivilizationID, i16);
45infallible_try_into!(CivilizationID, i32);
46fallible_try_from!(CivilizationID, i8);
47fallible_try_from!(CivilizationID, i16);
48fallible_try_from!(CivilizationID, u16);
49fallible_try_from!(CivilizationID, i32);
50fallible_try_from!(CivilizationID, u32);
51
52type CivName = ArrayString<[u8; 20]>;
53
54/// Information about a civilization.
55#[derive(Debug, Default, Clone)]
56pub struct Civilization {
57    name: CivName,
58    attributes: Vec<f32>,
59    civ_effect: u16,
60    bonus_effect: Option<u16>,
61    culture: u8,
62    unit_types: Vec<Option<UnitType>>,
63}
64
65impl Civilization {
66    /// Get the name of this civilization.
67    pub fn name(&self) -> &str {
68        self.name.as_str()
69    }
70
71    /// Read civilization data from an input stream.
72    pub fn read_from(mut input: impl Read, version: GameVersion) -> Result<Self> {
73        let mut civ = Self::default();
74        let mut bytes = [0; 20];
75        input.read_exact(&mut bytes)?;
76        let bytes = &bytes[..bytes.iter().position(|&c| c == 0).unwrap_or(bytes.len())];
77        let (name, _encoding, _failed) = WINDOWS_1252.decode(&bytes);
78        civ.name = CivName::from(&name).unwrap();
79        let num_attributes = input.read_u16::<LE>()?;
80        civ.civ_effect = input.read_u16::<LE>()?;
81        civ.bonus_effect = read_opt_u16(&mut input)?;
82
83        civ.attributes.reserve(num_attributes as usize);
84        for _ in 0..num_attributes {
85            civ.attributes.push(input.read_f32::<LE>()?);
86        }
87
88        civ.culture = input.read_u8()?;
89
90        let num_unit_types = input.read_u16::<LE>()?;
91        let have_unit_types = {
92            let mut list = vec![];
93            for _ in 0..num_unit_types {
94                list.push(input.read_u32::<LE>()? != 0);
95            }
96            list
97        };
98        for do_read in have_unit_types {
99            if !do_read {
100                civ.unit_types.push(None);
101                continue;
102            }
103            civ.unit_types
104                .push(Some(UnitType::read_from(&mut input, version.as_f32())?));
105        }
106
107        Ok(civ)
108    }
109
110    /// Write civilization data to an output stream.
111    pub fn write_to(&self, mut output: impl Write, version: GameVersion) -> Result<()> {
112        let mut name = [0; 20];
113        (&mut name[..self.name.len()]).copy_from_slice(self.name.as_bytes());
114        output.write_all(&name)?;
115        output.write_u16::<LE>(self.attributes.len().try_into().unwrap())?;
116        output.write_u16::<LE>(self.civ_effect)?;
117        output.write_u16::<LE>(self.bonus_effect.unwrap_or(0xFFFF))?;
118        for v in self.attributes.iter() {
119            output.write_f32::<LE>(*v)?;
120        }
121        output.write_u8(self.culture)?;
122
123        output.write_u16::<LE>(self.unit_types.len().try_into().unwrap())?;
124        for opt in &self.unit_types {
125            output.write_u32::<LE>(match opt {
126                Some(_) => 1,
127                None => 0,
128            })?;
129        }
130        for opt in &self.unit_types {
131            if let Some(unit_type) = opt {
132                unit_type.write_to(&mut output, version.as_f32())?;
133            }
134        }
135        Ok(())
136    }
137
138    /// Get a unit type by its ID.
139    pub fn get_unit_type(&self, id: impl Into<UnitTypeID>) -> Option<&UnitType> {
140        let id: UnitTypeID = id.into();
141        self.unit_types
142            .get(usize::from(id))
143            .and_then(Option::as_ref)
144    }
145}