infinite_rs_derive/
lib.rs1#![warn(clippy::pedantic)]
2#![allow(clippy::missing_errors_doc)]
3#![allow(clippy::missing_panics_doc)]
4#![allow(clippy::module_name_repetitions)]
5#![warn(clippy::all)]
6
7use std::collections::HashMap;
8
9use quote::quote;
10use syn::{DataStruct, DeriveInput};
11
12#[derive(deluxe::ExtractAttributes)]
13#[deluxe(attributes(data))]
14struct TagStructureAttributes {
15 size: u64,
16}
17
18#[derive(deluxe::ExtractAttributes, Clone)]
19#[deluxe(attributes(data))]
20struct TagStructureFieldAttributes {
21 offset: u64,
22 count: Option<u64>,
23}
24
25fn extract_struct_field_attributes(
26 ast: &mut DeriveInput,
27) -> deluxe::Result<HashMap<String, TagStructureFieldAttributes>> {
28 let mut field_attributes = HashMap::new();
29 if let syn::Data::Struct(data) = &mut ast.data {
30 for field in &mut data.fields {
31 let field_name = field.ident.as_ref().unwrap().to_string();
32 let attributes: TagStructureFieldAttributes = deluxe::extract_attributes(field)?;
33 field_attributes.insert(field_name, attributes);
34 }
35 }
36 Ok(field_attributes)
37}
38
39fn extract_field_maps(
40 field_attributes: &HashMap<String, TagStructureFieldAttributes>,
41) -> (Vec<String>, Vec<u64>) {
42 field_attributes
43 .clone()
44 .into_iter()
45 .map(|(field, attrs)| (field, attrs.offset))
46 .unzip()
47}
48
49fn generate_field_reads(
50 data: &DataStruct,
51 field_attributes: &HashMap<String, TagStructureFieldAttributes>,
52) -> Vec<proc_macro2::TokenStream> {
53 data.fields
54 .iter()
55 .map(|field| {
56 let field_name = &field.ident;
57 let offset = field_attributes
58 .get(&field_name.as_ref().unwrap().to_string())
59 .unwrap()
60 .offset;
61 if let syn::Type::Path(type_path) = &field.ty {
62 if let Some(segment) = type_path.path.segments.last() {
63 if segment.ident == "FieldArray" {
64 let count = field_attributes
65 .get(&field_name.as_ref().unwrap().to_string())
66 .unwrap()
67 .count
68 .unwrap();
69 return quote! {
70 reader.seek(std::io::SeekFrom::Start(main_offset + #offset))?;
71 self.#field_name.read(reader, #count)?;
72 };
73 }
74 }
75 }
76 quote! {
77 reader.seek(std::io::SeekFrom::Start(main_offset + #offset))?;
78 self.#field_name.read(reader)?;
79 }
80 })
81 .collect()
82}
83
84fn generate_field_blocks(
85 data: &DataStruct,
86 field_attributes: &HashMap<String, TagStructureFieldAttributes>,
87) -> Vec<proc_macro2::TokenStream> {
88 data.fields.iter().filter_map(|field| {
89 if let syn::Type::Path(type_path) = &field.ty {
90 if let Some(segment) = type_path.path.segments.last() {
91 let field_name = &field.ident;
92 match segment.ident.to_string().as_str() {
93 "FieldBlock" => {
94 let offset = field_attributes.get(&field_name.as_ref().unwrap().to_string()).unwrap().offset;
95 Some(quote! {
96 self.#field_name.load_blocks(source_index, adjusted_base + #offset, reader, tag_file)?;
97 })
98 },
99 "FieldTagResource" => {
100 let offset = field_attributes.get(&field_name.as_ref().unwrap().to_string()).unwrap().offset;
101 Some(quote! {
102 self.#field_name.load_resource(adjusted_base + #offset, reader, tag_file)?;
103 })
104 },
105 "FieldArray" => {
106 let offset = field_attributes.get(&field_name.as_ref().unwrap().to_string()).unwrap().offset;
107 Some(quote! {
108 self.#field_name.load_blocks(reader, source_index, adjusted_base + #offset, tag_file)?;
109 })
110 },
111 "FieldData" => {
112 Some(quote! {
113 self.#field_name.load_data(reader, source_index, parent_index, tag_file)?;
114 })
115 },
116 _ => None
117 }
118 } else {
119 None
120 }
121 } else {
122 None
123 }
124 }).collect()
125}
126fn tag_structure_derive2(
127 input: proc_macro2::TokenStream,
128) -> deluxe::Result<proc_macro2::TokenStream> {
129 let mut ast: DeriveInput = syn::parse2(input)?;
130 let TagStructureAttributes { size } = deluxe::extract_attributes(&mut ast)?;
131 let field_attributes: HashMap<String, TagStructureFieldAttributes> =
132 extract_struct_field_attributes(&mut ast)?;
133 let ident: &syn::Ident = &ast.ident;
134 let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
135
136 let syn::Data::Struct(data) = &ast.data else {
137 panic!("TagStructure can only be derived for structs")
138 };
139 let (name, field_offset) = extract_field_maps(&field_attributes);
140
141 let field_reads = generate_field_reads(data, &field_attributes);
142 let field_blocks = generate_field_blocks(data, &field_attributes);
143
144 Ok(quote! {
145 impl #impl_generics infinite_rs::module::file::TagStructure for #ident #type_generics #where_clause {
146 fn size(&mut self) -> u64 {
147 #size
148 }
149 fn read<R: infinite_rs::common::extensions::BufReaderExt>(&mut self, reader: &mut R) -> infinite_rs::Result<()> {
150 let main_offset = reader.stream_position()?;
151 #(#field_reads)*
152 reader.seek(std::io::SeekFrom::Start(main_offset + self.size()))?;
153 Ok(())
154 }
155
156 fn offsets(&self) -> std::collections::HashMap<&'static str, u64> {
157 let field_names = [#(#name),*];
158 let field_offsets = [#(#field_offset),*];
159
160 let map: std::collections::HashMap<&'static str, u64> = field_names.iter().zip(field_offsets.iter()).map(|(&name, &offset)| (name, offset)).collect();
161 map
162 }
163
164 fn load_field_blocks<R: std::io::BufRead + std::io::Seek + infinite_rs::common::extensions::BufReaderExt>(
165 &mut self,
166 source_index: i32,
167 parent_index: usize,
168 adjusted_base: u64,
169 reader: &mut R,
170 tag_file: &infinite_rs::tag::loader::TagFile,
171 ) -> infinite_rs::Result<()> {
172 #(#field_blocks)*
173 Ok(())
174 }
175 }
176 })
177}
178
179#[proc_macro_derive(TagStructure, attributes(data))]
180pub fn tag_structure_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
182 tag_structure_derive2(input.into()).unwrap().into()
183}