use crate::scene::Vec3;
use crate::{Error, Result};
use std::collections::HashMap;
const MAX_POINTS: usize = 5_000_000;
fn error(message: impl Into<String>) -> Error {
message.into().into()
}
struct Bytes<'a> {
data: &'a [u8],
offset: usize,
}
impl<'a> Bytes<'a> {
fn take(&mut self, n: usize) -> Result<&'a [u8]> {
let end = self
.offset
.checked_add(n)
.ok_or_else(|| error("LEPCC offset overflow"))?;
let value = self
.data
.get(self.offset..end)
.ok_or_else(|| error("truncated LEPCC record"))?;
self.offset = end;
Ok(value)
}
fn u8(&mut self) -> Result<u8> {
Ok(self.take(1)?[0])
}
fn u16(&mut self) -> Result<u16> {
Ok(u16::from_le_bytes(self.take(2)?.try_into()?))
}
fn u32(&mut self) -> Result<u32> {
Ok(u32::from_le_bytes(self.take(4)?.try_into()?))
}
fn u64(&mut self) -> Result<u64> {
Ok(u64::from_le_bytes(self.take(8)?.try_into()?))
}
fn f64(&mut self) -> Result<f64> {
let n = f64::from_le_bytes(self.take(8)?.try_into()?);
if !n.is_finite() {
return Err(error("non-finite LEPCC header"));
}
Ok(n)
}
fn vector(&mut self) -> Result<Vec3> {
Ok(Vec3::new(self.f64()?, self.f64()?, self.f64()?))
}
fn done(&self) -> Result<()> {
if self.offset != self.data.len() {
return Err(error("trailing bytes after LEPCC payload"));
}
Ok(())
}
}
fn checksum(data: &[u8]) -> u32 {
let mut one = 0xffff_u32;
let mut two = 0xffff_u32;
for block in data.chunks(718) {
for word in block.chunks(2) {
one += (u32::from(word[0]) << 8) + u32::from(word.get(1).copied().unwrap_or(0));
two += one;
}
one = (one & 0xffff) + (one >> 16);
two = (two & 0xffff) + (two >> 16);
}
one = (one & 0xffff) + (one >> 16);
two = (two & 0xffff) + (two >> 16);
(two << 16) | one
}
fn header<'a>(data: &'a [u8], signature: &[u8; 10]) -> Result<Bytes<'a>> {
let mut reader = Bytes { data, offset: 0 };
if reader.take(10)? != signature || reader.u16()? != 1 {
return Err(error("unsupported LEPCC signature/version"));
}
let expected = reader.u32()?;
let size = reader.u64()?;
if size != data.len() as u64 {
return Err(error("LEPCC blob size differs from its data"));
}
if checksum(&data[16..]) != expected {
return Err(error("LEPCC Fletcher32 checksum mismatch"));
}
Ok(reader)
}
fn packed(reader: &mut Bytes<'_>, count: usize, bits: usize) -> Result<Vec<u32>> {
if count > MAX_POINTS || bits > 32 {
return Err(error("LEPCC packed-array limit exceeded"));
}
let bit_count = count
.checked_mul(bits)
.ok_or_else(|| error("LEPCC packed count overflow"))?;
let bytes = reader.take(bit_count.div_ceil(8))?;
let mut values = Vec::with_capacity(count);
for index in 0..count {
let mut value = 0_u32;
for bit in 0..bits {
let position = index * bits + bit;
value |= u32::from((bytes[position / 8] >> (position % 8)) & 1) << bit;
}
values.push(value);
}
Ok(values)
}
fn bitstuff(reader: &mut Bytes<'_>) -> Result<Vec<u32>> {
let flags = reader.u8()?;
let width = match flags >> 6 {
0 => 4,
n => 3 - n as usize,
};
if width == 0 {
return Err(error("invalid LEPCC count width"));
}
let mut count_bytes = [0; 4];
count_bytes[..width].copy_from_slice(reader.take(width)?);
let count = u32::from_le_bytes(count_bytes) as usize;
let bits = (flags & 31) as usize;
if flags & 32 == 0 {
return packed(reader, count, bits);
}
let n = reader
.u8()?
.checked_sub(1)
.ok_or_else(|| error("invalid LEPCC lookup size"))? as usize;
let mut table = vec![0];
table.extend(packed(reader, n, bits)?);
let index_bits = n.bit_width() as usize;
packed(reader, count, index_bits)?
.into_iter()
.map(|i| {
table
.get(i as usize)
.copied()
.ok_or_else(|| error("LEPCC lookup index out of bounds"))
})
.collect()
}
fn segments(reader: &mut Bytes<'_>) -> Result<Vec<u32>> {
let minimums = bitstuff(reader)?;
let mut data = Vec::new();
for minimum in minimums {
let values = bitstuff(reader)?;
if data
.len()
.checked_add(values.len())
.is_none_or(|n| n > MAX_POINTS)
{
return Err(error("LEPCC segmented-array limit exceeded"));
}
for value in values {
data.push(
value
.checked_add(minimum)
.ok_or_else(|| error("LEPCC segment value overflow"))?,
);
}
}
Ok(data)
}
#[allow(
clippy::cast_precision_loss,
reason = "Stored integer coordinate deltas intentionally become f64 positions when applying the quantization/error scale."
)]
pub(super) fn xyz(data: &[u8]) -> Result<Vec<Vec3>> {
let mut reader = header(data, b"LEPCC ")?;
let minimum = reader.vector()?;
let maximum = reader.vector()?;
let error_bound = reader.vector()?;
let count = reader.u32()? as usize;
reader.u32()?;
if count == 0
|| count > MAX_POINTS
|| minimum.x > maximum.x
|| minimum.y > maximum.y
|| minimum.z > maximum.z
|| error_bound.x <= 0.
|| error_bound.y <= 0.
|| error_bound.z <= 0.
{
return Err(error("invalid LEPCC point count, extent or error bounds"));
}
let ys = segments(&mut reader)?;
let rows = segments(&mut reader)?;
let xs = segments(&mut reader)?;
let zs = segments(&mut reader)?;
if ys.len() != rows.len()
|| xs.len() != count
|| zs.len() != count
|| rows.iter().map(|&n| u64::from(n)).sum::<u64>() != count as u64
{
return Err(error("LEPCC row/coordinate counts differ from header"));
}
reader.done()?;
let mut points = Vec::with_capacity(count);
let mut y = 0_u64;
let mut index = 0;
for (dy, n) in ys.into_iter().zip(rows) {
y = y
.checked_add(u64::from(dy))
.ok_or_else(|| error("LEPCC Y delta overflow"))?;
let mut x = 0_u64;
for _ in 0..n {
x = x
.checked_add(u64::from(xs[index]))
.ok_or_else(|| error("LEPCC X delta overflow"))?;
let point = Vec3::new(
(minimum.x + x as f64 * 2. * error_bound.x).min(maximum.x),
(minimum.y + y as f64 * 2. * error_bound.y).min(maximum.y),
(minimum.z + f64::from(zs[index]) * 2. * error_bound.z).min(maximum.z),
);
if !point.is_finite() {
return Err(error("LEPCC reconstructed point overflow"));
}
points.push(point);
index += 1;
}
}
Ok(points)
}
struct WordBits<'a> {
data: &'a [u8],
position: usize,
}
impl WordBits<'_> {
fn read(&mut self, n: usize) -> Result<u32> {
if n > 32 {
return Err(error("LEPCC Huffman code exceeds 32 bits"));
}
let mut value = 0;
for _ in 0..n {
let index = self.position / 32 * 4;
let bit = self.position % 32;
let word = u32::from_le_bytes(
self.data
.get(index..index + 4)
.ok_or_else(|| error("truncated LEPCC Huffman word"))?
.try_into()?,
);
value = (value << 1) | ((word >> (31 - bit)) & 1);
self.position += 1;
}
Ok(value)
}
fn consumed(&self) -> usize {
self.position.div_ceil(32) * 4
}
}
fn huffman(reader: &mut Bytes<'_>, count: usize) -> Result<Vec<usize>> {
let version = reader.u32()?;
let size = reader.u32()? as usize;
let first = reader.u32()? as usize;
let last = reader.u32()? as usize;
if version < 2 || size == 0 || size > 256 || first >= last || last > size * 2 || first >= size {
return Err(error("invalid LEPCC Huffman table header"));
}
let lengths = bitstuff(reader)?;
if lengths.len() != last - first {
return Err(error("LEPCC Huffman code-length count mismatch"));
}
let mut bits = WordBits {
data: &reader.data[reader.offset..],
position: 0,
};
let mut codes = HashMap::new();
let mut max_length = 0;
for (index, length) in (first..last).zip(lengths) {
if length == 0 {
continue;
}
let length = length as usize;
let code = bits.read(length)?;
if codes.insert((length, code), index % size).is_some() {
return Err(error("duplicate LEPCC Huffman code"));
}
max_length = max_length.max(length);
}
for &(length, code) in codes.keys() {
for prefix in 1..length {
if codes.contains_key(&(prefix, code >> (length - prefix))) {
return Err(error("LEPCC Huffman prefix conflict"));
}
}
}
let table_size = bits.consumed();
reader.take(table_size)?;
let mut bits = WordBits {
data: &reader.data[reader.offset..],
position: 0,
};
let mut values = Vec::with_capacity(count);
for _ in 0..count {
let mut code = 0;
let mut found = None;
for length in 1..=max_length {
code = (code << 1) | bits.read(1)?;
if let Some(&symbol) = codes.get(&(length, code)) {
found = Some(symbol);
break;
}
}
values.push(found.ok_or_else(|| error("invalid LEPCC Huffman symbol"))?);
}
let stream_size = bits
.consumed()
.checked_add(4)
.ok_or_else(|| error("LEPCC Huffman stream overflow"))?;
reader.take(stream_size)?;
Ok(values)
}
pub(super) fn rgb(data: &[u8], expected: usize) -> Result<Vec<[u8; 3]>> {
let mut reader = header(data, b"ClusterRGB")?;
let count = reader.u32()? as usize;
let palette_size = reader.u16()? as usize;
let lookup = reader.u8()?;
let method = reader.u8()?;
if count != expected
|| count > MAX_POINTS
|| palette_size > 256
|| lookup > 2
|| (palette_size == 0 && (lookup != 0 || method != 0))
|| (palette_size > 0 && lookup == 0)
{
return Err(error("LEPCC RGB count/palette is invalid"));
}
let mut colors = Vec::with_capacity(count);
if palette_size == 0 {
for bytes in reader.take(count * 3)?.as_chunks::<3>().0 {
colors.push(*bytes);
}
} else {
let palette = reader.take(palette_size * 3)?.as_chunks::<3>().0.to_vec();
let indices = match method {
0 => reader.take(count)?.iter().map(|&n| n as usize).collect(),
1 if palette_size == 1 => vec![0; count],
2 => huffman(&mut reader, count)?,
_ => return Err(error("unsupported LEPCC RGB palette compression")),
};
for index in indices {
colors.push(
*palette
.get(index)
.ok_or_else(|| error("LEPCC RGB palette index out of bounds"))?,
);
}
}
reader.done()?;
Ok(colors)
}