1use crate::error::{Error, Result};
4use std::fmt;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
8pub enum WowVersion {
9 Classic,
11 TBC,
13 WotLK,
15 Cataclysm,
17 MoP,
19 WoD,
21 Legion,
23 BfA,
25 Shadowlands,
27 Dragonflight,
29}
30
31impl WowVersion {
32 pub fn from_string(s: &str) -> Result<Self> {
34 let parts: Vec<&str> = s.split('.').collect();
35 if parts.is_empty() {
36 return Err(Error::ValidationError(format!(
37 "Invalid version string: {s}"
38 )));
39 }
40
41 let major = parts[0]
42 .parse::<u32>()
43 .map_err(|_| Error::ValidationError(format!("Invalid major version: {}", parts[0])))?;
44
45 Ok(match major {
46 1 => WowVersion::Classic,
47 2 => WowVersion::TBC,
48 3 => WowVersion::WotLK,
49 4 => WowVersion::Cataclysm,
50 5 => WowVersion::MoP,
51 6 => WowVersion::WoD,
52 7 => WowVersion::Legion,
53 8 => WowVersion::BfA,
54 9 => WowVersion::Shadowlands,
55 10 => WowVersion::Dragonflight,
56 _ => {
57 return Err(Error::ValidationError(format!(
58 "Unknown WoW version: {major}"
59 )));
60 }
61 })
62 }
63
64 pub fn from_expansion_name(s: &str) -> Result<Self> {
67 match s.to_lowercase().as_str() {
69 "vanilla" | "classic" => Ok(WowVersion::Classic),
70 "tbc" | "bc" | "burningcrusade" | "burning_crusade" => Ok(WowVersion::TBC),
71 "wotlk" | "wrath" | "lichking" | "lich_king" | "wlk" => Ok(WowVersion::WotLK),
72 "cata" | "cataclysm" => Ok(WowVersion::Cataclysm),
73 "mop" | "pandaria" | "mists" | "mists_of_pandaria" => Ok(WowVersion::MoP),
74 "wod" | "draenor" | "warlords" | "warlords_of_draenor" => Ok(WowVersion::WoD),
75 "legion" => Ok(WowVersion::Legion),
76 "bfa" | "bfazeroth" | "battle_for_azeroth" | "battleforazeroth" => Ok(WowVersion::BfA),
77 "sl" | "shadowlands" => Ok(WowVersion::Shadowlands),
78 "df" | "dragonflight" => Ok(WowVersion::Dragonflight),
79 _ => {
80 Self::from_string(s)
82 }
83 }
84 }
85
86 pub fn has_terrain_mwmo(&self) -> bool {
88 *self < WowVersion::Cataclysm
90 }
91
92 pub fn has_maid_chunk(&self) -> bool {
94 *self >= WowVersion::BfA
96 }
97
98 pub fn has_auxiliary_files(&self) -> bool {
100 *self >= WowVersion::Legion
102 }
103
104 pub fn expected_modf_scale(&self) -> u16 {
106 match self {
107 WowVersion::Classic | WowVersion::TBC | WowVersion::WotLK => 0,
109 _ => 1024,
111 }
112 }
113
114 pub fn expected_modf_unique_id(&self) -> u32 {
116 match self {
117 WowVersion::Classic | WowVersion::TBC | WowVersion::WotLK => 0xFFFFFFFF,
119 _ => 0,
121 }
122 }
123
124 pub fn is_flag_common(&self, flag: u32) -> bool {
126 match flag {
127 0x0001 => true, 0x0002 => *self >= WowVersion::WotLK, 0x0004 => *self >= WowVersion::WotLK, 0x0008 => *self >= WowVersion::WotLK, 0x0010 => *self >= WowVersion::WotLK && *self < WowVersion::BfA, 0x0040 => *self >= WowVersion::Cataclysm, 0x0080 => *self >= WowVersion::MoP, 0x0200 => *self >= WowVersion::BfA, _ => false,
136 }
137 }
138
139 pub fn name(&self) -> &'static str {
141 match self {
142 WowVersion::Classic => "Classic",
143 WowVersion::TBC => "The Burning Crusade",
144 WowVersion::WotLK => "Wrath of the Lich King",
145 WowVersion::Cataclysm => "Cataclysm",
146 WowVersion::MoP => "Mists of Pandaria",
147 WowVersion::WoD => "Warlords of Draenor",
148 WowVersion::Legion => "Legion",
149 WowVersion::BfA => "Battle for Azeroth",
150 WowVersion::Shadowlands => "Shadowlands",
151 WowVersion::Dragonflight => "Dragonflight",
152 }
153 }
154}
155
156impl fmt::Display for WowVersion {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 write!(f, "{}", self.name())
159 }
160}
161
162#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct VersionConfig {
165 pub version: WowVersion,
166}
167
168impl VersionConfig {
169 pub fn new(version: WowVersion) -> Self {
171 Self { version }
172 }
173
174 pub fn validate_mphd_flags(&self, flags: u32) -> Vec<String> {
176 let mut warnings = Vec::new();
177
178 if (flags & 0x0200) != 0 && !self.version.has_maid_chunk() {
180 warnings.push(format!(
181 "Flag 0x0200 (MAID) present but not supported in {}",
182 self.version
183 ));
184 }
185
186 if (flags & 0x0040) != 0 && self.version < WowVersion::Cataclysm {
187 warnings.push("Flag 0x0040 present but not expected before Cataclysm".to_string());
188 }
189
190 if (flags & 0x0080) != 0 && self.version < WowVersion::MoP {
191 warnings.push(
192 "Flag 0x0080 (height texturing) present but not active before MoP".to_string(),
193 );
194 }
195
196 warnings
197 }
198
199 pub fn should_have_chunk(&self, chunk: &str, is_wmo_only: bool) -> bool {
201 match chunk {
202 "MVER" | "MPHD" | "MAIN" => true, "MWMO" => {
204 is_wmo_only || self.version.has_terrain_mwmo()
207 }
208 "MODF" => is_wmo_only, "MAID" => self.version.has_maid_chunk(), _ => false,
211 }
212 }
213}