1use std::str::Utf8Error;
2
3use blend_inspect_rs::Address;
4
5pub use blend_inspect_rs::{Version, Endianness};
6pub use reader::{read, Reader, ReadError, StructIter};
7
8pub mod traverse;
9
10mod reader;
11
12pub trait GeneratedBlendStruct : Sized {
13 const BLEND_VERSION: Version;
14 const BLEND_POINTER_SIZE: usize;
15 const BLEND_ENDIANNESS: Endianness;
16 const STRUCT_NAME: &'static str;
17 const STRUCT_INDEX: usize;
18 const STRUCT_TYPE_INDEX: usize;
19 const IS_SYNTHETIC: bool;
20}
21
22pub trait PointerLike<T> : Sized
23where T: PointerTarget<T> {
24
25 #[feature(generic_associated_types)] type Pointer<A: PointerTarget<A>>: PointerLike<A>;
27
28 fn as_instance_of<B: PointerTarget<B>>(&self) -> Self::Pointer<B>;
29
30 fn address(&self) -> Option<Address>;
31
32 fn is_valid(&self) -> bool;
33
34 fn is_invalid(&self) -> bool {
35 !self.is_valid()
36 }
37}
38
39pub trait PointerTarget<T> : Sized {
40
41}
42
43impl PointerTarget<i8> for i8 {}
44impl PointerTarget<u8> for u8 {}
45impl PointerTarget<i16> for i16 {}
46impl PointerTarget<u16> for u16 {}
47impl PointerTarget<i32> for i32 {}
48impl PointerTarget<i64> for i64 {}
49impl PointerTarget<u64> for u64 {}
50impl PointerTarget<u32> for u32 {}
51impl PointerTarget<f32> for f32 {}
52impl PointerTarget<f64> for f64 {}
53
54pub trait StringLike {
55
56 fn to_str(&self) -> Result<&str, Utf8Error>;
57
58 fn to_str_unchecked(&self) -> &str {
59 self.to_str().expect("Failed to extract &str!")
60 }
61
62 fn to_string(&self) -> Result<String, Utf8Error> {
63 self.to_str().map(|value| String::from(value))
64 }
65
66 fn to_string_unchecked(&self) -> String {
67 self.to_string().expect("Failed to extract String!")
68 }
69}
70
71impl <A> StringLike for A
72where A: AsRef<[i8]> {
73
74 fn to_str(&self) -> Result<&str, Utf8Error> {
75 let self_ref = self.as_ref();
76 if !self_ref.is_empty() {
77 let slice: &[u8] = unsafe {
78 core::slice::from_raw_parts(self_ref.as_ptr() as *const u8, self_ref.len())
79 };
80 let null = slice.iter()
81 .position(|element| *element == 0x00)
82 .unwrap_or(slice.len());
83 std::str::from_utf8(&slice[0..null])
84 }
85 else {
86 Ok("")
87 }
88 }
89}
90
91pub trait NameLike {
92
93 const NAME_PREFIXES: [&'static str; 17] = [
94 "OB", "ME", "WM", "IM", "SN",
95 "WS", "BR", "SC", "PL", "OB",
96 "GR", "CA", "LA", "ME", "WO",
97 "LS", "MA",
98 ];
99
100 fn to_name_str(&self) -> Result<&str, Utf8Error>;
101
102 fn to_name_string(&self) -> Result<String, Utf8Error> {
103 self.to_name_str().map(|value| String::from(value))
104 }
105
106 fn to_name_str_unchecked(&self) -> &str {
107 self.to_name_str().expect("Failed to convert to name!")
108 }
109
110 fn to_name_string_unchecked(&self) -> String {
111 self.to_name_string().expect("Failed to convert to name!")
112 }
113}
114
115impl <A> NameLike for A
116where A: StringLike {
117
118 fn to_name_str(&self) -> Result<&str, Utf8Error> {
119 self.to_str().map(|value| {
120 if Self::NAME_PREFIXES.contains(&&value[0..2]) {
121 &value[2..]
122 }
123 else {
124 &value
125 }
126 })
127 }
128}
129
130#[cfg(test)]
131mod test {
132 use crate::blend::{read, PointerLike, NameLike, StringLike};
133 use crate::blend::traverse::Named;
134 use crate::blender3_2::{bNode, bNodeSocket, bNodeTree, Image, Link, Material, Mesh, MLoop, MVert, Object};
135
136 #[test]
137 fn test() {
138
139 let blend_data = std::fs::read("examples/example-3.2.blend").unwrap();
140 let reader = read(&blend_data).unwrap();
141
142 let cube: &Object = reader.iter::<Object>().unwrap()
143 .find(|object| object.id.name.to_name_str_unchecked() == "Cube")
144 .unwrap();
145
146 println!("Object: {}", cube.id.get_name());
147
148 let parent = reader.deref(&cube.parent).unwrap().first().unwrap();
149
150 println!("Parent: {}", parent.id.get_name());
151
152 let mesh = reader.deref_single(&cube.data.as_instance_of::<Mesh>())
153 .unwrap();
154
155 println!("Mesh: {}", mesh.id.get_name());
156
157 let mesh_loop: Vec<&MLoop> = reader.deref(&mesh.mloop).unwrap().collect();
158 let mesh_vertices: Vec<&MVert> = reader.deref(&mesh.mvert).unwrap().collect();
159
160 let mesh_polygon = reader.deref(&mesh.mpoly).unwrap();
161 let _vertices = mesh_polygon
162 .map(|polygon| {
163 (polygon.loopstart..polygon.loopstart + polygon.totloop).into_iter().map(|loop_index| {
164 mesh_vertices[mesh_loop[loop_index as usize].v as usize].co
165 })
166 })
167 .flatten()
168 .collect::<Vec<[f32; 3]>>();
169
170 let mat = reader.deref(&mesh.mat.as_instance_of::<Link>())
171 .map(|links| {
172 let link = links.first().unwrap();
173 reader.deref(&link.next.as_instance_of::<Material>()).unwrap()
174 })
175 .unwrap()
176 .first()
177 .unwrap();
178
179 println!("Material: {}, use_nodes: {}", mat.id.get_name(), &mat.use_nodes);
180
181 let tree: &bNodeTree = reader.deref_single(&mat.nodetree)
182 .unwrap();
183
184 let node = reader.deref_single(&tree.nodes.last.as_instance_of::<bNode>()) .unwrap();
186
187 let base_color_socket = reader.deref_single(&node.inputs.first.as_instance_of::<bNodeSocket>())
188 .unwrap();
189
190 let link = reader.deref_single(&base_color_socket.link)
191 .unwrap();
192
193 let tex_node = reader.deref_single(&link.fromnode)
194 .unwrap();
195
196 let tex_image = reader.deref_single(&tex_node.id.as_instance_of::<Image>())
197 .unwrap();
198
199 let image_packed_file = reader.deref_single(&tex_image.packedfile)
200 .unwrap();
201
202 let data = reader.deref_raw_range(&image_packed_file.data, 0..image_packed_file.size as usize)
203 .unwrap();
204
205 std::fs::write("/tmp/texture.jpg", data)
206 .unwrap();
207
208 let nodes = reader.traverse_double_linked(&tree.nodes.first.as_instance_of::<bNode>())
209 .unwrap()
210 .find(|node: &bNode| node.idname.to_str_unchecked() == "ShaderNodeTexImage")
211 .unwrap();
212
213 println!("Node: {}", nodes.get_name());
214 println!("Node: {}", nodes.idname.to_name_str_unchecked());
215
216 }
220}