Skip to content

Commit

Permalink
feat: implement DAR hinder & OAR remover
Browse files Browse the repository at this point in the history
  • Loading branch information
SARDONYX-sard committed Oct 12, 2023
1 parent 19807e5 commit ae852d0
Show file tree
Hide file tree
Showing 23 changed files with 599 additions and 163 deletions.
1 change: 1 addition & 0 deletions cspell.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"Greatsword",
"icns",
"jwalk",
"mohidden",
"multispace",
"noconflict",
"rfind",
Expand Down
51 changes: 31 additions & 20 deletions dar2oar_cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use clap::{arg, Parser};
use dar2oar_core::{convert_dar_to_oar, fs::parallel, read_mapping_table};
use dar2oar_core::{
convert_dar_to_oar,
fs::{parallel, ConvertOptions},
read_mapping_table,
};
use std::path::PathBuf;

/// dar2oar --src "DAR path" --dist "OAR path"
Expand All @@ -12,7 +16,7 @@ pub struct Args {
/// OAR destination dir path(If not, it is inferred from src)
#[clap(long, value_parser)]
dist: Option<String>,
/// mod name in config.json & folder name(If not, it is inferred from src)
/// mod name in config.json & directory name(If not, it is inferred from src)
#[arg(long)]
name: Option<String>,
/// mod author in config.json
Expand All @@ -33,6 +37,9 @@ pub struct Args {
/// use multi thread(Probably effective for those with long DAR syntax. Basically single-threaded is faster.)
#[arg(long)]
run_parallel: bool,
#[arg(long)]
/// After converting to OAR, add mohidden to the DAR directory before conversion to treat it as a hidden directory. (for MO2 users)
hide_dar: bool,
}

pub fn run_cli(args: Args) -> anyhow::Result<()> {
Expand Down Expand Up @@ -64,22 +71,26 @@ pub fn run_cli(args: Args) -> anyhow::Result<()> {
None => None,
};

match args.run_parallel {
true => parallel::convert_dar_to_oar(
args.src,
dist,
args.name.as_deref(),
args.author.as_deref(),
table,
table_1person,
),
false => convert_dar_to_oar(
args.src,
dist,
args.name.as_deref(),
args.author.as_deref(),
table,
table_1person,
),
}
let msg = match args.run_parallel {
true => parallel::convert_dar_to_oar(ConvertOptions {
dar_dir: args.src,
oar_dir: dist,
mod_name: args.name.as_deref(),
author: args.author.as_deref(),
section_table: table,
section_1person_table: table_1person,
hide_dar: args.hide_dar,
}),
false => convert_dar_to_oar(ConvertOptions {
dar_dir: args.src,
oar_dir: dist,
mod_name: args.name.as_deref(),
author: args.author.as_deref(),
section_table: table,
section_1person_table: table_1person,
hide_dar: args.hide_dar,
}),
}?;
log::debug!("{}", msg);
Ok(())
}
18 changes: 15 additions & 3 deletions dar2oar_core/benches/convert_n_thread.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dar2oar_core::{convert_dar_to_oar, fs::parallel, read_mapping_table};
use dar2oar_core::{
convert_dar_to_oar,
fs::{parallel, ConvertOptions},
read_mapping_table,
};
use std::time::Duration;

const REMOVE_TARGET: &str =
Expand Down Expand Up @@ -28,7 +32,11 @@ fn criterion_benchmark(c: &mut Criterion) {
let table_content = "../test/settings/mapping_table.txt";
let mapping = read_mapping_table(table_content).unwrap();

parallel::convert_dar_to_oar(black_box(TARGET), None, None, None, Some(mapping), None)
parallel::convert_dar_to_oar(black_box(ConvertOptions {
dar_dir: TARGET,
section_table: Some(mapping),
..Default::default()
}))
})
});

Expand All @@ -40,7 +48,11 @@ fn criterion_benchmark(c: &mut Criterion) {
let table_content = "../test/settings/mapping_table.txt";
let mapping = read_mapping_table(table_content).unwrap();

convert_dar_to_oar(black_box(TARGET), None, None, None, Some(mapping), None)
convert_dar_to_oar(black_box(ConvertOptions {
dar_dir: TARGET,
section_table: Some(mapping),
..Default::default()
}))
})
});

