wowlab_parsers/parsers/transform/
global.rs1use wowlab_types::{
2 colors::{argb_to_hex, rgb_to_hex},
3 data::{ClassDataFlat, GlobalColorFlat, GlobalStringFlat},
4};
5
6#[cfg(feature = "dbc")]
7use super::{super::dbc::DbcData, super::errors::TransformError, resolve_icon_file_name};
8
9const DEFAULT_COLOR_COMPONENT: i32 = 255;
10
11pub fn transform_class(dbc: &DbcData, class_id: i32) -> Result<ClassDataFlat, TransformError> {
17 let class = dbc
18 .chr_classes
19 .get(&class_id)
20 .ok_or_else(|| TransformError::class_not_found(class_id))?;
21
22 let color = rgb_to_hex(
23 class.ClassColorR.unwrap_or(DEFAULT_COLOR_COMPONENT),
24 class.ClassColorG.unwrap_or(DEFAULT_COLOR_COMPONENT),
25 class.ClassColorB.unwrap_or(DEFAULT_COLOR_COMPONENT),
26 );
27
28 let file_name = resolve_icon_file_name(dbc, class.IconFileDataID);
29
30 Ok(ClassDataFlat {
31 id: class.ID,
32 name: class.Name_lang.clone().unwrap_or_default().into(),
33 filename: class.Filename.clone().unwrap_or_default().into(),
34 icon_file_id: class.IconFileDataID,
35 file_name: file_name.into(),
36 color: color.into(),
37 spell_class_set: class.SpellClassSet,
38 primary_stat_priority: class.PrimaryStatPriority,
39 roles_mask: class.RolesMask,
40 })
41}
42
43#[must_use]
45pub fn transform_all_classes(dbc: &DbcData) -> Vec<ClassDataFlat> {
46 super::transform_all(dbc.chr_classes.keys().copied(), "class", |class_id| {
47 transform_class(dbc, class_id)
48 })
49}
50
51pub fn transform_global_color(
57 dbc: &DbcData,
58 color_id: i32,
59) -> Result<GlobalColorFlat, TransformError> {
60 let row = dbc
61 .global_color
62 .get(&color_id)
63 .ok_or_else(|| TransformError::global_color_not_found(color_id))?;
64
65 Ok(GlobalColorFlat {
66 id: row.ID,
67 name: row.LuaConstantName.clone().into(),
68 color: argb_to_hex(row.Color).into(),
69 })
70}
71
72#[must_use]
74pub fn transform_all_global_colors(dbc: &DbcData) -> Vec<GlobalColorFlat> {
75 super::transform_all(
76 dbc.global_color.keys().copied(),
77 "global color",
78 |color_id| transform_global_color(dbc, color_id),
79 )
80}
81
82pub fn transform_global_string(
88 dbc: &DbcData,
89 string_id: i32,
90) -> Result<GlobalStringFlat, TransformError> {
91 let row = dbc
92 .global_strings
93 .get(&string_id)
94 .ok_or_else(|| TransformError::global_string_not_found(string_id))?;
95
96 Ok(GlobalStringFlat {
97 id: row.ID,
98 tag: row.BaseTag.clone().into(),
99 value: row.TagText_lang.clone().unwrap_or_default(),
100 flags: row.Flags,
101 })
102}
103
104#[must_use]
106pub fn transform_all_global_strings(dbc: &DbcData) -> Vec<GlobalStringFlat> {
107 super::transform_all(
108 dbc.global_strings.keys().copied(),
109 "global string",
110 |string_id| transform_global_string(dbc, string_id),
111 )
112}
113
114#[cfg(test)]
115mod tests {
116 use googletest::prelude::*;
117
118 use super::*;
119 use crate::parsers::{
120 dbc::rows::{ChrClassesRow, GlobalColorRow, GlobalStringsRow, ManifestInterfaceDataRow},
121 transform::fixtures::{class_row, dbc},
122 };
123
124 #[gtest]
125 fn transform_class_missing_errors() -> Result<()> {
126 let dbc = dbc();
127
128 verify_that!(
129 transform_class(&dbc, 999).unwrap_err().to_string(),
130 eq("Class 999 not found in DBC cache")
131 )
132 }
133
134 #[gtest]
135 fn transform_class_color_and_identity() -> Result<()> {
136 let mut dbc = dbc();
137
138 dbc.chr_classes.insert(
139 4,
140 ChrClassesRow {
141 Name_lang: Some("Rogue".to_string()),
142 SpellClassSet: 8,
143 ClassColorR: Some(255),
144 ClassColorG: Some(0),
145 ClassColorB: Some(0),
146 ..class_row(4)
147 },
148 );
149
150 let flat = transform_class(&dbc, 4)?;
151
152 verify_that!(flat.id, eq(4))?;
153 verify_that!(flat.name.as_str(), eq("Rogue"))?;
154 verify_that!(flat.spell_class_set, eq(8))?;
155
156 verify_that!(flat.color.as_str(), eq(rgb_to_hex(255, 0, 0).as_str()))
157 }
158
159 #[gtest]
160 fn transform_class_color_defaults_to_white() -> Result<()> {
161 let mut dbc = dbc();
162
163 dbc.chr_classes.insert(1, class_row(1));
164
165 let flat = transform_class(&dbc, 1)?;
166
167 verify_that!(flat.color.as_str(), eq(rgb_to_hex(255, 255, 255).as_str()))
168 }
169
170 #[gtest]
171 fn transform_class_icon_fallback_when_non_positive() -> Result<()> {
172 let mut dbc = dbc();
173
174 dbc.chr_classes.insert(
175 1,
176 ChrClassesRow {
177 IconFileDataID: 0,
178 ..class_row(1)
179 },
180 );
181
182 let flat = transform_class(&dbc, 1)?;
183
184 verify_that!(flat.file_name.as_str(), eq("inv_misc_questionmark"))
185 }
186
187 #[gtest]
188 fn transform_class_icon_resolved_from_manifest() -> Result<()> {
189 let mut dbc = dbc();
190
191 dbc.chr_classes.insert(
192 1,
193 ChrClassesRow {
194 IconFileDataID: 500,
195 ..class_row(1)
196 },
197 );
198 dbc.manifest_interface_data.insert(
199 500,
200 ManifestInterfaceDataRow {
201 ID: 500,
202 FilePath: "Interface/Icons/".to_string(),
203 FileName: "ClassIcon_Rogue.BLP".to_string(),
204 },
205 );
206
207 let flat = transform_class(&dbc, 1)?;
208
209 verify_that!(flat.file_name.as_str(), eq("classicon_rogue"))
210 }
211
212 #[gtest]
213 fn transform_global_color_missing_errors() -> Result<()> {
214 let dbc = dbc();
215
216 verify_that!(
217 transform_global_color(&dbc, 7).unwrap_err().to_string(),
218 eq("Global color 7 not found in DBC cache")
219 )
220 }
221
222 #[gtest]
223 fn transform_global_color_known_answer() -> Result<()> {
224 let mut dbc = dbc();
225 let raw = i32::from_ne_bytes(0xFFA3_35EE_u32.to_ne_bytes());
226
227 dbc.global_color.insert(
228 7,
229 GlobalColorRow {
230 ID: 7,
231 LuaConstantName: "EPIC".to_string(),
232 Color: raw,
233 },
234 );
235
236 let flat = transform_global_color(&dbc, 7)?;
237
238 verify_that!(flat.name.as_str(), eq("EPIC"))?;
239
240 verify_that!(flat.color.as_str(), eq(argb_to_hex(raw).as_str()))
241 }
242
243 #[gtest]
244 fn transform_global_string_missing_errors() -> Result<()> {
245 let dbc = dbc();
246
247 verify_that!(
248 transform_global_string(&dbc, 3).unwrap_err().to_string(),
249 eq("Global string 3 not found in DBC cache")
250 )
251 }
252
253 #[gtest]
254 fn transform_global_string_present_and_empty_value() -> Result<()> {
255 let mut dbc = dbc();
256
257 dbc.global_strings.insert(
258 3,
259 GlobalStringsRow {
260 ID: 3,
261 BaseTag: "SPELL_FAILED".to_string(),
262 TagText_lang: Some("Not ready yet.".to_string()),
263 Flags: 5,
264 },
265 );
266 dbc.global_strings.insert(
267 4,
268 GlobalStringsRow {
269 ID: 4,
270 BaseTag: "EMPTY".to_string(),
271 TagText_lang: None,
272 Flags: 0,
273 },
274 );
275
276 let present = transform_global_string(&dbc, 3)?;
277
278 verify_that!(present.tag.as_str(), eq("SPELL_FAILED"))?;
279 verify_that!(present.value.as_str(), eq("Not ready yet."))?;
280 verify_that!(present.flags, eq(5))?;
281
282 let empty = transform_global_string(&dbc, 4)?;
283
284 verify_that!(empty.value.as_str(), eq(""))
285 }
286}