diff --git a/Cargo.lock b/Cargo.lock index 0c349359..3434f417 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1491,6 +1491,13 @@ dependencies = [ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rle" +version = "0.1.0" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ron" version = "0.6.1" @@ -1783,6 +1790,7 @@ dependencies = [ "serde_scan 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "splay 0.1.0", "tiff 0.1.0", + "vot 0.1.0", "wgpu 0.6.0 (git+https://github.com/gfx-rs/wgpu-rs)", "winit 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1792,6 +1800,14 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vot" +version = "0.1.0" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rle 0.1.0", +] + [[package]] name = "walkdir" version = "2.3.1" diff --git a/Cargo.toml b/Cargo.toml index 6be179fe..00f5885d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ path = "bin/convert/main.rs" m3d = { path = "lib/m3d" } splay = { path = "lib/splay" } tiff = { path = "lib/tiff" } +vot = { path = "lib/vot" } # library bytemuck = "1" byteorder = "1.0" diff --git a/bin/convert/main.rs b/bin/convert/main.rs index 5f4aac2b..a013e9b6 100644 --- a/bin/convert/main.rs +++ b/bin/convert/main.rs @@ -145,6 +145,12 @@ fn main() { let level_data = layers.export(); level_data.save_vmp(&dst_path); } + ("vot", "ron") => { + println!("\tLoading the VOT..."); + let mut file = File::open(&src_path).unwrap(); + let vot = vot::MobileLocation::load(&mut file, 16); + println!("\tGot {} frames", vot.frames.len()); + } ("pal", "png") => { println!("Converting palette to PNG..."); let data = fs_read(&src_path).unwrap(); diff --git a/docs/index.md b/docs/index.md index 434000b8..fd80150e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,3 +10,5 @@ layout: home - fearless GPU and multi-core use This blog tells the story of this adventure :) + +Also see the [related videos](https://peertube.fidonet.io/video-channels/vangers_dev/videos) on PeerTube! \ No newline at end of file diff --git a/lib/rle/Cargo.toml b/lib/rle/Cargo.toml new file mode 100644 index 00000000..56c13bc8 --- /dev/null +++ b/lib/rle/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rle" +version = "0.1.0" +workspace = "../.." +authors = ["Dzmitry Malyshau "] +edition = "2018" + +[dependencies] +byteorder = "1.0" diff --git a/lib/rle/src/lib.rs b/lib/rle/src/lib.rs new file mode 100644 index 00000000..59f5c2d7 --- /dev/null +++ b/lib/rle/src/lib.rs @@ -0,0 +1,35 @@ +pub fn decode(data: &[u8], output: &mut Vec) { + let mut i = 1; + let mut cur = data[0]; + let mut len = 0; + while i <= data.len() { + while data.get(i) == Some(&cur) && len < 127 { + len += 1; + i += 1; + } + + if len != 0 { + output.push(len); + output.push(cur); + cur = data.get(i).cloned().unwrap_or(0); + i += 1; + len = 0; + } + + while match data.get(i) { + Some(&ch) if ch != cur && len < 127 => { + len += 1; + cur = ch; + true + } + _ => false, + } {} + + if len != 0 { + output.push(0x80 + (len - 1)); + output.extend_from_slice(&data[i - (len - 1) as usize..i - 1]); + output.push(cur); + len = 0; + } + } +} diff --git a/lib/vot/Cargo.toml b/lib/vot/Cargo.toml new file mode 100644 index 00000000..0d97f426 --- /dev/null +++ b/lib/vot/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "vot" +version = "0.1.0" +workspace = "../.." +authors = ["Dzmitry Malyshau "] +edition = "2018" + +[dependencies] +byteorder = "1.0" +rle = { path = "../rle" } diff --git a/lib/vot/src/lib.rs b/lib/vot/src/lib.rs new file mode 100644 index 00000000..27cd4ae4 --- /dev/null +++ b/lib/vot/src/lib.rs @@ -0,0 +1,151 @@ +use byteorder::{LittleEndian as E, ReadBytesExt}; + +const SIGNATURE: &[u8; 3] = b"ML3"; //MLSign +const NAME_LEN: usize = 16; //MLNAMELEN + 1 +const MAX_KEYPHRASE: usize = 4; //MAX_KEYPHASE + +#[derive(Clone, Copy, Debug)] +pub enum Mode { + Relative = 0, + Absolute = 1, + Rel2Abs = 2, +} + +pub struct Frame { + pos: (i32, i32), + size: (u32, u32), + period: u32, + surf_type: u32, + csd: u32, + cst: u32, + delta: Vec, + terrain: Vec, + sign_bits: Vec, +} + +impl Frame { + pub fn load( + input: &mut I, + mode: Mode, + max_suface_type: u32, + mut temp: &mut Vec, + ) -> Self { + let x0 = input.read_i32::().unwrap(); + let y0 = input.read_i32::().unwrap(); + let sx = input.read_u32::().unwrap(); + let sy = input.read_u32::().unwrap(); + let period = input.read_u32::().unwrap(); + let surf_type = input.read_u32::().unwrap(); + let csd = input.read_u32::().unwrap(); + let cst = input.read_u32::().unwrap(); + let _ = input.read_u32::(); + let _ = input.read_u32::(); + let total = (sx * sy) as usize; + + temp.clear(); + temp.resize(total, 0u8); + + let delta = if csd == 0 { + input.read(&mut temp).unwrap(); + let mut d = Vec::new(); + rle::decode(&temp, &mut d); + d + } else { + let mut d = vec![0u8; csd as usize]; + input.read(&mut d).unwrap(); + d + }; + + let terrain = if surf_type >= max_suface_type { + if cst == 0 { + input.read(&mut temp).unwrap(); + let mut t = Vec::new(); + rle::decode(&temp, &mut t); + t + } else { + let mut t = vec![0u8; cst as usize]; + input.read(&mut t).unwrap(); + t + } + } else { + Vec::new() + }; + + let sign_bits = match mode { + Mode::Relative => { + let words = total / 32 + 1; + let mut sb = Vec::with_capacity(words); + for _ in 0..words { + sb.push(input.read_u32::().unwrap()); + } + sb + } + _ => Vec::new(), + }; + + Frame { + pos: (x0, y0), + size: (sx, sy), + period, + surf_type, + csd, + cst, + delta, + terrain, + sign_bits, + } + } +} + +pub struct MobileLocation { + pub max_stage: u32, + pub frames: Vec, +} + +impl MobileLocation { + pub fn load(input: &mut I, max_surface: u32) -> Self { + let mut signature = [0u8; 3]; + input.read(&mut signature).unwrap(); + assert_eq!(&signature, SIGNATURE); + + let mut raw_name = [0u8; NAME_LEN]; + input.read(&mut raw_name).unwrap(); + + let max_frame = input.read_u32::().unwrap(); + let _dry_terrain = input.read_u32::().unwrap(); + let _impulse = input.read_u32::().unwrap(); + + let _ = input.read_u8(); + let mode = match input.read_u8().unwrap() { + 0 => Mode::Relative, + 1 => Mode::Absolute, + 2 => Mode::Rel2Abs, + other => panic!("Unexpected mode {}", other), + }; + let _ = input.read_u8(); + let _ = input.read_u8(); + + let mut keyphrase = [0u32; MAX_KEYPHRASE]; + for key in keyphrase[1..].iter_mut() { + *key = input.read_u32::().unwrap(); + } + let _ = input.read_u32::(); + + let mut is_alt = false; + let mut max_stage = 0; + let mut alt_size = (0, 0); + let mut frames = Vec::with_capacity(max_frame as usize); + let mut temp = Vec::new(); + for _ in 0..max_frame { + let frame = Frame::load(input, mode, max_surface, &mut temp); + alt_size.0 = alt_size.0.max(frame.size.0); + alt_size.1 = alt_size.1.max(frame.size.1); + is_alt |= frame.period > 1; + max_stage += frame.period; + frames.push(frame); + } + let _ = is_alt; + + MobileLocation { max_stage, frames } + } +}