1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
// texture_converter.rs - Conversion of texture layers between ADT versions
use crate::chunk::*;
use crate::error::Result;
use crate::mcnk_subchunks::*;
use crate::version::AdtVersion;
/// Convert texture layers from one version to another
pub fn convert_texture_layers(
source_layers: &[McnkTextureLayer],
from_version: AdtVersion,
to_version: AdtVersion,
) -> Result<Vec<McnkTextureLayer>> {
match (from_version, to_version) {
// Vanilla to TBC
(AdtVersion::Vanilla, AdtVersion::TBC) => vanilla_to_tbc_layers(source_layers),
// TBC to WotLK
(AdtVersion::TBC, AdtVersion::WotLK) => tbc_to_wotlk_layers(source_layers),
// WotLK to Cataclysm
(AdtVersion::WotLK, AdtVersion::Cataclysm) => wotlk_to_cataclysm_layers(source_layers),
// Cataclysm to MoP
(AdtVersion::Cataclysm, AdtVersion::MoP) => cataclysm_to_mop_layers(source_layers),
// Downgrading
(AdtVersion::TBC, AdtVersion::Vanilla) => tbc_to_vanilla_layers(source_layers),
(AdtVersion::WotLK, AdtVersion::TBC) => wotlk_to_tbc_layers(source_layers),
(AdtVersion::Cataclysm, AdtVersion::WotLK) => cataclysm_to_wotlk_layers(source_layers),
(AdtVersion::MoP, AdtVersion::Cataclysm) => mop_to_cataclysm_layers(source_layers),
// Same version - just clone
(from, to) if from == to => Ok(source_layers.to_vec()),
// Multiple version jumps - handled by main converter
// Just return as-is, they'll be processed step by step
_ => Ok(source_layers.to_vec()),
}
}
/// Convert texture layers from Vanilla to TBC
fn vanilla_to_tbc_layers(source_layers: &[McnkTextureLayer]) -> Result<Vec<McnkTextureLayer>> {
// TBC added support for ADPCM compressed alpha maps
// The flags used in TBC are mostly the same as Vanilla
let mut result = Vec::with_capacity(source_layers.len());
for layer in source_layers {
let new_layer = layer.clone();
// TBC supports compressed alpha maps - for conversion, we'll leave them uncompressed
// Don't set the MCLY_FLAGS_COMPRESSED_ALPHA flag
result.push(new_layer);
}
Ok(result)
}
/// Convert texture layers from TBC to WotLK
fn tbc_to_wotlk_layers(source_layers: &[McnkTextureLayer]) -> Result<Vec<McnkTextureLayer>> {
// WotLK added new animation flags
// MCLY_FLAGS_ANIMATE_FASTER (0x008)
// MCLY_FLAGS_ANIMATE_FASTEST (0x010)
let mut result = Vec::with_capacity(source_layers.len());
for layer in source_layers {
let mut new_layer = layer.clone();
// Check for animation flags and update to the new format if needed
if has_animation_flags(layer.flags) {
// If using animation, add appropriate speed flags based on original animation
// This is a heuristic - we don't know the exact desired speed
let anim_type = layer.flags & 0x7; // Extract animation type (bits 0-2)
if anim_type == MclyFlags::Animate3 as u32 {
// Fast rotation (180°) gets ANIMATE_FASTER flag
new_layer.flags |= MclyFlags::AnimateFaster as u32;
}
}
result.push(new_layer);
}
Ok(result)
}
/// Convert texture layers from WotLK to Cataclysm
fn wotlk_to_cataclysm_layers(source_layers: &[McnkTextureLayer]) -> Result<Vec<McnkTextureLayer>> {
// Cataclysm added MCLY_FLAGS_ANIMATE_USE_OTHER_LAYER (0x040)
// This flag allows reusing animation settings from another layer
let mut result = Vec::with_capacity(source_layers.len());
for layer in source_layers.iter() {
let new_layer = layer.clone();
// For conversion, we don't need to set any new flags
// The existing flags remain valid in Cataclysm
result.push(new_layer);
}
Ok(result)
}
/// Convert texture layers from Cataclysm to MoP
fn cataclysm_to_mop_layers(source_layers: &[McnkTextureLayer]) -> Result<Vec<McnkTextureLayer>> {
// MoP didn't significantly change the texture layer format
// Just clone the layers
Ok(source_layers.to_vec())
}
/// Convert texture layers from TBC to Vanilla
fn tbc_to_vanilla_layers(source_layers: &[McnkTextureLayer]) -> Result<Vec<McnkTextureLayer>> {
// Need to remove any TBC-specific flags
let mut result = Vec::with_capacity(source_layers.len());
for layer in source_layers {
let mut new_layer = layer.clone();
// Remove compressed alpha flag if set
// (MCLY_FLAGS_COMPRESSED_ALPHA = 0x080)
new_layer.flags &= !(MclyFlags::CompressedAlpha as u32);
result.push(new_layer);
}
Ok(result)
}
/// Convert texture layers from WotLK to TBC
fn wotlk_to_tbc_layers(source_layers: &[McnkTextureLayer]) -> Result<Vec<McnkTextureLayer>> {
// Need to remove WotLK-specific flags
let mut result = Vec::with_capacity(source_layers.len());
for layer in source_layers {
let mut new_layer = layer.clone();
// Remove WotLK-specific animation flags
// (MCLY_FLAGS_ANIMATE_FASTER = 0x008, MCLY_FLAGS_ANIMATE_FASTEST = 0x010)
new_layer.flags &=
!((MclyFlags::AnimateFaster as u32) | (MclyFlags::AnimateFastest as u32));
result.push(new_layer);
}
Ok(result)
}
/// Convert texture layers from Cataclysm to WotLK
fn cataclysm_to_wotlk_layers(source_layers: &[McnkTextureLayer]) -> Result<Vec<McnkTextureLayer>> {
// Need to remove Cataclysm-specific flags
let mut result = Vec::with_capacity(source_layers.len());
for layer in source_layers {
let mut new_layer = layer.clone();
// Remove Cataclysm-specific animation flags
// (MCLY_FLAGS_ANIMATE_USE_OTHER_LAYER = 0x040)
new_layer.flags &= !(MclyFlags::AnimateUseOtherLayer as u32);
result.push(new_layer);
}
Ok(result)
}
/// Convert texture layers from MoP to Cataclysm
fn mop_to_cataclysm_layers(source_layers: &[McnkTextureLayer]) -> Result<Vec<McnkTextureLayer>> {
// MoP didn't significantly change the texture layer format
// Just clone the layers
Ok(source_layers.to_vec())
}
/// Check if the layer has any animation flags set
fn has_animation_flags(flags: u32) -> bool {
(flags
& ((MclyFlags::Animate1 as u32)
| (MclyFlags::Animate2 as u32)
| (MclyFlags::Animate3 as u32)))
!= 0
}
/// Convert alpha maps between different versions
pub fn convert_alpha_maps(
source_alpha_maps: &[Vec<u8>],
source_layers: &[McnkTextureLayer],
from_version: AdtVersion,
to_version: AdtVersion,
) -> Result<Vec<Vec<u8>>> {
match (from_version, to_version) {
// WotLK+ uses 64x64 alpha maps (big alpha)
(from, to) if from < AdtVersion::WotLK && to >= AdtVersion::WotLK => {
upgrade_alpha_maps_to_big(source_alpha_maps, source_layers)
}
// Downgrade from WotLK+ to earlier versions
(from, to) if from >= AdtVersion::WotLK && to < AdtVersion::WotLK => {
downgrade_alpha_maps_from_big(source_alpha_maps, source_layers)
}
// Same version format - just clone
_ => Ok(source_alpha_maps.to_vec()),
}
}
/// Upgrade alpha maps from 32x32 to 64x64 (for WotLK+)
fn upgrade_alpha_maps_to_big(
source_alpha_maps: &[Vec<u8>],
source_layers: &[McnkTextureLayer],
) -> Result<Vec<Vec<u8>>> {
let mut result = Vec::with_capacity(source_alpha_maps.len());
for (i, alpha_map) in source_alpha_maps.iter().enumerate() {
// Get the corresponding layer (skip the base layer)
let layer_idx = i + 1;
if layer_idx >= source_layers.len() {
// No corresponding layer, just copy as-is
result.push(alpha_map.clone());
continue;
}
let _layer = &source_layers[layer_idx];
// Check if the alpha map should be expanded from 32x32 to 64x64
if alpha_map.len() == 32 * 32 {
// Create a new 64x64 alpha map
let mut new_alpha = vec![0u8; 64 * 64];
// Scale the 32x32 map to 64x64 using bilinear interpolation
for y in 0..64 {
for x in 0..64 {
// Map from 64x64 to 32x32 coordinates
let src_x = (x as f32 * 31.0 / 63.0) as usize;
let src_y = (y as f32 * 31.0 / 63.0) as usize;
// Get source value
let value = alpha_map[src_y * 32 + src_x];
// Set the new value
new_alpha[y * 64 + x] = value;
}
}
result.push(new_alpha);
} else {
// Unknown size or already 64x64, just copy
result.push(alpha_map.clone());
}
}
Ok(result)
}
/// Downgrade alpha maps from 64x64 to 32x32 (for pre-WotLK)
fn downgrade_alpha_maps_from_big(
source_alpha_maps: &[Vec<u8>],
source_layers: &[McnkTextureLayer],
) -> Result<Vec<Vec<u8>>> {
let mut result = Vec::with_capacity(source_alpha_maps.len());
for (i, alpha_map) in source_alpha_maps.iter().enumerate() {
// Get the corresponding layer (skip the base layer)
let layer_idx = i + 1;
if layer_idx >= source_layers.len() {
// No corresponding layer, just copy as-is
result.push(alpha_map.clone());
continue;
}
let _layer = &source_layers[layer_idx];
// Check if the alpha map should be reduced from 64x64 to 32x32
if alpha_map.len() == 64 * 64 {
// Create a new 32x32 alpha map
let mut new_alpha = vec![0u8; 32 * 32];
// Scale the 64x64 map to 32x32 by averaging 2x2 blocks
for y in 0..32 {
for x in 0..32 {
// Map to 64x64 coordinates
let src_x = x * 2;
let src_y = y * 2;
// Average 2x2 block
let sum = alpha_map[src_y * 64 + src_x] as u32
+ alpha_map[src_y * 64 + src_x + 1] as u32
+ alpha_map[(src_y + 1) * 64 + src_x] as u32
+ alpha_map[(src_y + 1) * 64 + src_x + 1] as u32;
// Set the new value (average of 2x2 block)
new_alpha[y * 32 + x] = (sum / 4) as u8;
}
}
result.push(new_alpha);
} else {
// Unknown size or already 32x32, just copy
result.push(alpha_map.clone());
}
}
Ok(result)
}
/// Convert area IDs between different versions
pub fn convert_area_id(area_id: u32, from_version: AdtVersion, to_version: AdtVersion) -> u32 {
// Area IDs changed significantly between expansions
// This function provides mappings for known area ID changes
// If same version, no conversion needed
if from_version == to_version {
return area_id;
}
// Handle special area ID conversions
match (from_version, to_version, area_id) {
// Examples of area ID changes between Vanilla and TBC
(AdtVersion::Vanilla, AdtVersion::TBC, 0) => 0, // Default
(AdtVersion::Vanilla, AdtVersion::TBC, 1) => 1, // Dun Morogh
(AdtVersion::Vanilla, AdtVersion::TBC, 12) => 12, // Elwynn Forest
(AdtVersion::Vanilla, AdtVersion::TBC, 14) => 14, // Durotar
// Examples of area ID changes between TBC and WotLK
(AdtVersion::TBC, AdtVersion::WotLK, 0) => 0, // Default
(AdtVersion::TBC, AdtVersion::WotLK, 3430) => 3430, // Eversong Woods
(AdtVersion::TBC, AdtVersion::WotLK, 3433) => 3433, // Ghostlands
// Examples of area ID changes between WotLK and Cataclysm
(AdtVersion::WotLK, AdtVersion::Cataclysm, 0) => 0, // Default
(AdtVersion::WotLK, AdtVersion::Cataclysm, 4742) => 4742, // Hrothgar's Landing
// Handle downgrading area IDs
// TBC to Vanilla
(AdtVersion::TBC, AdtVersion::Vanilla, 3430) => 0, // Eversong Woods (didn't exist)
(AdtVersion::TBC, AdtVersion::Vanilla, 3433) => 0, // Ghostlands (didn't exist)
// WotLK to TBC
(AdtVersion::WotLK, AdtVersion::TBC, 4742) => 0, // Hrothgar's Landing (didn't exist)
// Cataclysm to WotLK
(AdtVersion::Cataclysm, AdtVersion::WotLK, 5042) => 0, // Deepholm (didn't exist)
(AdtVersion::Cataclysm, AdtVersion::WotLK, 5095) => 0, // Tol Barad (didn't exist)
// Default case - keep the same ID if no special conversion exists
_ => area_id,
}
}
/// Area ID mapping entry
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct AreaIdMapping {
pub from_version: AdtVersion,
pub to_version: AdtVersion,
pub from_id: u32,
pub to_id: u32,
pub name: String,
}
/// Get a list of known area ID mappings
#[allow(dead_code)]
fn get_area_id_mappings() -> Vec<AreaIdMapping> {
vec![
// Vanilla to TBC
AreaIdMapping {
from_version: AdtVersion::Vanilla,
to_version: AdtVersion::TBC,
from_id: 1,
to_id: 1,
name: "Dun Morogh".to_string(),
},
// TBC to WotLK
AreaIdMapping {
from_version: AdtVersion::TBC,
to_version: AdtVersion::WotLK,
from_id: 3430,
to_id: 3430,
name: "Eversong Woods".to_string(),
},
// Examples of new areas that didn't exist in previous versions
AreaIdMapping {
from_version: AdtVersion::TBC,
to_version: AdtVersion::Vanilla,
from_id: 3430,
to_id: 0,
name: "Eversong Woods (didn't exist in Vanilla)".to_string(),
},
AreaIdMapping {
from_version: AdtVersion::WotLK,
to_version: AdtVersion::TBC,
from_id: 4742,
to_id: 0,
name: "Hrothgar's Landing (didn't exist in TBC)".to_string(),
},
// Add more mappings as needed
]
}