rpgmxp_tool/commands/extract_assets/
vx_ace.rs

1use super::extract_map_infos;
2use super::extract_ruby_data;
3use super::FileEntry;
4use super::Options;
5use anyhow::Context;
6use ruby_marshal::FromValueContext;
7use std::fs::File;
8use std::path::Path;
9use std::path::PathBuf;
10
11fn extract_scripts_vx_ace<P>(file: impl std::io::Read, dir_path: P) -> anyhow::Result<()>
12where
13    P: AsRef<Path>,
14{
15    let dir_path = dir_path.as_ref();
16    let temp_dir_path = nd_util::with_push_extension(dir_path, "temp");
17
18    // TODO: Lock?
19    // TODO: Drop delete guard for file?
20    std::fs::create_dir_all(&temp_dir_path)?;
21
22    let arena = ruby_marshal::load(file).context("failed to load ruby data")?;
23    let ctx = FromValueContext::new(&arena);
24    let script_list: rpgmvx_ace_types::ScriptList = ctx.from_value(arena.root())?;
25
26    for (script_index, script) in script_list.scripts.iter().enumerate() {
27        println!("  extracting script \"{}\"", script.name);
28
29        let escaped_script_name = crate::util::percent_escape_file_name(&script.name);
30
31        let out_path = temp_dir_path.join(format!("{script_index:03}-{escaped_script_name}.rb"));
32        let temp_path = nd_util::with_push_extension(&out_path, "temp");
33
34        // TODO: Lock?
35        // TODO: Drop delete guard for file?
36        std::fs::write(&temp_path, &script.data)?;
37        std::fs::rename(temp_path, out_path)?;
38    }
39
40    std::fs::rename(temp_dir_path, dir_path)?;
41
42    Ok(())
43}
44
45pub fn extract(
46    options: &Options,
47    entry: &mut FileEntry<'_>,
48    relative_path_components: Vec<&str>,
49    output_path: PathBuf,
50) -> anyhow::Result<()> {
51    match relative_path_components.as_slice() {
52        ["Data", "Scripts.rvdata2"] if !options.skip_extract_scripts => {
53            extract_scripts_vx_ace(entry, output_path)?;
54        }
55        ["Data", "MapInfos.rvdata2"] if !options.skip_extract_map_infos => {
56            extract_map_infos(entry, output_path)?;
57        }
58        ["Data", file]
59            if !options.skip_extract_maps && crate::util::is_map_file_name(file, "rvdata2") =>
60        {
61            extract_ruby_data::<rpgmvx_ace_types::Map>(entry, output_path)?;
62        }
63        _ => {
64            let temp_path = nd_util::with_push_extension(&output_path, "temp");
65            // TODO: Lock?
66            // TODO: Drop delete guard for file?
67            let mut output_file = File::create(&temp_path)
68                .with_context(|| format!("failed to open file at \"{}\"", output_path.display()))?;
69
70            std::io::copy(entry, &mut output_file)?;
71            std::fs::rename(&temp_path, &output_path)?;
72        }
73    }
74
75    Ok(())
76}