Initial commit: Terrafier v0.1.0
- Core world model (World, Dimension, Tile, Terrain, Platform) - NBT reader/writer with full tag support - Anvil region (.mca) format reader with chunk parsing - Block palette compression and export - Biome database with Minecraft 1.21 biomes - CLI commands: new, import, export, info, render - GUI application with egui (viewport, tools, undo/redo) - Noise-based heightmap generation and editing operations License: GPL-3.0-or-later
This commit is contained in:
commit
91835402b3
94 changed files with 12372 additions and 0 deletions
112
crates/palette-compress/src/bits.rs
Executable file
112
crates/palette-compress/src/bits.rs
Executable file
|
|
@ -0,0 +1,112 @@
|
|||
/// Compact storage of fixed-size non-negative integers in a `Vec<i64>`.
|
||||
///
|
||||
/// Minecraft Anvil stores block palette indices as packed integers.
|
||||
/// N bits per entry, entries packed consecutively into 64-bit longs.
|
||||
/// Entries never cross long boundaries.
|
||||
pub struct BitArray {
|
||||
pub data: Vec<i64>,
|
||||
pub bits_per_entry: u8,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl BitArray {
|
||||
/// Create a new `BitArray` with all entries initialized to 0.
|
||||
pub fn new(size: usize, bits_per_entry: u8) -> Self {
|
||||
assert!(
|
||||
bits_per_entry > 0 && bits_per_entry <= 64,
|
||||
"bits_per_entry must be 1..=64"
|
||||
);
|
||||
let entries_per_long = 64 / bits_per_entry as usize;
|
||||
let data_len = size.div_ceil(entries_per_long);
|
||||
BitArray {
|
||||
data: vec![0i64; data_len],
|
||||
bits_per_entry,
|
||||
size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap existing raw data as a `BitArray`.
|
||||
pub fn from_raw(data: Vec<i64>, bits_per_entry: u8, size: usize) -> Self {
|
||||
BitArray {
|
||||
data,
|
||||
bits_per_entry,
|
||||
size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the value at `index`.
|
||||
pub fn get(&self, index: usize) -> i64 {
|
||||
assert!(index < self.size, "index out of bounds");
|
||||
let epb = 64 / self.bits_per_entry as usize;
|
||||
let long_idx = index / epb;
|
||||
let offset = (index % epb) * self.bits_per_entry as usize;
|
||||
let mask = (1i64 << self.bits_per_entry) - 1;
|
||||
(self.data[long_idx] >> offset) & mask
|
||||
}
|
||||
|
||||
/// Write `value` at `index`.
|
||||
pub fn set(&mut self, index: usize, value: i64) {
|
||||
assert!(index < self.size, "index out of bounds");
|
||||
let epb = 64 / self.bits_per_entry as usize;
|
||||
let long_idx = index / epb;
|
||||
let offset = (index % epb) * self.bits_per_entry as usize;
|
||||
let mask = (1i64 << self.bits_per_entry) - 1;
|
||||
self.data[long_idx] =
|
||||
(self.data[long_idx] & !(mask << offset)) | ((value & mask) << offset);
|
||||
}
|
||||
|
||||
/// Borrow the raw underlying long array.
|
||||
pub fn to_raw(&self) -> &[i64] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
/// Minimum bits needed to represent `count` distinct values.
|
||||
///
|
||||
/// Minecraft requires at least 1 bit per entry, even for a single block.
|
||||
pub fn bits_needed(count: usize) -> u8 {
|
||||
if count <= 2 {
|
||||
return 1; // 0 and 1 both fit in 1 bit; 2 values → 1 bit
|
||||
}
|
||||
let count = count as u64;
|
||||
(64 - count.saturating_sub(1).leading_zeros()) as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_pack_unpack() {
|
||||
let mut ba = BitArray::new(10, 4);
|
||||
for i in 0..10 {
|
||||
ba.set(i, (i % 15) as i64);
|
||||
}
|
||||
for i in 0..10 {
|
||||
assert_eq!(ba.get(i), (i % 15) as i64);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bits_needed() {
|
||||
assert_eq!(BitArray::bits_needed(0), 1);
|
||||
assert_eq!(BitArray::bits_needed(1), 1);
|
||||
assert_eq!(BitArray::bits_needed(2), 1);
|
||||
assert_eq!(BitArray::bits_needed(3), 2);
|
||||
assert_eq!(BitArray::bits_needed(4), 2);
|
||||
assert_eq!(BitArray::bits_needed(8), 3);
|
||||
assert_eq!(BitArray::bits_needed(16), 4);
|
||||
assert_eq!(BitArray::bits_needed(256), 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_large_array() {
|
||||
let mut ba = BitArray::new(4096, 13);
|
||||
for i in 0..4096 {
|
||||
ba.set(i, (i % 8191) as i64);
|
||||
}
|
||||
for i in 0..4096 {
|
||||
assert_eq!(ba.get(i), (i % 8191) as i64);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue