rpgmxp_tool/commands/compile_assets/
vx_ace.rs1use super::generate_map_infos_data;
2use super::generate_ruby_data;
3use super::set_extension_str;
4use super::FileSink;
5use anyhow::bail;
6use anyhow::ensure;
7use anyhow::Context;
8use rpgmvx_ace_types::Script;
9use rpgmvx_ace_types::ScriptList;
10use ruby_marshal::IntoValue;
11use std::collections::BTreeMap;
12use std::fs::File;
13use std::path::Path;
14
15fn generate_scripts_data_vx_ace(path: &Path) -> anyhow::Result<Vec<u8>> {
16 let mut scripts_map = BTreeMap::new();
17
18 for dir_entry in path.read_dir()? {
19 let dir_entry = dir_entry?;
20 let dir_entry_file_type = dir_entry.file_type()?;
21
22 ensure!(dir_entry_file_type.is_file());
23
24 let dir_entry_file_name = dir_entry.file_name();
25 let dir_entry_file_name = dir_entry_file_name
26 .to_str()
27 .context("non-unicode script name")?;
28 let dir_entry_file_stem = dir_entry_file_name
29 .strip_suffix(".rb")
30 .context("script is not an \"rb\" file")?;
31
32 let (script_index, escaped_script_name) = dir_entry_file_stem
33 .split_once('-')
34 .context("invalid script name format")?;
35 let script_index: usize = script_index.parse()?;
36 let unescaped_file_name = crate::util::percent_unescape_file_name(escaped_script_name)?;
37
38 println!(" packing script \"{escaped_script_name}\"");
39
40 let dir_entry_path = dir_entry.path();
41 let script_data = std::fs::read_to_string(dir_entry_path)?;
42
43 let old_entry = scripts_map.insert(
44 script_index,
45 Script {
46 data: script_data,
47 id: i32::try_from(script_index)? + 1,
48 name: unescaped_file_name,
49 },
50 );
51 if old_entry.is_some() {
52 bail!("duplicate scripts for index {script_index}");
53 }
54 }
55
56 let script_list = ScriptList {
58 scripts: scripts_map.into_values().collect(),
59 };
60
61 let mut arena = ruby_marshal::ValueArena::new();
62 let handle = script_list.into_value(&mut arena)?;
63 arena.replace_root(handle);
64
65 let mut data = Vec::new();
66 ruby_marshal::dump(&mut data, &arena)?;
67
68 Ok(data)
69}
70
71pub fn compile(
72 entry_path: &Path,
73 entry_file_type: std::fs::FileType,
74 relative_path: &Path,
75 relative_path_components: Vec<&str>,
76 file_sink: &mut FileSink,
77) -> anyhow::Result<()> {
78 match relative_path_components.as_slice() {
79 ["Data", "Scripts.rvdata2"] if entry_file_type.is_dir() => {
80 println!("packing \"{}\"", relative_path.display());
81
82 let scripts_data = generate_scripts_data_vx_ace(entry_path)?;
83 let size = u32::try_from(scripts_data.len())?;
84
85 file_sink.write_file(&relative_path_components, size, &*scripts_data)?;
86 }
87 ["Data", "Scripts.rvdata2", ..] => {
88 }
90 ["Data", "MapInfos.rvdata2"] if entry_file_type.is_dir() => {
91 println!("packing \"{}\"", relative_path.display());
92
93 let data = generate_map_infos_data(entry_path)?;
94 let size = u32::try_from(data.len())?;
95
96 file_sink.write_file(&relative_path_components, size, &*data)?;
97 }
98 ["Data", "MapInfos.rvdata2", ..] => {
99 }
101 ["Data", file] if crate::util::is_map_file_name(file, "json") => {
102 println!("packing \"{}\"", relative_path.display());
103
104 let map_data = generate_ruby_data::<rpgmvx_ace_types::Map>(entry_path)?;
105 let size = u32::try_from(map_data.len())?;
106
107 let renamed_file = set_extension_str(file, "rvdata2");
108 let mut relative_path_components = relative_path_components.clone();
109 *relative_path_components.last_mut().unwrap() = renamed_file.as_str();
110
111 file_sink.write_file(&relative_path_components, size, &*map_data)?;
112 }
113 relative_path_components if entry_file_type.is_file() => {
114 println!("packing \"{}\"", relative_path.display());
116
117 let input_file = File::open(entry_path).with_context(|| {
118 format!(
119 "failed to open input file from \"{}\"",
120 entry_path.display()
121 )
122 })?;
123 let metadata = input_file.metadata()?;
124 let size = u32::try_from(metadata.len())?;
125
126 file_sink.write_file(relative_path_components, size, input_file)?;
127 }
128 _ => {}
129 }
130
131 Ok(())
132}