1use std::fs::File;
2use std::io::Write;
3use std::ops::Deref;
4
5use proc_macro2::{Ident, Literal, TokenStream};
6use quote::{format_ident, quote};
7
8use blend_inspect_rs::{Blend, Endianness, inspect, Struct, Type};
9use itertools::Itertools;
10use syn::__private::str;
11
12
13pub fn generate(source_file: &str, target_dir: &str) -> String {
14
15 let data = std::fs::read(source_file).unwrap();
16 let blend = inspect(&data).ok().unwrap();
17
18 let module_name = {
19 let major = blend.version().major;
20 let minor = blend.version().minor.to_digit(10).unwrap() * 10 + blend.version().patch.to_digit(10).unwrap();
21 match blend.pointer_size() {
22 4 => format!("blender{}_{}x86", major, minor),
23 8 => format!("blender{}_{}", major, minor),
24 _ => panic!("Illegal pointer size '{}'! Possible values are 4 and 8.", blend.pointer_size())
25 }
26 };
27
28 let module_name_ident = format_ident!("{}", module_name);
29 let file_name = format!("{}/{}.rs", target_dir, module_name);
30
31 let blend_structs: Vec<TokenStream> = blend.structs()
32 .sorted_by(|a, b| Ord::cmp(a.name(), b.name()))
33 .map(|structure| {
34 let name = format_ident!("{}", structure.name());
35 let pointer_size = Literal::usize_unsuffixed(blend.pointer_size());
36 let fields: Vec<TokenStream> = structure.fields()
37 .map(|field| {
38 let name = match field.name() {
39 "type" => "type_",
40 "macro" => "macro_",
41 "match" => "match_",
42 "ref" => "ref_",
43 name => name,
44 };
45 let name = format_ident!("{}", name);
46 let ty = quote_type(field.data_type());
47 quote! {
48 pub #name: #ty
49 }
50 })
51 .collect();
52 let double_linked = {
53 if is_struct_double_linked(structure) {
54 quote! {
55 impl DoubleLinked<Pointer<#name>> for #name {
56 fn next(&self) -> &Pointer<Self> {
57 &self.next
58 }
59 fn prev(&self) -> &Pointer<Self> {
60 &self.prev
61 }
62 }
63 }
64 }
65 else {
66 quote!()
67 }
68 };
69 let named = {
70 if is_struct_named(structure) {
71 quote! {
72 impl Named for #name {
73 fn get_name(&self) -> &str {
74 self.name.to_name_str_unchecked()
75 }
76 }
77 }
78 }
79 else {
80 quote!()
81 }
82 };
83 let generated_impl = quote_generated_impl(
84 &blend,
85 structure.name(),
86 structure.struct_index(),
87 structure.struct_type_index(),
88 Vec::new(),
89 false
90 );
91 let pointer_target_impl = quote_pointer_target_impl(structure.name());
92 quote! {
93 #[repr(C, packed(4))]
94 pub struct #name {
95 #(#fields),*
96 }
97 #generated_impl
98 #pointer_target_impl
99 #double_linked
100 #named
101 }
102 })
103 .collect();
104
105 let primitive_types_verifications = quote_primitive_types_verifications();
106 let struct_verifications = quote_struct_verifications(&blend);
107 let nothing_struct = quote_nothing_struct(&blend);
108 let void_struct = quote_void_struct(&blend);
109 let pointer_struct = quote_pointer_struct(&blend);
110 let function_struct = quote_function_struct(&blend);
111
112 let code = quote! {
113 pub mod #module_name_ident {
114 #![allow(non_snake_case)]
115 #![allow(dead_code)]
116
117 use crate::blend::{GeneratedBlendStruct, Version, Endianness, PointerLike, PointerTarget, NameLike};
118 use crate::blend::traverse::DoubleLinked;
119 use crate::blend::traverse::Named;
120 use blend_inspect_rs::Address;
121 use std::marker::PhantomData;
122
123 #nothing_struct
124 #void_struct
125 #pointer_struct
126 #function_struct
127
128 #(#blend_structs)*
129
130 #[cfg(test)]
131 mod verifications {
132 use super::*;
133 #primitive_types_verifications
134 #struct_verifications
135 }
136 }
137 };
138
139 let mut generated_file = File::create(&file_name).expect(&file_name);
140 write!(&mut generated_file, "{:#}", code).expect("Unable to write generated.ts");
141
142 file_name
143}
144
145fn quote_nothing_struct(blend: &Blend) -> TokenStream {
146 let generated_impl = quote_generated_impl(
147 blend,
148 "Nothing",
149 usize::MAX,
150 usize::MAX,
151 Vec::new(),
152 true
153 );
154 quote! {
155 #[derive(Debug, Copy, Clone)]
156 pub struct Nothing;
157 impl PointerTarget<Nothing> for Nothing {}
158 #generated_impl
159 }
160}
161
162fn quote_void_struct(blend: &Blend) -> TokenStream {
163 let generated_impl = quote_generated_impl(
164 blend,
165 "Void",
166 usize::MAX - 1,
167 usize::MAX - 1,
168 Vec::new(),
169 true
170 );
171 quote! {
172 #[derive(Debug, Copy, Clone)]
173 pub struct Void;
174 impl PointerTarget<Void> for Void {}
175 #generated_impl
176 }
177}
178
179fn quote_pointer_struct(blend: &Blend) -> TokenStream {
180 let pointer_size = Literal::usize_unsuffixed(blend.pointer_size());
181 let generated_impl_fields = quote_generated_impl_fields(
182 blend,
183 "Pointer",
184 usize::MAX - 2,
185 usize::MAX - 2,
186 true
187 );
188 quote! {
189 #[derive(Debug, Clone)]
190 pub struct Pointer<T>
191 where T: PointerTarget<T> {
192 pub value: [u8; #pointer_size],
193 phantom: PhantomData<T>
194 }
195 impl <T> Pointer<T>
196 where T: PointerTarget<T> {
197 pub fn new(value: [u8; #pointer_size]) -> Self {
198 Pointer {
199 value,
200 phantom: Default::default()
201 }
202 }
203 }
204 impl <T> PointerLike<T> for Pointer<T>
205 where T: PointerTarget<T> {
206 type Pointer<A: PointerTarget<A>> = Pointer<A>;
207 fn as_instance_of<B: PointerTarget<B>>(&self) -> Self::Pointer<B> {
208 Pointer::new(self.value)
209 }
210 fn address(&self) -> Option<Address> {
211 let result = self.value.iter().enumerate().fold(0usize, |result, (index, value)| {
212 result + ((*value as usize) << (8 * index))
213 });
214 Address::new(result)
215 }
216 fn is_valid(&self) -> bool {
217 self.value.iter().sum::<u8>() > 0
218 }
219 }
220 impl <T> PointerTarget<Pointer<T>> for Pointer<T>
221 where T: PointerTarget<T> {
222
223 }
224 impl <T> GeneratedBlendStruct for Pointer<T>
225 where T: PointerTarget<T> {
226 #generated_impl_fields
227 }
228 }
229}
230
231fn quote_function_struct(blend: &Blend) -> TokenStream {
232 let pointer_size = Literal::usize_unsuffixed(blend.pointer_size());
233 let generated_impl = quote_generated_impl(
234 blend,
235 "Function",
236 usize::MAX - 3,
237 usize::MAX - 3,
238 Vec::new(),
239 true
240 );
241 quote! {
242 #[derive(Debug, Copy, Clone)]
243 pub struct Function {
244 pub value: [u8; #pointer_size]
245 }
246 #generated_impl
247 }
248}
249
250fn quote_generated_impl(blend: &Blend, struct_name: &str, struct_index: usize, type_index: usize, type_parameters: Vec<(&str, Option<&str>)>, is_synthetic: bool) -> TokenStream {
251 let type_parameters_idents = type_parameters.iter()
252 .map(|(name, _)| format_ident!("{}", name))
253 .collect::<Vec<Ident>>();
254 let name = {
255 let name = format_ident!("{}", struct_name);
256 if type_parameters_idents.is_empty() {
257 quote!(#name)
258 }
259 else {
260 quote!(#name<#(#type_parameters_idents),*>)
261 }
262 };
263 let type_parameters = {
264 if type_parameters.is_empty() {
265 quote!()
266 }
267 else {
268 let type_parameters = type_parameters.iter()
269 .map(|(name, bounds)| {
270 let name = format_ident!("{}", name);
271 let bounds = bounds
272 .map(|bounds| {
273 let bounds = format_ident!("{}", bounds);
274 quote!{: #bounds}
275 })
276 .unwrap_or(quote!());
277 quote! {#name #bounds}
278 })
279 .collect::<Vec<TokenStream>>();
280 quote!(<#(#type_parameters),*>)
281 }
282 };
283 let fields = quote_generated_impl_fields(blend, struct_name, struct_index, type_index, is_synthetic);
284 quote! {
285 impl #type_parameters GeneratedBlendStruct for #name {
286 #fields
287 }
288 }
289}
290
291fn quote_generated_impl_fields(blend: &Blend, struct_name: &str, struct_index: usize, type_index: usize, is_synthetic: bool) -> TokenStream {
292 let major_version = Literal::character(blend.version().major);
293 let minor_version = Literal::character(blend.version().minor);
294 let patch_version = Literal::character(blend.version().patch);
295 let struct_name_literal = Literal::string(struct_name);
296 let struct_index_literal = Literal::usize_unsuffixed(struct_index);
297 let type_index_literal = Literal::usize_unsuffixed(type_index);
298 let endianness = quote_endianness(blend.endianness());
299 let pointer_size_literal = Literal::usize_unsuffixed(blend.pointer_size());
300 let is_synthetic_ident = format_ident!("{}", is_synthetic);
301 quote! {
302 const BLEND_VERSION: Version = Version::new(#major_version, #minor_version, #patch_version);
303 const BLEND_POINTER_SIZE: usize = #pointer_size_literal;
304 const BLEND_ENDIANNESS: Endianness = #endianness;
305 const STRUCT_NAME: &'static str = #struct_name_literal;
306 const STRUCT_INDEX: usize = #struct_index_literal;
307 const STRUCT_TYPE_INDEX: usize = #type_index_literal;
308 const IS_SYNTHETIC: bool = #is_synthetic_ident;
309 }
310}
311
312
313fn quote_pointer_target_impl(struct_name: &str) -> TokenStream {
314 let name = format_ident!("{}", struct_name);
315 quote! {
316 impl PointerTarget<#name> for #name {}
317 }
318}
319
320fn quote_type(ty: &Type) -> TokenStream {
321 match ty {
322 Type::Char => {
323 let ident = primitive_type_ident(ty);
324 quote!(#ident)
325 },
326 Type::UChar => {
327 let ident = primitive_type_ident(ty);
328 quote!(#ident)
329 },
330 Type::Short => {
331 let ident = primitive_type_ident(ty);
332 quote!(#ident)
333 }
334 Type::UShort => {
335 let ident = primitive_type_ident(ty);
336 quote!(#ident)
337 }
338 Type::Int => {
339 let ident = primitive_type_ident(ty);
340 quote!(#ident)
341 }
342 Type::Int8 => {
343 let ident = primitive_type_ident(ty);
344 quote!(#ident)
345 }
346 Type::Int64 => {
347 let ident = primitive_type_ident(ty);
348 quote!(#ident)
349 }
350 Type::UInt64 => {
351 let ident = primitive_type_ident(ty);
352 quote!(#ident)
353 }
354 Type::Long => {
355 let ident = primitive_type_ident(ty);
356 quote!(#ident)
357 }
358 Type::ULong => {
359 let ident = primitive_type_ident(ty);
360 quote!(#ident)
361 }
362 Type::Float => {
363 let ident = primitive_type_ident(ty);
364 quote!(#ident)
365 }
366 Type::Double => {
367 let ident = primitive_type_ident(ty);
368 quote!(#ident)
369 }
370 Type::Void => quote!(Void),
371 Type::Struct { name, size: _size } => {
372 let name = format_ident!("{}", name);
373 quote!(#name)
374 },
375 Type::Pointer { base_type, size: _size } => {
376 let ty = quote_type(base_type);
377 quote! {
378 Pointer<#ty>
379 }
380 }
381 Type::Array { base_type, length } => {
382 let size = Literal::usize_unsuffixed(*length);
383 let ty = quote_type(base_type);
384 quote! {
385 [#ty; #size]
386 }
387 }
388 Type::Function { size: _size } => {
389 quote! {
390 Function
391 }
392 },
393 Type::Special { .. } => quote!(Nothing),
394 }
395}
396
397fn quote_endianness(endianness: &Endianness) -> TokenStream {
398 match endianness {
399 Endianness::Little => quote!(Endianness::Little),
400 Endianness::Big => quote!(Endianness::Big),
401 }
402}
403
404fn quote_primitive_types_verifications() -> TokenStream {
405 let assertions: Vec<TokenStream> = Type::PRIMITIVES.iter()
406 .map(|ty| {
407 let expectation_literal = Literal::usize_unsuffixed(ty.size());
408 let type_ident = quote_type(ty);
409 quote! {
410 assert_eq!(std::mem::size_of::<#type_ident>(), #expectation_literal);
411 }
412 })
413 .collect();
414 quote! {
415 #[test]
416 fn verify_primitive_types_size() {
417 #(#assertions);*
418 }
419 }
420}
421
422fn quote_struct_verifications(blend: &Blend) -> TokenStream {
423 let verifications = blend.structs()
424 .map(|structure| {
425 let function_name = format_ident!("verify_{}_size", structure.name());
426 let type_ident = format_ident!("{}", structure.name());
427 let expected_size = Literal::usize_unsuffixed(structure.size());
428 quote! {
429 #[test]
430 fn #function_name() {
431 assert_eq!(std::mem::size_of::<#type_ident>(), #expected_size);
432 }
433 }
434 });
435 quote! {
436 #(#verifications)*
437 }
438}
439
440fn primitive_type_ident(ty: &Type) -> Ident {
441 match ty {
442 Type::Char => format_ident!("{}", "i8"),
443 Type::UChar => format_ident!("{}", "u8"),
444 Type::Short => format_ident!("{}", "i16"),
445 Type::UShort => format_ident!("{}", "u16"),
446 Type::Int => format_ident!("{}", "i32"),
447 Type::Int8 => format_ident!("{}", "i8"),
448 Type::Int64 => format_ident!("{}", "i64"),
449 Type::UInt64 => format_ident!("{}", "u64"),
450 Type::Long => format_ident!("{}", "i32"),
451 Type::ULong => format_ident!("{}", "u32"),
452 Type::Float => format_ident!("{}", "f32"),
453 Type::Double => format_ident!("{}", "f64"),
454 _ => panic!("not a primitive type")
455 }
456}
457
458fn is_struct_double_linked(structure: &Struct) -> bool {
459 let (has_next, has_prev) = structure.fields().fold((false, false), |(has_next, has_prev), field| {
460 match field.data_type() {
461 Type::Pointer { base_type, size: _pointer_size } => {
462 match base_type.deref() {
463 Type::Struct { name, size: _struct_size } => {
464 if name == structure.name() {
465 match field.name() {
466 "next" => (true, has_prev),
467 "prev" => (has_next, true),
468 _ => (has_next, has_prev)
469 }
470 } else {
471 (has_next, has_prev)
472 }
473 }
474 _ => (has_next, has_prev)
475 }
476 }
477 _ => (has_next, has_prev)
478 }
479 });
480 has_next && has_prev
481}
482
483fn is_struct_named(structure: &Struct) -> bool {
484 structure.fields().any(|field| {
485 field.name() == "name" && match field.data_type() {
486 Type::Array { base_type, length: _length } => {
487 match base_type.deref() {
488 Type::Char => true,
489 _ => false,
490 }
491 }
492 _ => false,
493 }
494 })
495}