use mercs2_formats::crc32::crc32_mercs2;
fn rd_u32(b: &[u8], o: usize) -> u32 {
u32::from_le_bytes([b[o], b[o + 1], b[o + 2], b[o + 3]])
}
fn is_f16_one(b: &[u8], o: usize) -> bool {
o + 2 <= b.len() && b[o] == 0x00 && b[o + 1] == 0x3C
}
pub fn fix_container_vertices(container: &mut Vec<u8>) -> Result<usize, String> {
if container.len() < 20 || &container[0..4] != b"UCFX" {
return Err("not a UCFX container".into());
}
let data_base = rd_u32(container, 4) as usize;
let n_desc = rd_u32(container, 16) as usize;
struct Desc {
tag: [u8; 4],
row_u0: u32,
body_size: u32,
}
let mut descs = Vec::with_capacity(n_desc);
for d in 0..n_desc {
let o = 20 + d * 20;
if o + 20 > container.len() {
break;
}
let mut tag = [0u8; 4];
tag.copy_from_slice(&container[o..o + 4]);
descs.push(Desc { tag, row_u0: rd_u32(container, o + 4), body_size: rd_u32(container, o + 8) });
}
let mut fixes: Vec<(usize, usize, usize)> = Vec::new(); let mut i = 0;
while i < descs.len() {
if &descs[i].tag != b"STRM" || descs[i].row_u0 != 0xFFFF_FFFF {
i += 1;
continue;
}
let mut stride: Option<usize> = None;
let mut data: Option<(usize, usize)> = None;
let mut j = i + 1;
while j < descs.len() && descs[j].row_u0 != 0xFFFF_FFFF {
let start = data_base + descs[j].row_u0 as usize;
let size = descs[j].body_size as usize;
match &descs[j].tag {
b"info" if size >= 12 && start + 12 <= container.len() => {
stride = Some(rd_u32(container, start + 4) as usize);
}
b"data" if start + size <= container.len() => data = Some((start, size)),
_ => {}
}
j += 1;
}
i = j;
if let (Some(s), Some((doff, dsz))) = (stride, data) {
if (6..=256).contains(&s) && dsz >= s {
fixes.push((doff, s, dsz / s));
}
}
}
let mut fixed = 0usize;
for (doff, stride, n_verts) in fixes {
if n_verts == 0 {
continue;
}
let v0 = doff;
if is_f16_one(container, v0 + 6) {
continue; }
if !is_f16_one(container, v0 + 4) {
continue; }
for v in 0..n_verts {
let p = doff + v * stride;
if p + 8 > container.len() {
break;
}
container.swap(p, p + 2);
container.swap(p + 1, p + 3);
container.swap(p + 4, p + 6);
container.swap(p + 5, p + 7);
}
fixed += 1;
}
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)
}