Expand Down
123 changes: 122 additions & 1 deletion dar2oar_core/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,28 @@ pub use sequential::convert_dar_to_oar;

use crate::conditions::{ConditionsConfig, MainConfig};
use anyhow::Context as _;
use std::collections::HashMap;
use std::fs;
use std::io::{self, Read, Write};
use std::path::Path;
use std::path::{Path, PathBuf};

#[derive(Debug, Default, PartialEq)]
pub struct ConvertOptions<'a, P: AsRef<Path>> {
/// DAR source dir path
pub dar_dir: P,
/// OAR destination dir path(If not, it is inferred from src)
pub oar_dir: Option<PathBuf>,
/// mod name in config.json & directory name(If not, it is inferred from src)
pub mod_name: Option<&'a str>,
/// mod author in config.json
pub author: Option<&'a str>,
/// path to section name table
pub section_table: Option<HashMap<String, String>>,
/// path to section name table(For _1st_person)
pub section_1person_table: Option<HashMap<String, String>>,
/// After converting to OAR, add mohidden to the DAR directory before conversion to treat it as a hidden directory. (for MO2 users)
pub hide_dar: bool,
}

fn read_file<P>(file_path: P) -> io::Result<String>
where
Expand Down Expand Up @@ -64,3 +83,105 @@ where
config_file.write_all(json.as_bytes())?;
Ok(())
}

/// # Returns
/// Report which dirs have been restored
///
/// # NOTE
/// It is currently used only in GUI, but is implemented in Core as an API.
pub fn restore_dar(dar_dir: impl AsRef<Path>) -> anyhow::Result<String> {
let mut restored_dar = None;
let mut restored_1st_dar = None;
for entry in walkdir::WalkDir::new(dar_dir) {
let entry = entry?;
let path = entry.path();
let (dar_root, _, is_1st_person, _, _, _) =
match path_changer::parse_dar_path(path, Some("DynamicAnimationReplacer.mohidden")) {
Ok(data) => data,
Err(_) => continue, // NOTE: The first search is skipped because it does not yet lead to the DAR file.
};

if restored_dar.is_none() && path.is_dir() {
restored_dar = Some(dar_root);
continue;
}
if restored_1st_dar.is_none() && path.is_dir() && is_1st_person {
restored_1st_dar = Some(dar_root);
}
}

let mut msg = String::new();
if let Some(dar_root) = restored_dar.as_ref() {
let dist = dar_root
.as_os_str()
.to_string_lossy()
.replace(".mohidden", "");
fs::rename(dar_root.clone(), dist)?;
msg = format!("{}- Restored 3rd_person", msg);
}
if let Some(dar_root) = restored_1st_dar.as_ref() {
let dist = dar_root
.as_os_str()
.to_string_lossy()
.replace(".mohidden", "");
fs::rename(dar_root.clone(), dist)?;
msg = format!("{}\n- Restored 1rd_person", msg);
}

if restored_dar.is_none() && restored_1st_dar.is_none() {
anyhow::bail!("Neither 1st or 3rd person DynamicAnimationReplacer.mohidden found.")
} else {
Ok(msg)
}
}

/// # NOTE
/// It is currently used only in GUI, but is implemented in Core as an API.
pub fn remove_oar(dar_dir: impl AsRef<Path>) -> anyhow::Result<()> {
let mut restored_dar = None;
let mut restored_1st_dar = None;
for entry in walkdir::WalkDir::new(dar_dir) {
let entry = entry?;
let path = entry.path();
// NOTE: The OAR root obtained by parse fn is calculated and not guaranteed to exist.
let (dar_root, oar_name_space_path, is_1st_person, _, _, _) =
match path_changer::parse_dar_path(path, Some("DynamicAnimationReplacer.mohidden")) {
Ok(data) => data,
Err(_) => {
match path_changer::parse_dar_path(path, Some("DynamicAnimationReplacer")) {
Ok(data) => data,
Err(_) => continue, // NOTE: The first search is skipped because it does not yet lead to the DAR file.
}
} // NOTE: The first search is skipped because it does not yet lead to the DAR file.
};

if restored_dar.is_none() && path.is_dir() {
restored_dar = Some((dar_root, oar_name_space_path));
continue;
}
if restored_1st_dar.is_none() && path.is_dir() && is_1st_person {
restored_1st_dar = Some((dar_root, oar_name_space_path));
}
}

if let Some((_, oar_root)) = restored_dar {
dbg!(&oar_root);
if oar_root.exists() {
fs::remove_dir_all(oar_root)?;
}
}
if let Some((_, oar_root)) = restored_1st_dar {
dbg!(&oar_root);
if oar_root.exists() {
fs::remove_dir_all(oar_root)?;
}
}
Ok(())
}

