1use rgrit_core::Compression;
2use rgrit_core::GfxFormat;
3use rgrit_rs::Bitmap;
4use rgrit_rs::BitmapBuilder;
5
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::Ident;
9use syn::LitInt;
10use syn::{parse::Parse, parse_macro_input, LitStr};
11
12#[derive(Debug, Clone)]
13struct Grit {
14 bitmap: Bitmap,
15}
16
17impl Parse for Grit {
18 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
19 let lit = input.parse::<LitStr>()?;
20
21 if input.is_empty() {
22 let bitmap = BitmapBuilder::new(lit.value())
23 .with_transparency(rgrit_core::Transparency::Disabled)
24 .with_bit_depth_override(rgrit_core::BitDepth::Custom(16))
25 .with_format(GfxFormat::Bitmap)
26 .build()
27 .map_err(|e| {
28 let msg = format!("Failed to load {}: {}", lit.value(), e);
29 syn::Error::new(lit.span(), msg)
30 })?;
31
32 Ok(Grit { bitmap })
33 } else {
34 if !input.peek(syn::Token![,]) {
35 return Err(syn::Error::new(input.span(), "Expected comma after input"));
36 }
37 input.parse::<syn::Token![,]>()?;
38
39 let mut builder = BitmapBuilder::new(lit.value());
40
41 while !input.is_empty() {
42 let ident = input.parse::<Ident>()?;
43 input.parse::<syn::Token![=]>()?;
44
45 match ident.to_string().as_str() {
46 "transparency" => {
47 if input.peek(Ident) {
48 let ident = input.parse::<Ident>()?;
49 match ident.to_string().as_str() {
50 "Disabled" => {
51 builder = builder
52 .with_transparency(rgrit_core::Transparency::Disabled)
53 }
54 _ => {
55 return Err(syn::Error::new(
56 ident.span(),
57 "Unknown transparency",
58 ))
59 }
60 }
61 } else if input.peek(LitInt) {
62 todo!()
63 } else {
64 return Err(syn::Error::new(
65 input.span(),
66 "Expected identifier or literal",
67 ));
68 }
69 }
70 "bit_depth" => {
71 if input.peek(Ident) {
72 let ident = input.parse::<Ident>()?;
73 match ident.to_string().as_str() {
74 "A3I5" => {
75 builder =
76 builder.with_bit_depth_override(rgrit_core::BitDepth::A3I5)
77 }
78 "A5I3" => {
79 builder =
80 builder.with_bit_depth_override(rgrit_core::BitDepth::A5I3)
81 }
82 "FourByFour" | "4x4" => {
83 builder = builder
84 .with_bit_depth_override(rgrit_core::BitDepth::FourByFour)
85 }
86 _ => {
87 return Err(syn::Error::new(ident.span(), "Unknown bit depth"))
88 }
89 }
90 } else if input.peek(LitInt) {
91 let lit = input.parse::<LitInt>()?;
92 builder = builder.with_bit_depth_override(
93 rgrit_core::BitDepth::Custom(lit.base10_parse()?),
94 );
95 } else {
96 return Err(syn::Error::new(
97 input.span(),
98 "Expected identifier or literal",
99 ));
100 }
101 }
102 "format" => {
103 let format_ident = input.parse::<Ident>()?;
104
105 match format_ident.to_string().as_str() {
106 "Bitmap" => builder = builder.with_format(GfxFormat::Bitmap),
107 "Tile" => builder = builder.with_format(GfxFormat::Tile),
108 _ => {
109 return Err(syn::Error::new(format_ident.span(), "Unknown format"))
110 }
111 }
112 }
113 "tile_width" => {
114 let lit = input.parse::<LitInt>()?;
115 builder = builder.with_tile_width(lit.base10_parse()?);
116 }
117 "tile_height" => {
118 let lit = input.parse::<LitInt>()?;
119 builder = builder.with_tile_height(lit.base10_parse()?);
120 }
121 "meta_width" => {
122 let lit = input.parse::<LitInt>()?;
123 builder = builder.with_meta_width(lit.base10_parse()?);
124 }
125 "meta_height" => {
126 let lit = input.parse::<LitInt>()?;
127 builder = builder.with_meta_height(lit.base10_parse()?);
128 }
129 "area_left" => {
130 let lit = input.parse::<LitInt>()?;
131 builder = builder.with_area_left(lit.base10_parse()?);
132 }
133 "area_right" => {
134 let lit = input.parse::<LitInt>()?;
135 builder = builder.with_area_right(lit.base10_parse()?);
136 }
137 "area_width" => {
138 let lit = input.parse::<LitInt>()?;
139 builder = builder.with_area_width(lit.base10_parse()?);
140 }
141 "area_top" => {
142 let lit = input.parse::<LitInt>()?;
143 builder = builder.with_area_top(lit.base10_parse()?);
144 }
145 "area_bottom" => {
146 let lit = input.parse::<LitInt>()?;
147 builder = builder.with_area_bottom(lit.base10_parse()?);
148 }
149 "area_height" => {
150 let lit = input.parse::<LitInt>()?;
151 builder = builder.with_area_height(lit.base10_parse()?);
152 }
153 "compression" => {
154 let compression_ident = input.parse::<Ident>()?;
155
156 match compression_ident.to_string().as_str() {
157 "Off" => builder = builder.with_compression(Compression::Off),
158 "LZ77" => builder = builder.with_compression(Compression::LZ77),
159 "Huffman" => builder = builder.with_compression(Compression::Huffman),
160 "RLE" => builder = builder.with_compression(Compression::RLE),
161 "OffHeader" => {
162 builder = builder.with_compression(Compression::OffHeader)
163 }
164 _ => {
165 return Err(syn::Error::new(
166 compression_ident.span(),
167 "Unknown compression",
168 ))
169 }
170 }
171 }
172 _ => return Err(syn::Error::new(ident.span(), "Unknown attribute")),
173 };
174
175 if input.peek(syn::Token![,]) {
176 input.parse::<syn::Token![,]>()?;
177 }
178 }
179
180 let bitmap = builder.build().map_err(|e| {
181 let msg = format!("Failed to load {}: {}", lit.value(), e);
182 syn::Error::new(lit.span(), msg)
183 })?;
184
185 Ok(Grit { bitmap })
186 }
187 }
188}
189
190#[proc_macro]
191pub fn grit(input: TokenStream) -> TokenStream {
192 let input = parse_macro_input!(input as Grit);
193
194 let gfx = input.bitmap.gfx;
196 let palette = input.bitmap.palette;
197 let map = input.bitmap.map;
198 let meta = input.bitmap.meta;
199
200 let bit_depth = match input.bitmap.spec.bit_depth {
202 Some(rgrit_core::BitDepth::A3I5) => quote! { Some(rgrit_core::BitDepth::A3I5) },
203 Some(rgrit_core::BitDepth::A5I3) => quote! { Some(rgrit_core::BitDepth::A5I3) },
204 Some(rgrit_core::BitDepth::FourByFour) => quote! { Some(rgrit_core::BitDepth::FourByFour) },
205 Some(rgrit_core::BitDepth::Custom(n)) => quote! { Some(rgrit_core::BitDepth::Custom(#n)) },
206
207 None => quote! { None },
208 };
209 let format = match input.bitmap.spec.format {
210 rgrit_core::GfxFormat::Bitmap => quote! { rgrit_core::GfxFormat::Bitmap },
211 rgrit_core::GfxFormat::Tile => quote! { rgrit_core::GfxFormat::Tile },
212 };
213 let transparency = match input.bitmap.spec.transparency {
214 rgrit_core::Transparency::Disabled => quote! { rgrit_core::Transparency::Disabled },
215 rgrit_core::Transparency::Color(rgrit_core::Color::RGB { r, g, b }) => {
216 quote! { grit_core::Transparency::Color(rgrit_core::Color::RGB { r: #r, g: #g, b: #b }) }
217 }
218 rgrit_core::Transparency::Color(rgrit_core::Color::GBR16(clr)) => {
219 quote! { grit_core::Transparency::Color(rgrit_core::Color::GBR16(#clr)) }
220 }
221 };
222
223 quote! {
224 rgrit::StaticBitmap {
225 gfx: &[#(#gfx),*],
226 palette: &[#(#palette),*],
227 map: &[#(#map),*],
228 meta: &[#(#meta),*],
229 spec: rgrit_core::BitmapSpec {
230 bit_depth: #bit_depth,
231 format: #format,
232 transparency: #transparency,
233 },
234 }
235 }
236 .into()
237}