use mercs2_formats::crc32::crc32_mercs2;
const MTRL_PRE: usize = 104;
fn rd_u32(b: &[u8], o: usize) -> u32 {
u32::from_le_bytes([b[o], b[o + 1], b[o + 2], b[o + 3]])
}
fn find_chunk(container: &[u8], tag: &[u8; 4]) -> Option<(usize, usize)> {
if container.len() < 20 || &container[0..4] != b"UCFX" {
return None;
}
let data_base = rd_u32(container, 4) as usize;
let n_desc = rd_u32(container, 16) as usize;
for d in 0..n_desc {
let off = 20 + d * 20;
if off + 20 > container.len() {
break;
}
if &container[off..off + 4] == tag {
let row_u0 = rd_u32(container, off + 4) as usize;
let body_size = rd_u32(container, off + 8) as usize;
return Some((data_base + row_u0, body_size));
}
}
None
}
pub fn repoint_container_textures(
container: &mut Vec<u8>,
remap: &std::collections::HashMap<u32, u32>,
) -> Result<usize, String> {
let (mabs, msize) = find_chunk(container, b"MTRL").ok_or("no MTRL chunk in container")?;
if mabs + msize > container.len() {
return Err("MTRL body out of range".into());
}
let mut changed = 0usize;
let mut off = 0usize;
loop {
let cp = mabs + off + MTRL_PRE; if cp + 4 > mabs + msize {
break;
}
let count = u16::from_le_bytes([container[cp + 2], container[cp + 3]]) as usize;
if count == 0 || count > 10 {
break; }
for k in 0..count {
let slot = cp + 4 + k * 4;
if slot + 4 > mabs + msize {
break;
}
let h = rd_u32(container, slot);
if let Some(&new) = remap.get(&h) {
container[slot..slot + 4].copy_from_slice(&new.to_le_bytes());
changed += 1;
}
}
off += 116 + count * 4;
}
if changed > 0 {
let n = container.len();
if n < 8 || &container[n - 8..n - 4] != b"CSUM" {
return Err("container missing CSUM trailer".into());
}
let csum = crc32_mercs2(&container[..n - 8]);
container[n - 4..n].copy_from_slice(&csum.to_le_bytes());
}
Ok(changed)
}
pub fn fix_container_mtrl(container: &mut Vec<u8>) -> Result<usize, String> {
let (mabs, msize) = find_chunk(container, b"MTRL").ok_or("no MTRL chunk in container")?;
if mabs + msize > container.len() {
return Err("MTRL body out of range".into());
}
let mut fixed = 0usize;
let mut off = 0usize;
loop {
let cp = mabs + off + MTRL_PRE; if cp + 4 > mabs + msize {
break;
}
let lo = u16::from_le_bytes([container[cp], container[cp + 1]]); let hi = u16::from_le_bytes([container[cp + 2], container[cp + 3]]); let count = if (1..=10).contains(&hi) {
hi } else if (1..=10).contains(&lo) {
let b = [container[cp], container[cp + 1], container[cp + 2], container[cp + 3]];
container[cp] = b[2];
container[cp + 1] = b[3];
container[cp + 2] = b[0];
container[cp + 3] = b[1];
fixed += 1;
lo
} else {
break; };
off += 116 + count as usize * 4;
}
if fixed > 0 {
let n = container.len();
if n < 8 || &container[n - 8..n - 4] != b"CSUM" {
return Err("container missing CSUM trailer".into());
}
let csum = crc32_mercs2(&container[..n - 8]);
container[n - 4..n].copy_from_slice(&csum.to_le_bytes());
}
Ok(fixed)
}