"]
6 | edition = "2021"
7 | publish = false
8 |
9 | [dependencies]
10 | byteorder = "1.0"
11 | log = "0.4"
12 | ron = "0.8"
13 | profiling = "1.0"
14 | serde = { version = "1.0", features = ["serde_derive"] }
15 |
--------------------------------------------------------------------------------
/lib/m3d/src/geometry.rs:
--------------------------------------------------------------------------------
1 | pub const NORMALIZER: f32 = 124.0;
2 | pub const NUM_COLOR_IDS: u32 = 25;
3 |
4 | #[derive(Copy, Clone, Debug, PartialEq)]
5 | #[repr(u32)]
6 | pub enum ColorId {
7 | Reserved = 0,
8 | Body = 1,
9 | Window = 2,
10 | Wheel = 3,
11 | Defense = 4,
12 | Weapon = 5,
13 | Tube = 6,
14 | BodyRed = 7,
15 | BodyBlue = 8,
16 | BodyYellow = 9,
17 | BodyGray = 10,
18 | YellowCharged = 11,
19 | Custom0 = 12,
20 | Custom1 = 13,
21 | Custom2 = 14,
22 | Custom3 = 15,
23 | Custom4 = 16,
24 | Custom5 = 17,
25 | Custom6 = 18,
26 | Custom7 = 19,
27 | Black = 20,
28 | BodyGreen = 21,
29 | SkyFarmerKernboo = 22,
30 | SkyFarmerPipetka = 23,
31 | RottenItem = 24,
32 | }
33 |
34 | #[derive(Copy, Clone, Debug)]
35 | pub struct Vertex {
36 | pub pos: u16,
37 | pub normal: u16,
38 | }
39 |
40 | impl Vertex {
41 | pub const DUMMY: Self = Vertex {
42 | pos: !0,
43 | normal: !0,
44 | };
45 | }
46 |
47 | #[derive(Copy, Clone)]
48 | pub struct DrawTriangle {
49 | pub vertices: [Vertex; 3],
50 | pub flat_normal: [i8; 3],
51 | pub material: [u32; 2],
52 | }
53 |
54 | #[derive(Copy, Clone)]
55 | pub struct CollisionQuad {
56 | pub vertices: [u16; 4],
57 | pub middle: [i8; 3],
58 | pub flat_normal: [i8; 3],
59 | }
60 |
61 | #[derive(Default)]
62 | pub struct Geometry {
63 | pub positions: Vec<[i8; 3]>,
64 | pub normals: Vec<[i8; 3]>,
65 | pub polygons: Vec
,
66 | }
67 |
--------------------------------------------------------------------------------
/lib/splay/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "splay"
3 | version = "0.1.0"
4 | workspace = "../.."
5 | authors = ["Dzmitry Malyshau "]
6 | edition = "2018"
7 | publish = false
8 |
9 | [dependencies]
10 | byteorder = "1.0"
11 |
--------------------------------------------------------------------------------
/lib/splay/benches/level.rs:
--------------------------------------------------------------------------------
1 | #![feature(test)]
2 |
3 | use byteorder::{LittleEndian as E, ReadBytesExt};
4 | use std::{
5 | fs::File,
6 | io::{Read, Seek},
7 | };
8 |
9 | const VMC_PATH: &'static str = "/hub/gog/Vangers/game/thechain/fostral/output.vmc";
10 | const SIZE: [usize; 2] = [1 << 11, 1 << 14];
11 |
12 | #[bench]
13 | fn load_level(bench: &mut test::Bencher) {
14 | let mut file = File::open(VMC_PATH).unwrap();
15 | let table: Vec<_> = (0..SIZE[1])
16 | .map(|_| {
17 | let offset = file.read_i32::().unwrap();
18 | let size = file.read_i16::().unwrap();
19 | (offset, size)
20 | })
21 | .collect();
22 |
23 | let splay = splay::Splay::new(&mut file);
24 | let mut height = vec![0u8; SIZE[0]];
25 | let mut meta = vec![0u8; SIZE[0]];
26 | let data_offset = file.seek(std::io::SeekFrom::Current(0)).unwrap();
27 | let mut buffer = Vec::new();
28 | file.read_to_end(&mut buffer).unwrap();
29 |
30 | bench.iter(|| {
31 | for &(offset, size) in table[..0x100].iter() {
32 | let off = offset as usize - data_offset as usize;
33 | splay.expand(&buffer[off..off + size as usize], &mut height, &mut meta);
34 | }
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/lib/splay/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![deny(
2 | trivial_casts,
3 | trivial_numeric_casts,
4 | unused,
5 | unused_qualifications,
6 | rust_2018_compatibility,
7 | rust_2018_idioms,
8 | future_incompatible,
9 | nonstandard_style,
10 | missing_copy_implementations
11 | )]
12 | #![allow(missing_debug_implementations, clippy::new_without_default)]
13 |
14 | use byteorder::{LittleEndian as E, ReadBytesExt, WriteBytesExt};
15 | use std::io::{Read, Write};
16 |
17 | #[derive(Copy, Clone)]
18 | pub struct Splay {
19 | tree1: [i32; 512],
20 | tree2: [i32; 512],
21 | }
22 |
23 | //TODO: use iterators
24 |
25 | impl Splay {
26 | pub fn new(input: &mut I) -> Self {
27 | let mut splay = Splay {
28 | tree1: [0; 512],
29 | tree2: [0; 512],
30 | };
31 | for i in 0..512 {
32 | let v = input.read_i32::().unwrap();
33 | splay.tree1[i] = v;
34 | }
35 | for i in 0..512 {
36 | let v = input.read_i32::().unwrap();
37 | splay.tree2[i] = v;
38 | }
39 | splay
40 | }
41 |
42 | pub fn write_trivial(output: &mut O) {
43 | for _ in 0..2 {
44 | for i in 0i32..256 {
45 | output.write_i32::(i).unwrap();
46 | }
47 | for i in 0i32..256 {
48 | output.write_i32::(-i).unwrap();
49 | }
50 | }
51 | }
52 |
53 | pub fn tree_size() -> u64 {
54 | 512 * 2 * 4
55 | }
56 |
57 | fn decompress u8>(
58 | tree: &[i32],
59 | input: &[u8],
60 | output: &mut [u8],
61 | fun: F,
62 | ) -> usize {
63 | let mut k_input = 0;
64 | let mut last_char = 0u8;
65 | let mut bit = 0;
66 | let mut cur = 0u8;
67 | for out in output.iter_mut() {
68 | let mut code = 1i32;
69 | while code > 0 {
70 | bit = if bit == 0 {
71 | cur = input[k_input];
72 | k_input += 1;
73 | 7
74 | } else {
75 | bit - 1
76 | };
77 | let i = ((code as usize) << 1) + ((cur >> bit) as usize & 1);
78 | code = tree[i];
79 | }
80 | last_char = fun(last_char, -code as u8);
81 | *out = last_char;
82 | }
83 | k_input
84 | }
85 |
86 | #[allow(dead_code)]
87 | fn decompress_orig u8>(
88 | tree: &[i32],
89 | input: &mut I,
90 | output: &mut [u8],
91 | fun: F,
92 | ) {
93 | let mut last_char = 0u8;
94 | let mut c_index = 1usize;
95 | let mut cur_size = 0;
96 | loop {
97 | let cur = input.read_u8().unwrap();
98 | for bit in (0..8).rev() {
99 | let i = (c_index << 1) + ((cur >> bit) as usize & 1);
100 | let code = tree[i];
101 | c_index = if code <= 0 {
102 | last_char = fun(last_char, -code as u8);
103 | output[cur_size] = last_char;
104 | cur_size += 1;
105 | if cur_size == output.len() {
106 | return;
107 | }
108 | 1
109 | } else {
110 | code as usize
111 | };
112 | }
113 | }
114 | }
115 |
116 | pub fn expand(&self, input: &[u8], output1: &mut [u8], output2: &mut [u8]) {
117 | let off1 = Self::decompress(&self.tree1, input, output1, |b, c| b.wrapping_add(c));
118 | let off2 = Self::decompress(&self.tree2, &input[off1..], output2, |b, c| b ^ c);
119 | assert_eq!(off1 + off2, input.len());
120 | }
121 |
122 | pub fn compress_trivial(input1: &[u8], input2: &[u8], output: &mut O) {
123 | let mut last_char = 0;
124 | for &b in input1 {
125 | output.write_u8(b.wrapping_sub(last_char)).unwrap();
126 | last_char = b;
127 | }
128 | last_char = 0;
129 | for &b in input2 {
130 | output.write_u8(b ^ last_char).unwrap();
131 | last_char = b;
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/lib/tiff/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tiff"
3 | version = "0.1.0"
4 | workspace = "../.."
5 | authors = ["Dzmitry Malyshau "]
6 | edition = "2021"
7 | publish = false
8 |
9 | [dependencies]
10 | byteorder = "1.0"
11 |
--------------------------------------------------------------------------------
/lib/tiff/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![deny(
2 | trivial_casts,
3 | trivial_numeric_casts,
4 | unused,
5 | unused_qualifications,
6 | rust_2018_compatibility,
7 | rust_2018_idioms,
8 | future_incompatible,
9 | nonstandard_style,
10 | missing_copy_implementations
11 | )]
12 | #![allow(missing_debug_implementations, clippy::new_without_default)]
13 |
14 | use byteorder::{LittleEndian as E, WriteBytesExt};
15 |
16 | use std::io::{Result as IoResult, Seek, SeekFrom};
17 |
18 | const TY_ASCII: u16 = 2;
19 | const TY_SHORT: u16 = 3;
20 | const TY_LONG: u16 = 4;
21 | const TAG_IMAGE_WIDTH: u16 = 0x100;
22 | const TAG_IMAGE_LENGTH: u16 = 0x101;
23 | const TAG_BITS_PER_SAMPLE: u16 = 0x102;
24 | const TAG_IMAGE_DESCRIPTION: u16 = 0x10E;
25 | const TAG_STRIP_OFFSETS: u16 = 0x111;
26 | const TAG_ROWS_PER_STRIP: u16 = 0x116;
27 | const TAG_STRIP_BYTE_COUNTS: u16 = 0x117;
28 |
29 | struct Field {
30 | tag: u16,
31 | ty: u16,
32 | count: u32,
33 | value: u32,
34 | }
35 |
36 | pub struct Image<'a> {
37 | pub width: u32,
38 | pub height: u32,
39 | pub bpp: u16,
40 | pub name: &'a str,
41 | pub data: &'a [u8],
42 | }
43 |
44 | pub fn save(mut tiff: W, images: &[Image<'_>]) -> IoResult<()> {
45 | // header
46 | tiff.write_u16::(0x4949)?; // little endian
47 | tiff.write_u16::(42)?; // magic
48 | let data_start = (images.len() * 0x80) as u32;
49 | let mut data_offset = data_start;
50 | let mut cur_offset = 4;
51 | // image file directory
52 | for im in images {
53 | tiff.write_u32::(cur_offset + 4)?; // IFD offset
54 | let total_bytes = (im.width * im.height) as usize * im.bpp as usize / 8;
55 | assert_eq!(total_bytes, im.data.len());
56 | let description: u32 = im
57 | .name
58 | .chars()
59 | .take(3)
60 | .enumerate()
61 | .map(|(i, c)| (c as u32) << (i * 8))
62 | .sum();
63 | let fields = [
64 | Field {
65 | tag: TAG_IMAGE_WIDTH,
66 | ty: TY_LONG,
67 | count: 1,
68 | value: im.width,
69 | },
70 | Field {
71 | tag: TAG_IMAGE_LENGTH,
72 | ty: TY_LONG,
73 | count: 1,
74 | value: im.height,
75 | },
76 | Field {
77 | tag: TAG_BITS_PER_SAMPLE,
78 | ty: TY_SHORT,
79 | count: 1,
80 | value: im.bpp as u32,
81 | },
82 | Field {
83 | tag: TAG_IMAGE_DESCRIPTION,
84 | ty: TY_ASCII,
85 | count: im.name.len().min(3) as u32 + 1,
86 | value: description,
87 | },
88 | Field {
89 | tag: TAG_STRIP_OFFSETS,
90 | ty: TY_LONG,
91 | count: 1,
92 | value: data_offset,
93 | },
94 | Field {
95 | tag: TAG_ROWS_PER_STRIP,
96 | ty: TY_SHORT,
97 | count: 1,
98 | value: im.height,
99 | },
100 | Field {
101 | tag: TAG_STRIP_BYTE_COUNTS,
102 | ty: TY_LONG,
103 | count: 1,
104 | value: total_bytes as u32,
105 | },
106 | ];
107 | tiff.write_u16::(fields.len() as u16)?;
108 | for &Field {
109 | tag,
110 | ty,
111 | count,
112 | value,
113 | } in &fields
114 | {
115 | tiff.write_u16::(tag)?;
116 | tiff.write_u16::(ty)?;
117 | tiff.write_u32::(count)?;
118 | tiff.write_u32::(value)?;
119 | }
120 | cur_offset += 4 + 2 + fields.len() as u32 * 12;
121 | data_offset += total_bytes as u32;
122 | assert_eq!(tiff.seek(SeekFrom::Current(0)).unwrap(), cur_offset as u64);
123 | }
124 | // gap
125 | assert!(cur_offset < data_start);
126 | tiff.write_u32::(0)?; // next IFD offset
127 | for _ in cur_offset + 4..data_start {
128 | tiff.write_u8(0)?;
129 | }
130 | // image data
131 | for im in images {
132 | tiff.write_all(im.data)?;
133 | }
134 | Ok(())
135 | }
136 |
--------------------------------------------------------------------------------
/res/ffi/geometry.ron:
--------------------------------------------------------------------------------
1 | (
2 | height: 0x80,
3 | delta_mask: 239,
4 | delta_power: 3,
5 | delta_const: 4,
6 | )
--------------------------------------------------------------------------------
/res/ffi/objects.pal:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
! ""$#%%'&((*)++-,..0/113244
5 |
6 |
7 |
!
8 |
9 |
10 |
11 | !
12 |
13 |
14 | !# " ( - 3 8 !# &( +, 01 46 9; %*.3 8
15 | $'*.258< "
16 | & *
17 | .
18 | 158< "! %$ )( -, 0/ 43 86 <: $%*"+1(27-9 ! % * . 2 7 ; ? ? ?
? ? ? ?' ?- ?4 ?: ?? ???????????? ??&??+??0??6???
19 |
20 |
21 | !!!%%%(((+++...000333666888;;;===
22 |
# &