#[ignore]
#[test]
fn should_restore_dar() {
let dar_dir = "../test/data/UNDERDOG Animations";
remove_oar(dar_dir).unwrap();
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,36 @@
use crate::condition_parser::parse_dar2oar;
use crate::conditions::ConditionsConfig;
use crate::fs::path_changer::parse_dar_path;
use crate::fs::{read_file, write_name_space_config, write_section_config};
use anyhow::{Context as _, Result, bail};
use crate::fs::{read_file, write_name_space_config, write_section_config, ConvertOptions};
use anyhow::{bail, Context as _, Result};
use jwalk::WalkDir;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::path::Path;

/// multi thread converter
pub fn convert_dar_to_oar<P>(
dar_dir: P,
oar_dir: Option<PathBuf>,
mod_name: Option<&str>,
author: Option<&str>,
section_table: Option<HashMap<String, String>>,
section_1person_table: Option<HashMap<String, String>>,
) -> Result<()>
where
P: AsRef<Path>,
{
/// # Return
/// Complete info
pub fn convert_dar_to_oar(options: ConvertOptions<impl AsRef<Path>>) -> Result<String> {
let ConvertOptions {
dar_dir,
oar_dir,
mod_name,
author,
section_table,
section_1person_table,
hide_dar,
} = options;
let mut is_converted_once = false;
let mut dar_namespace = None; // To need rename to hidden
let mut dar_1st_namespace = None; // To need rename to hidden(For _1stperson)

for entry in WalkDir::new(dar_dir) {
let entry = entry?;
let path = entry.path(); // Separate this for binding
let path = path.as_path();
let (oar_name_space_path, is_1st_person, parsed_mod_name, priority, remain) =
match parse_dar_path(path) {

let (dar_root, oar_name_space_path, is_1st_person, parsed_mod_name, priority, remain) =
match parse_dar_path(path, None) {
Ok(data) => data,
Err(_) => continue, // NOTE: The first search is skipped because it does not yet lead to the DAR file.
};
Expand Down Expand Up @@ -88,6 +91,13 @@ where
Err(err) => log::error!("Error reading file {path:?}: {err}"),
}

if is_1st_person {
if dar_1st_namespace.is_none() {
dar_1st_namespace = Some(dar_root);
}
} else if dar_namespace.is_none() {
dar_namespace = Some(dar_root);
}
if !is_converted_once {
is_converted_once = true;
write_name_space_config(&oar_name_space_path, &parsed_mod_name, author)
Expand All @@ -99,7 +109,7 @@ where
})?;
}
} else {
// maybe motion files(.hkx)
// maybe motion files(.kkx)
if let Some(remain) = remain {
let non_leaf_dir = section_root.join(remain);
fs::create_dir_all(&non_leaf_dir)?;
Expand All @@ -112,7 +122,25 @@ where
}

match is_converted_once {
true => Ok(()),
true => {
let mut msg = "Conversion Completed.".to_string();
if hide_dar {
if let Some(dar_namespace) = dar_namespace {
let mut dist = dar_namespace.clone();
dist.as_mut_os_string().push(".mohidden");
fs::rename(dar_namespace, dist)?;
msg = format!("{}\n- 3rdPerson DAR dir was renamed", msg);
};

if let Some(dar_1st_namespace) = dar_1st_namespace {
let mut dist = dar_1st_namespace.clone();
dist.as_mut_os_string().push(".mohidden");
fs::rename(dar_1st_namespace, dist)?;
msg = format!("{}\n- 1stPerson DAR dir was renamed", msg);
};
}
Ok(msg)
}
false => bail!("DynamicAnimationReplacer dir was never found"),
}
}
Loading

0 comments on commit ae852d0

Please sign in to comment.