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#[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#[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 pub fn name(&self) -> &str {
68 self.name.as_str()
69 }
70
71 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 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 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}