From ae852d0757a0a7515856bf09ffbabf2c9c9a0a6e Mon Sep 17 00:00:00 2001
From: SARDONYX-sard <68905624+SARDONYX-sard@users.noreply.github.com>
Date: Fri, 13 Oct 2023 05:35:27 +0900
Subject: [PATCH] feat: implement DAR hinder & OAR remover
---
cspell.jsonc | 1 +
dar2oar_cli/src/lib.rs | 51 +++++---
dar2oar_core/benches/convert_n_thread.rs | 18 ++-
dar2oar_core/src/fs/mod.rs | 123 +++++++++++++++++-
.../src/fs/{parallel/mod.rs => parallel.rs} | 66 +++++++---
dar2oar_core/src/fs/path_changer.rs | 45 +++++--
.../fs/{sequential/mod.rs => sequential.rs} | 78 +++++++----
frontend/src/app/layout.tsx | 2 +-
.../components/{ => buttons}/convert_btn.tsx | 0
.../components/{ => buttons}/log_file_btn.tsx | 0
.../{ => buttons}/path_selector.tsx | 4 +-
.../src/components/buttons/remove_oar_btn.tsx | 49 +++++++
.../components/buttons/restore_dar_btn.tsx | 41 ++++++
frontend/src/components/form.tsx | 115 ++++++++++++----
.../components/{ => lists}/editor_list.tsx | 2 +-
.../{ => lists}/select_log_level.tsx | 2 +-
.../src/components/{ => lists}/style_list.tsx | 2 +-
frontend/src/components/pages/converter.tsx | 11 +-
frontend/src/components/pages/settings.tsx | 24 ++--
.../src/components/{ => providers}/theme.tsx | 0
frontend/src/tauri_cmd/index.ts | 50 ++++---
src-tauri/src/cmd.rs | 72 ++++++----
src-tauri/src/runner.rs | 6 +-
23 files changed, 599 insertions(+), 163 deletions(-)
rename dar2oar_core/src/fs/{parallel/mod.rs => parallel.rs} (68%)
rename dar2oar_core/src/fs/{sequential/mod.rs => sequential.rs} (70%)
rename frontend/src/components/{ => buttons}/convert_btn.tsx (100%)
rename frontend/src/components/{ => buttons}/log_file_btn.tsx (100%)
rename frontend/src/components/{ => buttons}/path_selector.tsx (80%)
create mode 100644 frontend/src/components/buttons/remove_oar_btn.tsx
create mode 100644 frontend/src/components/buttons/restore_dar_btn.tsx
rename frontend/src/components/{ => lists}/editor_list.tsx (94%)
rename frontend/src/components/{ => lists}/select_log_level.tsx (96%)
rename frontend/src/components/{ => lists}/style_list.tsx (95%)
rename frontend/src/components/{ => providers}/theme.tsx (100%)
diff --git a/cspell.jsonc b/cspell.jsonc
index 5123700..259af7a 100644
--- a/cspell.jsonc
+++ b/cspell.jsonc
@@ -7,6 +7,7 @@
"Greatsword",
"icns",
"jwalk",
+ "mohidden",
"multispace",
"noconflict",
"rfind",
diff --git a/dar2oar_cli/src/lib.rs b/dar2oar_cli/src/lib.rs
index 28b2c5c..aebe9f0 100644
--- a/dar2oar_cli/src/lib.rs
+++ b/dar2oar_cli/src/lib.rs
@@ -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"
@@ -12,7 +16,7 @@ pub struct Args {
/// OAR destination dir path(If not, it is inferred from src)
#[clap(long, value_parser)]
dist: Option,
- /// 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,
/// mod author in config.json
@@ -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<()> {
@@ -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(())
}
diff --git a/dar2oar_core/benches/convert_n_thread.rs b/dar2oar_core/benches/convert_n_thread.rs
index c57e1b4..ffda5c0 100644
--- a/dar2oar_core/benches/convert_n_thread.rs
+++ b/dar2oar_core/benches/convert_n_thread.rs
@@ -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 =
@@ -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()
+ }))
})
});
@@ -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()
+ }))
})
});
diff --git a/dar2oar_core/src/fs/mod.rs b/dar2oar_core/src/fs/mod.rs
index 5a3c2de..1ec9099 100644
--- a/dar2oar_core/src/fs/mod.rs
+++ b/dar2oar_core/src/fs/mod.rs
@@ -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> {
+ /// DAR source dir path
+ pub dar_dir: P,
+ /// OAR destination dir path(If not, it is inferred from src)
+ pub oar_dir: Option,
+ /// 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>,
+ /// path to section name table(For _1st_person)
+ pub section_1person_table: Option>,
+ /// 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
(file_path: P) -> io::Result
where
@@ -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) -> 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();
+ 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) -> 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();
+}
diff --git a/dar2oar_core/src/fs/parallel/mod.rs b/dar2oar_core/src/fs/parallel.rs
similarity index 68%
rename from dar2oar_core/src/fs/parallel/mod.rs
rename to dar2oar_core/src/fs/parallel.rs
index 3c8e787..2d9dfa0 100644
--- a/dar2oar_core/src/fs/parallel/mod.rs
+++ b/dar2oar_core/src/fs/parallel.rs
@@ -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
(
- dar_dir: P,
- oar_dir: Option,
- mod_name: Option<&str>,
- author: Option<&str>,
- section_table: Option>,
- section_1person_table: Option>,
-) -> Result<()>
-where
- P: AsRef,
-{
+/// # Return
+/// Complete info
+pub fn convert_dar_to_oar(options: ConvertOptions>) -> Result {
+ 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.
};
@@ -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)
@@ -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)?;
@@ -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"),
}
}
diff --git a/dar2oar_core/src/fs/path_changer.rs b/dar2oar_core/src/fs/path_changer.rs
index 8c5e020..718b57d 100644
--- a/dar2oar_core/src/fs/path_changer.rs
+++ b/dar2oar_core/src/fs/path_changer.rs
@@ -5,6 +5,7 @@ use std::path::{Path, PathBuf};
/// - Ok: oar root path, is_1st_person_dir, mod name, priority, non leaf remain(Path excluding file from priority to the end)
/// - Err: anyhow Error
type DarPathsResult = Result<(
+ PathBuf,
PathBuf,
bool,
Option,
@@ -29,20 +30,23 @@ type DarPathsResult = Result<(
///
/// # DAR:
/// - "\/\/_1stperson/character/animations/DynamicAnimationReplacer/_CustomConditions/\/_conditions.txt"
-pub fn parse_dar_path(path: impl AsRef) -> DarPathsResult {
+pub fn parse_dar_path(path: impl AsRef, dar_dirname: Option<&str>) -> DarPathsResult {
let path = path.as_ref();
let paths: Vec<&OsStr> = path.iter().collect();
let is_1st_person = path.iter().any(|os_str| os_str == OsStr::new("_1stperson"));
- let oar_root = path
+ let (dar_root, oar_root) = path
.iter()
- .position(|os_str| os_str == OsStr::new("DynamicAnimationReplacer"))
+ .position(|os_str| os_str == OsStr::new(dar_dirname.unwrap_or("DynamicAnimationReplacer")))
.and_then(|idx| {
paths.get(0..idx).map(|path| {
- let mut string = path.join(OsStr::new("/"));
- string.push("/OpenAnimationReplacer");
- Path::new(&string).to_path_buf()
+ let mut dar = path.join(OsStr::new("/"));
+ let mut oar = dar.clone();
+ dar.push("/");
+ dar.push(dar_dirname.unwrap_or("DynamicAnimationReplacer"));
+ oar.push("/OpenAnimationReplacer");
+ (Path::new(&dar).to_path_buf(), Path::new(&oar).to_path_buf())
})
})
.with_context(|| {
@@ -93,7 +97,14 @@ pub fn parse_dar_path(path: impl AsRef) -> DarPathsResult {
})
});
- Ok((oar_root, is_1st_person, mod_name, priority, non_leaf_remain))
+ Ok((
+ dar_root,
+ oar_root,
+ is_1st_person,
+ mod_name,
+ priority,
+ non_leaf_remain,
+ ))
}
#[cfg(test)]
@@ -105,11 +116,15 @@ mod test {
#[test]
fn test_parse_dar_path_1st_person() {
let path = Path::new("../ModName/meshes/actors/character/_1stperson/animations/DynamicAnimationReplacer/_CustomConditions/8107000/_conditions.txt");
- let result = parse_dar_path(path);
+ let result = parse_dar_path(path, None);
assert!(result.is_ok());
- let (oar_root, is_1st_person, mod_name, priority, remain) = result.unwrap();
+ let (dar_root, oar_root, is_1st_person, mod_name, priority, remain) = result.unwrap();
+ assert_eq!(
+ dar_root,
+ PathBuf::from("../ModName/meshes/actors/character/_1stperson/animations/DynamicAnimationReplacer")
+ );
assert_eq!(
oar_root,
PathBuf::from(
@@ -124,12 +139,16 @@ mod test {
#[test]
fn test_parse_dar_path_3rd_person() {
- let path = Path::new("../ModName/meshes/actors/character/animations/DynamicAnimationReplacer/_CustomConditions/8107000/InnerDir/_conditions.txt");
- let result = parse_dar_path(path);
+ let path = Path::new("../ModName/meshes/actors/character/animations/DynamicAnimationReplacer.mohidden/_CustomConditions/8107000/InnerDir/_conditions.txt");
+ let result = parse_dar_path(path, Some("DynamicAnimationReplacer.mohidden"));
assert!(result.is_ok());
- let (oar_root, is_1st_person, mod_name, priority, remain) = result.unwrap();
+ let (dar_root, oar_root, is_1st_person, mod_name, priority, remain) = result.unwrap();
+ assert_eq!(
+ dar_root,
+ PathBuf::from("../ModName/meshes/actors/character/animations/DynamicAnimationReplacer.mohidden")
+ );
assert_eq!(
oar_root,
PathBuf::from("../ModName/meshes/actors/character/animations/OpenAnimationReplacer")
@@ -145,7 +164,7 @@ mod test {
// Create a path with invalid UTF-8 characters
let invalid_path = OsStr::new("invalid_path").to_os_string();
let path = Path::new(&invalid_path);
- let result = parse_dar_path(path);
+ let result = parse_dar_path(path, None);
assert!(result.is_err());
}
}
diff --git a/dar2oar_core/src/fs/sequential/mod.rs b/dar2oar_core/src/fs/sequential.rs
similarity index 70%
rename from dar2oar_core/src/fs/sequential/mod.rs
rename to dar2oar_core/src/fs/sequential.rs
index 456a220..b9acd12 100644
--- a/dar2oar_core/src/fs/sequential/mod.rs
+++ b/dar2oar_core/src/fs/sequential.rs
@@ -1,32 +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 crate::fs::{read_file, write_name_space_config, write_section_config, ConvertOptions};
use anyhow::{bail, Context as _, Result};
-use std::collections::HashMap;
use std::fs;
-use std::path::{Path, PathBuf};
+use std::path::Path;
use walkdir::WalkDir;
/// Single thread converter
-pub fn convert_dar_to_oar
(
- dar_dir: P,
- oar_dir: Option,
- mod_name: Option<&str>,
- author: Option<&str>,
- section_table: Option>,
- section_1person_table: Option>,
-) -> Result<()>
-where
- P: AsRef,
-{
+///
+/// # Return
+/// Complete info
+pub fn convert_dar_to_oar(options: ConvertOptions>) -> Result {
+ 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();
- 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.
};
@@ -87,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)
@@ -111,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"),
}
}
@@ -137,13 +166,12 @@ mod test {
let table =
crate::read_mapping_table("../test/settings/UnderDog Animations_mapping_table.txt")
.unwrap();
- convert_dar_to_oar(
- "../test/data/UNDERDOG Animations",
- None,
- None,
- None,
- Some(table),
- None,
- )
+ convert_dar_to_oar(ConvertOptions {
+ dar_dir: "../test/data/UNDERDOG Animations",
+ section_table: Some(table),
+ hide_dar: true,
+ ..Default::default()
+ })?;
+ Ok(())
}
}
diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx
index bd42e93..6427105 100644
--- a/frontend/src/app/layout.tsx
+++ b/frontend/src/app/layout.tsx
@@ -11,7 +11,7 @@ const Menu = dynamic(() => import("../components/navigation"), {
loading: () => ,
ssr: false,
});
-const ThemeProvider = dynamic(() => import("../components/theme"), {
+const ThemeProvider = dynamic(() => import("../components/providers/theme"), {
loading: () => ,
ssr: false,
diff --git a/frontend/src/components/convert_btn.tsx b/frontend/src/components/buttons/convert_btn.tsx
similarity index 100%
rename from frontend/src/components/convert_btn.tsx
rename to frontend/src/components/buttons/convert_btn.tsx
diff --git a/frontend/src/components/log_file_btn.tsx b/frontend/src/components/buttons/log_file_btn.tsx
similarity index 100%
rename from frontend/src/components/log_file_btn.tsx
rename to frontend/src/components/buttons/log_file_btn.tsx
diff --git a/frontend/src/components/path_selector.tsx b/frontend/src/components/buttons/path_selector.tsx
similarity index 80%
rename from frontend/src/components/path_selector.tsx
rename to frontend/src/components/buttons/path_selector.tsx
index d679281..00748c7 100644
--- a/frontend/src/components/path_selector.tsx
+++ b/frontend/src/components/buttons/path_selector.tsx
@@ -1,6 +1,6 @@
import { Button } from "@mui/material";
import toast from "react-hot-toast";
-import { openPath } from "../tauri_cmd";
+import { openPath } from "../../tauri_cmd";
type Props = {
path: string;
@@ -8,7 +8,7 @@ type Props = {
setValue: (value: string) => void;
};
-export function PathSelector({ path, isDir = false, setValue }: Props) {
+export function SelectPathButton({ path, isDir = false, setValue }: Props) {
const handleClick = async () => {
openPath(path, setValue, isDir).catch((e) => toast.error(`${e}`));
};
diff --git a/frontend/src/components/buttons/remove_oar_btn.tsx b/frontend/src/components/buttons/remove_oar_btn.tsx
new file mode 100644
index 0000000..9dca8ce
--- /dev/null
+++ b/frontend/src/components/buttons/remove_oar_btn.tsx
@@ -0,0 +1,49 @@
+import { removeOarDir } from "@/tauri_cmd";
+import toast from "react-hot-toast";
+import DeleteIcon from "@mui/icons-material/Delete";
+import Button from "@mui/material/Button";
+import Tooltip from "@mui/material/Tooltip";
+
+type Props = {
+ darPath: string;
+ oarPath: string;
+};
+
+export const RemoveOarBtn = ({ darPath, oarPath }: Props) => {
+ return (
+
+ Find and delete OAR dir from "DAR(src) Directory*" or
+ "OAR(dist) Directory".
+
+ }
+ >
+
+
+ );
+};
diff --git a/frontend/src/components/buttons/restore_dar_btn.tsx b/frontend/src/components/buttons/restore_dar_btn.tsx
new file mode 100644
index 0000000..f8f36ea
--- /dev/null
+++ b/frontend/src/components/buttons/restore_dar_btn.tsx
@@ -0,0 +1,41 @@
+import { Tooltip } from "@mui/material";
+import Button from "@mui/material/Button";
+import { restoreDarDir } from "@/tauri_cmd";
+import toast from "react-hot-toast";
+import RestoreIcon from "@mui/icons-material/Restore";
+
+type Props = {
+ path: string;
+};
+
+export const RestoreDarBtn = ({ path }: Props) => {
+ return (
+
+ Restore the directory hidden by "Hide DAR".(For MO2 user)
+
+ }
+ >
+
+
+ );
+};
diff --git a/frontend/src/components/form.tsx b/frontend/src/components/form.tsx
index 612a1ef..1c9af1e 100644
--- a/frontend/src/components/form.tsx
+++ b/frontend/src/components/form.tsx
@@ -12,9 +12,11 @@ import type { SubmitHandler } from "react-hook-form";
import { Controller, useForm } from "react-hook-form";
import toast from "react-hot-toast";
import { convertDar2oar } from "../tauri_cmd";
-import ConvertButton from "./convert_btn";
-import { PathSelector } from "./path_selector";
-import { LogFileButton } from "./log_file_btn";
+import ConvertButton from "./buttons/convert_btn";
+import { LogFileButton } from "./buttons/log_file_btn";
+import { SelectPathButton } from "./buttons/path_selector";
+import { RemoveOarBtn } from "./buttons/remove_oar_btn";
+import { RestoreDarBtn } from "./buttons/restore_dar_btn";
type FormProps = {
src: string;
@@ -25,10 +27,11 @@ type FormProps = {
mapping1personPath: string;
loading: boolean;
runParallel: boolean;
+ hideDar: boolean;
};
export function ConvertForm() {
- const { handleSubmit, control, setValue } = useForm({
+ const { handleSubmit, control, setValue, getValues } = useForm({
mode: "onBlur",
criteriaMode: "all",
shouldFocusError: false,
@@ -40,6 +43,7 @@ export function ConvertForm() {
mappingPath: localStorage.getItem("mappingPath") ?? "",
mapping1personPath: localStorage.getItem("mapping1personPath") ?? "",
runParallel: localStorage.getItem("runParallel") === "true",
+ hideDar: localStorage.getItem("hideDar") === "true",
loading: false as boolean,
} satisfies FormProps,
});
@@ -72,22 +76,27 @@ export function ConvertForm() {
mappingPath,
mapping1personPath,
runParallel,
+ hideDar,
}) => {
setLoading(true);
- await convertDar2oar({
- src,
- dist,
- modName,
- modAuthor,
- mappingPath,
- mapping1personPath,
- runParallel,
- }).catch((e) => {
- toast.error(`${e}`);
+ try {
+ const completeInfo = await convertDar2oar({
+ src,
+ dist,
+ modName,
+ modAuthor,
+ mappingPath,
+ mapping1personPath,
+ runParallel,
+ hideDar,
+ });
+ toast.success(completeInfo);
+ } catch (err) {
+ toast.error(`${err}`);
+ } finally {
setLoading(false);
- });
- setLoading(false);
+ }
};
return (
@@ -148,7 +157,11 @@ export function ConvertForm() {
-
+
)}
@@ -182,7 +195,7 @@ export function ConvertForm() {
/>
-
-
@@ -257,7 +270,7 @@ export function ConvertForm() {
-
@@ -322,13 +335,26 @@ export function ConvertForm() {
+
-
+
+ (
-
+
+ Use multi-threading.
+
+ In most cases, it slows down by tens of ms, but may be
+ effective when there is more weight on CPU processing with
+ fewer files to copy and more logic parsing of
+ "_condition.txt"
+
+ }
+ >
+
+
+ (
+
+ After conversion, append ".mohidden" to the DAR
+ dirname in "DAR(src) Directory*" to make it a
+ hidden directory(For MO2 users)
+
+
+ NOTE: Failure to cross the drive or No permission.
+