1#![allow(dead_code, unused_imports)]
2
3use lazy_format::lazy_format;
4use std::env;
5use std::fmt::Display;
6use std::format as fmt;
7use std::path::{Path, PathBuf};
8use std::fs::{File, DirEntry};
9use std::io;
10use structopt::StructOpt;
11
12mod dep;
13mod draw;
14mod parse;
15mod util;
16
17use dep::*;
18use draw::*;
19use parse::*;
20use util::*;
21
22pub type SgSpriteErr = anyhow::Error;
23use anyhow::{anyhow as raise_e, bail as raise};
24
25pub fn print_err(e: impl Display) {
26 eprintln!("[E] {}", e);
27}
28
29#[derive(StructOpt, Debug)]
30#[structopt()]
31pub struct Opts {
32 #[structopt(short, long, parse(from_os_str))]
34 pub dir: Option<PathBuf>,
35
36 #[structopt(short, long)]
38 pub limit: Option<usize>,
39
40 #[structopt(name = "LAY_FILES", parse(from_os_str))]
42 pub lay_files: Vec<PathBuf>,
43
44 #[structopt(long)]
47 pub dry_run: bool,
48}
49
50const LAY_EXT: &[&str] = &["_.lay", ".lay"];
51
52pub fn lib_main(o: &Opts) -> Result<(), SgSpriteErr> {
53 let layouts = &o.lay_files;
54 let out_dir = o.dir.as_ref();
55
56 match out_dir {
57 Some(d) if !d.is_dir() => raise!("out_dir isn't a directory"),
58 None if !o.dry_run => {
59 raise!("Output dir should be specified (-d)\nSee --help for details");
60 }
61 _ => (),
62 }
63
64 let total = o.lay_files.len();
65
66 if total == 0 {
67 raise!("no .lay files provided");
68 }
69
70 let status = |c: usize| move |t: &str| println!("[{}/{}] {}", c + 1, total, t);
71
72 for i in 0..layouts.len() {
73 let lay_path = &layouts[i];
74 if let Err(e) = lay_in(out_dir, lay_path, o.limit, status(i)) {
75 print_err(e);
76 print_err(format_args!("({})", lay_path.display()));
77 }
78 }
79
80 Ok(())
81}
82
83fn lay_in(
84 out_dir: Option<impl AsRef<Path>>,
85 lay_file: &Path,
86 limit: Option<usize>,
87 status_cb: impl Fn(&str),
88) -> Result<(), SgSpriteErr> {
89 let limit = limit.unwrap_or(0);
90
91 let (lay_filename, lay_ext) = lay_file
92 .file_name()
93 .and_then(|n| n.to_str())
94 .and_then(|f| LAY_EXT.iter().find(|e| f.ends_with(**e)).map(|e| (f, e)))
95 .ok_or_else(|| raise_e!("not a lay file"))?;
96
97 let sprite_name =
98 lay_filename.trim_end_matches(lay_ext);
99
100 status_cb(sprite_name);
101
102 let lay = parse_lay(&mut File::open(lay_file)?)?;
103 let graph = DepGraph::resolve_dep_graph(&lay);
104 let leaves = graph.get_leaf_sprites();
105
106 if let Some(out_dir) = out_dir {
107 let src_image_path: PathBuf = {
108 let mut path_buf = lay_file.canonicalize().expect("Can't canonicalize .lay path");
109 let parent_dir = path_buf.parent().expect("No parent dir");
110 let src_filename = parent_dir.read_dir()
113 .unwrap()
114 .flatten()
115 .find(|f|
116 f.file_name().to_str()
117 .map(|n| n.starts_with(sprite_name) && n.ends_with(".png"))
118 .unwrap_or(false)
119 )
120 .ok_or_else(|| raise_e!("No corresponding png file"))?;
121
122 path_buf.pop();
123 path_buf.push(src_filename.file_name());
124 path_buf
125 };
126
127 println!("[I] Using source file: {}",
128 src_image_path.file_name().unwrap().to_str().unwrap_or("???"));
129
130 let mut src_image = draw_prep(&src_image_path)?;
131
132 for (pass, sp) in leaves.enumerate() {
133 if limit > 0 && pass >= limit {
134 eprintln!("[I] Limit reached, proceeding to next sprite");
135 break;
136 }
137
138 let s = sp.sprite;
139 let name_suf = lazy_format!(match (s.sprite_type) {
140 SpriteT::Base => ("b{}", s.id),
141 SpriteT::Sub => ("s{}", s.id),
142 SpriteT::Overlay => ("o{}", s.id),
143 SpriteT::Dep { exact_type: st, depends_on: dep } => ("t{}_d{}_{}", st, dep, s.id),
144 });
145
146 let layers = graph.resolve_layers(sp)?;
147 let mut out_path = out_dir.as_ref().to_path_buf();
148 out_path.push(fmt!("{}_{}.png", sprite_name, name_suf));
149
150 if let Err(e) = draw_sprites(&mut src_image, &out_path, layers.as_ref(), pass + 1, &lay) {
151 print_err(e);
152 }
153 }
154 } else {
155 for sp in leaves {
156 graph.resolve_layers(sp)?; }
158 }
159
160 Ok(())
161}