wow_wmo/
version.rs

1/// WMO format versions corresponding to different WoW expansions/patches
2/// Based on empirical analysis: WMO version remains 17 across all analyzed versions (1.12.1-5.4.8)
3/// Features are differentiated by chunk presence rather than version numbers
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
5pub enum WmoVersion {
6    /// Classic/Vanilla (1.12.1) - Version 17, core chunks only
7    Classic,
8
9    /// The Burning Crusade (2.4.3) - Version 17, improved lighting
10    Tbc,
11
12    /// Wrath of the Lich King (3.3.5a) - Version 17, skybox support
13    Wotlk,
14
15    /// Cataclysm (4.3.4) - Version 17 + MCVP chunk for transport WMOs
16    Cataclysm,
17
18    /// Mists of Pandaria (5.4.8) - Version 17 + MCVP chunk, group MOCV support
19    Mop,
20
21    /// Warlords of Draenor (6.x) - Theoretical post-MoP versions
22    Wod,
23
24    /// Legion (7.x)
25    Legion,
26
27    /// Battle for Azeroth (8.x)
28    Bfa,
29
30    /// Shadowlands (9.x)
31    Shadowlands,
32
33    /// Dragonflight (10.x)
34    Dragonflight,
35
36    /// The War Within (11.x)
37    WarWithin,
38}
39
40impl WmoVersion {
41    /// Convert a raw version number to a WmoVersion
42    /// Based on empirical analysis: version 17 spans Classic through MoP
43    /// Features are determined by expansion context and chunk presence
44    pub fn from_raw(raw: u32) -> Option<Self> {
45        match raw {
46            17 => Some(Self::Classic), // Default to Classic for version 17
47            18 => Some(Self::Wod),
48            19 => Some(Self::Legion),
49            20 => Some(Self::Bfa),
50            21 => Some(Self::Shadowlands),
51            22 => Some(Self::Dragonflight),
52            23 => Some(Self::WarWithin),
53            _ => None,
54        }
55    }
56
57    /// Convert a raw version number to WmoVersion with expansion context
58    /// Use this when you know the WoW expansion to get accurate feature detection
59    pub fn from_raw_with_expansion(raw: u32, expansion: &str) -> Option<Self> {
60        match (raw, expansion.to_lowercase().as_str()) {
61            (17, exp) if exp.contains("vanilla") || exp.contains("classic") => Some(Self::Classic),
62            (17, exp) if exp.contains("tbc") || exp.contains("burning") => Some(Self::Tbc),
63            (17, exp) if exp.contains("wotlk") || exp.contains("wrath") => Some(Self::Wotlk),
64            (17, exp) if exp.contains("cata") || exp.contains("cataclysm") => Some(Self::Cataclysm),
65            (17, exp) if exp.contains("mop") || exp.contains("pandaria") => Some(Self::Mop),
66            (17, _) => Some(Self::Classic), // Default to Classic for unknown v17
67            _ => Self::from_raw(raw),       // Delegate to standard method for other versions
68        }
69    }
70
71    /// Get the raw version number used in WMO files
72    /// Returns the actual version number found in WMO headers
73    pub fn to_raw(self) -> u32 {
74        match self {
75            // Empirically verified: all these use version 17
76            Self::Classic | Self::Tbc | Self::Wotlk | Self::Cataclysm | Self::Mop => 17,
77            // Theoretical post-MoP versions
78            Self::Wod => 18,
79            Self::Legion => 19,
80            Self::Bfa => 20,
81            Self::Shadowlands => 21,
82            Self::Dragonflight => 22,
83            Self::WarWithin => 23,
84        }
85    }
86
87    /// Get the expansion name as a string
88    pub fn expansion_name(self) -> &'static str {
89        match self {
90            Self::Classic => "Classic/Vanilla",
91            Self::Tbc => "The Burning Crusade",
92            Self::Wotlk => "Wrath of the Lich King",
93            Self::Cataclysm => "Cataclysm",
94            Self::Mop => "Mists of Pandaria",
95            Self::Wod => "Warlords of Draenor",
96            Self::Legion => "Legion",
97            Self::Bfa => "Battle for Azeroth",
98            Self::Shadowlands => "Shadowlands",
99            Self::Dragonflight => "Dragonflight",
100            Self::WarWithin => "The War Within",
101        }
102    }
103
104    /// Get the minimum supported version
105    pub fn min_supported() -> Self {
106        Self::Classic
107    }
108
109    /// Get the maximum supported version
110    pub fn max_supported() -> Self {
111        Self::WarWithin
112    }
113
114    /// Check if this version supports a particular feature
115    pub fn supports_feature(self, feature: WmoFeature) -> bool {
116        self >= feature.min_version()
117    }
118
119    /// Parse version from expansion short names
120    /// Supports short names like "WotLK", "TBC", "Classic", etc.
121    pub fn from_expansion_name(s: &str) -> Option<Self> {
122        match s.to_lowercase().as_str() {
123            "vanilla" | "classic" => Some(Self::Classic),
124            "tbc" | "bc" | "burningcrusade" | "burning_crusade" => Some(Self::Tbc),
125            "wotlk" | "wrath" | "lichking" | "lich_king" | "wlk" => Some(Self::Wotlk),
126            "cata" | "cataclysm" => Some(Self::Cataclysm),
127            "mop" | "pandaria" | "mists" | "mists_of_pandaria" => Some(Self::Mop),
128            "wod" | "draenor" | "warlords" | "warlords_of_draenor" => Some(Self::Wod),
129            "legion" => Some(Self::Legion),
130            "bfa" | "bfazeroth" | "battle_for_azeroth" | "battleforazeroth" => Some(Self::Bfa),
131            "sl" | "shadowlands" => Some(Self::Shadowlands),
132            "df" | "dragonflight" => Some(Self::Dragonflight),
133            "tww" | "warwithin" | "the_war_within" | "thewarwithin" => Some(Self::WarWithin),
134            _ => None,
135        }
136    }
137}
138
139/// Features introduced in different WMO versions
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub enum WmoFeature {
142    /// Base WMO features (available in all versions)
143    Base,
144
145    /// Improved lighting introduced in TBC
146    ImprovedLighting,
147
148    /// Skybox references introduced in WotLK
149    SkyboxReferences,
150
151    /// Destructible objects introduced in Cataclysm
152    DestructibleObjects,
153
154    /// Convex volume planes (MCVP) introduced in Cataclysm for transport WMOs
155    ConvexVolumePlanes,
156
157    /// Extended materials introduced in MoP
158    ExtendedMaterials,
159
160    /// Liquid data v2 introduced in WoD
161    LiquidV2,
162
163    /// Global ambient color introduced in Legion
164    GlobalAmbientColor,
165
166    /// Particle systems introduced in BfA
167    ParticleSystems,
168
169    /// Shadow batches introduced in Shadowlands
170    ShadowBatches,
171
172    /// Ray-traced shadows introduced in Dragonflight
173    RayTracedShadows,
174
175    /// Enhanced materials introduced in The War Within
176    EnhancedMaterials,
177}
178
179impl WmoFeature {
180    /// Get the minimum version that supports this feature
181    pub fn min_version(self) -> WmoVersion {
182        match self {
183            Self::Base => WmoVersion::Classic,
184            Self::ImprovedLighting => WmoVersion::Tbc,
185            Self::SkyboxReferences => WmoVersion::Wotlk,
186            Self::DestructibleObjects => WmoVersion::Cataclysm,
187            Self::ConvexVolumePlanes => WmoVersion::Cataclysm,
188            Self::ExtendedMaterials => WmoVersion::Mop,
189            Self::LiquidV2 => WmoVersion::Wod,
190            Self::GlobalAmbientColor => WmoVersion::Legion,
191            Self::ParticleSystems => WmoVersion::Bfa,
192            Self::ShadowBatches => WmoVersion::Shadowlands,
193            Self::RayTracedShadows => WmoVersion::Dragonflight,
194            Self::EnhancedMaterials => WmoVersion::WarWithin,
195        }
196    }
197}