diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 2f05de5..e6d2867 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -16,7 +16,8 @@ jobs:
fail-fast: false
matrix:
platform:
- # TODO 其他平台均支持打包,但未经过测试,故暂时屏蔽掉
+ # TODO other platforms are supported for packaging, but not tested, so they are temporarily disabled
+ # 其他平台均支持打包,但未经过测试,故暂时屏蔽掉
# - "macos-latest"
# - "ubuntu-latest"
- "windows-latest"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..a99538a
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,34 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+### Added
+
+- v1.1 Brazilian Portuguese translation.
+- v1.1 German Translation
+- v1.1 Spanish translation.
+- v1.1 Italian translation.
+- v1.1 Polish translation.
+- v1.1 Ukrainian translation.
+
+### Changed
+
+- Use frontmatter title & description in each language version template
+- Replace broken OpenGraph image with an appropriately-sized Keep a Changelog
+ image that will render properly (although in English for all languages)
+- Fix OpenGraph title & description for all languages so the title and
+description when links are shared are language-appropriate
+
+### Removed
+
+- Trademark sign previously shown after the project description in version
+0.3.0
+
+### Fixed
+
+- Broken OpenGraph image in all language versions
diff --git a/app-icon.png b/app-icon.png
new file mode 100644
index 0000000..b1b61c9
Binary files /dev/null and b/app-icon.png differ
diff --git a/index.html b/index.html
index c184456..db55254 100644
--- a/index.html
+++ b/index.html
@@ -2,7 +2,7 @@
-
+
Tauri + Vue + Typescript App
diff --git a/package.json b/package.json
index 72fd95b..9503625 100644
--- a/package.json
+++ b/package.json
@@ -15,19 +15,20 @@
"dependencies": {
"@tauri-apps/api": "^1",
"vite-plugin-vuetify": "^2.0.3",
- "vue": "^3.3.4",
+ "vue": "^3.4.27",
"vue-i18n": "9",
- "vue-router": "4",
- "vuetify": "^3.6.8"
+ "vue-router": "^4.3.3",
+ "vuetify": "^3.6.9"
},
"devDependencies": {
"@mdi/font": "^7.4.47",
"@tauri-apps/cli": "^1",
"@vitejs/plugin-vue": "^5.0.4",
"prettier": "3.3.2",
- "sass": "^1.77.4",
+ "sass": "^1.77.5",
"typescript": "^5.0.2",
- "vite": "^5.0.0",
- "vue-tsc": "^1.8.5"
- }
+ "vite": "^5.2.13",
+ "vue-tsc": "^2.0.21"
+ },
+ "packageManager": "yarn@1.22.19"
}
diff --git a/public/tauri.svg b/public/tauri.svg
deleted file mode 100644
index 31b62c9..0000000
--- a/public/tauri.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/public/vite.svg b/public/vite.svg
deleted file mode 100644
index e7b8dfb..0000000
--- a/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 9ec7582..dae5180 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -67,6 +67,55 @@ dependencies = [
"libc",
]
+[[package]]
+name = "anstream"
+version = "0.6.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
[[package]]
name = "anyhow"
version = "1.0.86"
@@ -409,6 +458,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+[[package]]
+name = "colorchoice"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+
[[package]]
name = "combine"
version = "4.6.7"
@@ -759,6 +814,29 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "env_filter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "humantime",
+ "log",
+]
+
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -863,6 +941,12 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
[[package]]
name = "futf"
version = "0.1.5"
@@ -1327,6 +1411,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
[[package]]
name = "iana-time-zone"
version = "0.1.60"
@@ -1453,6 +1543,12 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+
[[package]]
name = "itoa"
version = "0.4.8"
@@ -1815,6 +1911,15 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "objc"
version = "0.2.7"
@@ -2661,6 +2766,17 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+[[package]]
+name = "simplelog"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0"
+dependencies = [
+ "log",
+ "termcolor",
+ "time",
+]
+
[[package]]
name = "siphasher"
version = "0.3.11"
@@ -3099,6 +3215,15 @@ dependencies = [
"utf-8",
]
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
[[package]]
name = "thin-slice"
version = "0.1.1"
@@ -3143,7 +3268,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa 1.0.11",
+ "libc",
"num-conv",
+ "num_threads",
"powerfmt",
"serde",
"time-core",
@@ -3373,6 +3500,12 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
[[package]]
name = "uuid"
version = "1.8.0"
@@ -4052,9 +4185,13 @@ dependencies = [
name = "wt-helper-app"
version = "0.0.0"
dependencies = [
+ "env_logger",
+ "fs_extra",
+ "log",
"serde",
"serde_json",
"sevenz-rust",
+ "simplelog",
"tauri",
"tauri-build",
"tempfile",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 64e5a68..096c2e8 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -11,7 +11,10 @@ edition = "2021"
tauri-build = { version = "1", features = [] }
[dependencies]
-tauri = { version = "1", features = [ "window-start-dragging",
+tauri = { version = "1", features = [
+ "window-create",
+ "window-show",
+ "window-start-dragging",
"fs-all",
"path-all",
"dialog-open",
@@ -23,10 +26,15 @@ sevenz-rust = "0.6.0"
zip = "2.1.3"
tempfile = "3"
walkdir = "2.5.0"
+log = "0.4.21"
+fs_extra = "1.3.0"
+env_logger = "0.11.3"
+simplelog = "0.12.2"
[target.'cfg(target_os = "windows")'.dependencies]
winreg = "0.52.0"
+
[features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]
diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png
index 6be5e50..9fc0560 100644
Binary files a/src-tauri/icons/128x128.png and b/src-tauri/icons/128x128.png differ
diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png
index e81bece..cd97405 100644
Binary files a/src-tauri/icons/128x128@2x.png and b/src-tauri/icons/128x128@2x.png differ
diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png
index a437dd5..d21e103 100644
Binary files a/src-tauri/icons/32x32.png and b/src-tauri/icons/32x32.png differ
diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png
index 0ca4f27..04818dd 100644
Binary files a/src-tauri/icons/Square107x107Logo.png and b/src-tauri/icons/Square107x107Logo.png differ
diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png
index b81f820..90c1fc2 100644
Binary files a/src-tauri/icons/Square142x142Logo.png and b/src-tauri/icons/Square142x142Logo.png differ
diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png
index 624c7bf..606fadc 100644
Binary files a/src-tauri/icons/Square150x150Logo.png and b/src-tauri/icons/Square150x150Logo.png differ
diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png
index c021d2b..0ce2868 100644
Binary files a/src-tauri/icons/Square284x284Logo.png and b/src-tauri/icons/Square284x284Logo.png differ
diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png
index 6219700..f196c35 100644
Binary files a/src-tauri/icons/Square30x30Logo.png and b/src-tauri/icons/Square30x30Logo.png differ
diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png
index f9bc048..f1bb4db 100644
Binary files a/src-tauri/icons/Square310x310Logo.png and b/src-tauri/icons/Square310x310Logo.png differ
diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png
index d5fbfb2..5529c3e 100644
Binary files a/src-tauri/icons/Square44x44Logo.png and b/src-tauri/icons/Square44x44Logo.png differ
diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png
index 63440d7..be10db5 100644
Binary files a/src-tauri/icons/Square71x71Logo.png and b/src-tauri/icons/Square71x71Logo.png differ
diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png
index f3f705a..5a4169b 100644
Binary files a/src-tauri/icons/Square89x89Logo.png and b/src-tauri/icons/Square89x89Logo.png differ
diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png
index 4556388..b440e7c 100644
Binary files a/src-tauri/icons/StoreLogo.png and b/src-tauri/icons/StoreLogo.png differ
diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns
index 12a5bce..871f064 100644
Binary files a/src-tauri/icons/icon.icns and b/src-tauri/icons/icon.icns differ
diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico
index b3636e4..2226e78 100644
Binary files a/src-tauri/icons/icon.ico and b/src-tauri/icons/icon.ico differ
diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png
index e1cd261..01d4d7f 100644
Binary files a/src-tauri/icons/icon.png and b/src-tauri/icons/icon.png differ
diff --git a/src-tauri/src/commands/log.rs b/src-tauri/src/commands/log.rs
new file mode 100644
index 0000000..0d7d307
--- /dev/null
+++ b/src-tauri/src/commands/log.rs
@@ -0,0 +1,10 @@
+use crate::ret_code::RetCode;
+
+#[tauri::command]
+pub fn get_app_log_dir(app: tauri::AppHandle) -> Result {
+ let app_log_dir = app
+ .path_resolver()
+ .app_log_dir()
+ .ok_or(RetCode::GetAppLogPathFailed)?;
+ Ok(app_log_dir.to_str().unwrap().to_string())
+}
diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs
deleted file mode 100644
index c6ad6c4..0000000
--- a/src-tauri/src/commands/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod manage_wt;
-pub mod os;
diff --git a/src-tauri/src/commands/os.rs b/src-tauri/src/commands/os.rs
index 7eb417a..4c49894 100644
--- a/src-tauri/src/commands/os.rs
+++ b/src-tauri/src/commands/os.rs
@@ -1,5 +1,3 @@
-use std::fs;
-use std::path::Path;
use std::process::Command;
#[tauri::command]
@@ -28,32 +26,5 @@ pub fn show_in_folder(path: String) {
#[tauri::command]
pub fn delete_folder(path: String) -> Result<(), String> {
- let path = Path::new(&path);
-
- // 检查路径是否存在
- if !path.exists() {
- return Err(format!("Path does not exist: {}", path.display()));
- }
-
- // 检查是否是目录
- if !path.is_dir() {
- return Err(format!("Path is not a directory: {}", path.display()));
- }
-
- // 尝试删除目录及其内容
- match fs::remove_dir_all(path) {
- Ok(_) => Ok(()),
- Err(err) => Err(format!("Failed to delete folder: {}", err)),
- }
-}
-
-#[tauri::command]
-pub fn create_folder(path: String) {
- let path = Path::new(&path);
-
- // 检查路径是否存在
- if !path.exists() {
- // 尝试创建目录
- fs::create_dir_all(path).unwrap();
- }
+ fs_extra::dir::remove(path).map_err(|e| e.to_string())
}
diff --git a/src-tauri/src/commands/setting.rs b/src-tauri/src/commands/setting.rs
new file mode 100644
index 0000000..39b1903
--- /dev/null
+++ b/src-tauri/src/commands/setting.rs
@@ -0,0 +1,42 @@
+use log::{debug, error};
+
+use crate::{
+ config::{self, AppConfig},
+ ret_code::RetCode,
+ WrappedState,
+};
+
+#[tauri::command]
+pub fn get_app_config(state: tauri::State) -> Result {
+ Ok(state.lock().unwrap().as_ref().unwrap().config.clone())
+}
+
+#[tauri::command]
+pub fn save_app_config(
+ app: tauri::AppHandle,
+ state: tauri::State,
+ config: AppConfig,
+) -> Result<(), RetCode> {
+ let app_config_dir = app
+ .path_resolver()
+ .app_config_dir()
+ .ok_or(RetCode::SaveAppSettingsFailed)?;
+ debug!("save config to {:?}", app_config_dir);
+ config::save_config(&app_config_dir, &config).map_err(|e| {
+ error!("save config error: {}", e);
+ RetCode::SaveAppSettingsFailed
+ })?;
+ debug!("save config to state");
+ state.lock().unwrap().as_mut().unwrap().config = config;
+
+ Ok(())
+}
+
+#[tauri::command]
+pub fn get_app_config_dir(app: tauri::AppHandle) -> Result {
+ let app_config_dir = app
+ .path_resolver()
+ .app_config_dir()
+ .ok_or(RetCode::GetAppSettingsFailed)?;
+ Ok(app_config_dir.to_str().unwrap().to_string())
+}
diff --git a/src-tauri/src/commands/manage_wt.rs b/src-tauri/src/commands/war_thunder.rs
similarity index 60%
rename from src-tauri/src/commands/manage_wt.rs
rename to src-tauri/src/commands/war_thunder.rs
index 132c893..5113493 100644
--- a/src-tauri/src/commands/manage_wt.rs
+++ b/src-tauri/src/commands/war_thunder.rs
@@ -1,12 +1,15 @@
use std::{
- fs, io,
+ fs,
path::{Path, PathBuf},
};
use walkdir::WalkDir;
-use zip::ZipArchive;
+
+use log::warn;
+
+use crate::{ret_code::RetCode, tools, WrappedState};
#[tauri::command]
-pub fn auto_detected_wt_install_path() -> Result {
+pub fn auto_detected_wt_root_path() -> Result {
let paths_to_scan: Vec;
if cfg!(target_os = "windows") {
@@ -36,12 +39,11 @@ pub fn auto_detected_wt_install_path() -> Result {
return Ok(path.to_str().unwrap().to_string());
}
}
- return Err("War Thunder install path not found".to_string());
+ return Err(RetCode::AutoDetectedWtRootPathFailed);
}
#[tauri::command]
-pub fn auto_detected_wt_setting_path() -> String {
- // 写一个rust代码,判断 %USERPROFILE%\Documents\My Games\WarThunder\Saves存不存在
+pub fn auto_detected_wt_setting_path() -> Result {
let user_profile = std::env::var("USERPROFILE").unwrap();
let path = std::path::Path::new(&user_profile)
.join("Documents")
@@ -49,19 +51,32 @@ pub fn auto_detected_wt_setting_path() -> String {
.join("WarThunder")
.join("Saves");
if path.exists() {
- return path.to_str().unwrap().to_string();
+ return Ok(path.to_str().unwrap().to_string());
}
- return "".to_string();
+ return Err(RetCode::AutoDetectedWtSettingPathFailed);
}
#[tauri::command]
-pub fn install_user_skin(skin_path: String, wt_install_path: String) -> Result<(), String> {
+pub fn install_user_skin(
+ skin_path: String,
+ state: tauri::State,
+) -> Result<(), RetCode> {
+ let wt_root_path = state
+ .lock()
+ .unwrap()
+ .as_ref()
+ .unwrap()
+ .config
+ .clone()
+ .wt_root_path;
+
let skin_pb = PathBuf::from(&skin_path);
if !skin_pb.exists() {
- return Err(format!("Path does not exist: {}", skin_pb.display()));
+ warn!("Skin path not exists: {:?}", skin_pb);
+ return Err(RetCode::InstallUserSkinFailed);
}
// 如果涂装文件夹不存在,则创建
- let skin_base_path = Path::new(&wt_install_path).join("UserSkins");
+ let skin_base_path = Path::new(&wt_root_path).join("UserSkins");
if !skin_base_path.exists() {
fs::create_dir_all(skin_base_path).unwrap();
}
@@ -69,35 +84,34 @@ pub fn install_user_skin(skin_path: String, wt_install_path: String) -> Result<(
// 如果是文件夹,则判断文件夹下是否有.blk文件,如果有,则复制这个文件夹到 wt_install_path 下
if skin_pb.is_dir() {
if !check_is_folder_contains_blk_file(&skin_pb) {
- return Err(format!("Invalid skin folder: {}", skin_pb.display()));
+ warn!("Skin folder not contains blk file: {:?}", skin_pb);
+ return Err(RetCode::InstallUserSkinFailed);
}
- let new_path = Path::new(&wt_install_path)
+ let new_path = Path::new(&wt_root_path)
.join("UserSkins")
.join(skin_pb.file_name().unwrap());
if new_path.exists() {
- return Err(format!(
- "Skin folder already exists: {}",
- new_path.display()
- ));
+ warn!("Skin folder already exists: {:?}", new_path);
+ return Err(RetCode::InstallUserSkinFailed);
}
- match copy_everything_to(&skin_pb, &new_path) {
+ match tools::fs::copy_folder(&skin_pb, &new_path) {
Ok(_) => Ok(()),
- Err(err) => Err(format!("Failed to copy skin folder: {}", err)),
+ Err(err) => {
+ warn!("Copy skin folder failed: {:?}", err);
+ Err(RetCode::InstallUserSkinFailed)
+ }
}
} else {
// 处理压缩文件
- let temp_dir =
- tempfile::tempdir().map_err(|e| format!("Failed to create temp dir: {}", e))?;
+ let temp_dir = tempfile::tempdir().unwrap();
let temp_path = temp_dir.path();
- decompress_file(skin_pb, temp_path).unwrap();
+ tools::fs::decompress_file(skin_pb.as_path(), temp_path).unwrap();
// 查找解压后的文件夹
let mut extracted_folder = None;
- for entry in
- fs::read_dir(&temp_path).map_err(|e| format!("Failed to read temp dir: {}", e))?
- {
- let entry = entry.map_err(|e| format!("Failed to read temp dir entry: {}", e))?;
+ for entry in fs::read_dir(&temp_path).unwrap() {
+ let entry = entry.unwrap();
if entry.path().is_dir() {
extracted_folder = Some(entry.path());
break;
@@ -106,72 +120,87 @@ pub fn install_user_skin(skin_path: String, wt_install_path: String) -> Result<(
if let Some(extracted_folder) = extracted_folder {
if !check_is_folder_contains_blk_file(&extracted_folder) {
- return Err(format!(
- "Invalid skin folder in archive: {}",
- extracted_folder.display()
- ));
+ warn!(
+ "Extracted folder not contains blk file: {:?}",
+ extracted_folder
+ );
+ return Err(RetCode::InstallUserSkinFailed);
}
- let new_path = Path::new(&wt_install_path)
+ let new_path = Path::new(&wt_root_path)
.join("UserSkins")
.join(extracted_folder.file_name().unwrap());
if new_path.exists() {
- return Err(format!(
- "Skin folder already exists: {}",
- new_path.display()
- ));
+ warn!("Skin folder already exists: {:?}", new_path);
+ return Err(RetCode::InstallUserSkinFailed);
}
- match copy_everything_to(&extracted_folder, &new_path) {
+ match tools::fs::copy_folder(&extracted_folder, &new_path) {
Ok(_) => Ok(()),
- Err(err) => Err(format!("Failed to copy extracted skin folder: {}", err)),
+ Err(err) => {
+ warn!("Copy skin folder failed: {:?}", err);
+ Err(RetCode::InstallUserSkinFailed)
+ }
}
} else {
- Err("No folder found in extracted archive".to_string())
+ warn!("Extracted folder not found");
+ Err(RetCode::InstallUserSkinFailed)
}
}
}
#[tauri::command]
-pub fn install_user_sight(sight_path: String, wt_install_path: String) -> Result<(), String> {
+pub fn install_user_sight(
+ sight_path: String,
+ state: tauri::State,
+) -> Result<(), RetCode> {
+ let wt_root_path = state
+ .lock()
+ .unwrap()
+ .as_ref()
+ .unwrap()
+ .config
+ .clone()
+ .wt_root_path;
+
let sight_pb = PathBuf::from(&sight_path);
if !sight_pb.exists() {
- return Err(format!("Path does not exist: {}", sight_pb.display()));
+ warn!("Sight path not exists: {:?}", sight_pb);
+ return Err(RetCode::InstallUserSightFailed);
}
- let sight_base_path = Path::new(&wt_install_path).join("UserSights");
+ let sight_base_path = Path::new(&wt_root_path).join("UserSights");
if !sight_base_path.exists() {
fs::create_dir_all(sight_base_path).unwrap();
}
if sight_pb.is_dir() {
if !check_is_folder_contains_blk_file(&sight_pb) {
- return Err(format!("Invalid sight folder: {}", sight_pb.display()));
+ warn!("Sight folder not contains blk file: {:?}", sight_pb);
+ return Err(RetCode::InstallUserSightFailed);
}
- let new_path = Path::new(&wt_install_path)
+ let new_path = Path::new(&wt_root_path)
.join("UserSights")
.join(sight_pb.file_name().unwrap());
if new_path.exists() {
- return Err(format!(
- "Sight folder already exists: {}",
- new_path.display()
- ));
+ warn!("Sight folder already exists: {:?}", new_path);
+ return Err(RetCode::InstallUserSightFailed);
}
- match copy_everything_to(&sight_pb, &new_path) {
+ match tools::fs::copy_folder(&sight_pb, &new_path) {
Ok(_) => Ok(()),
- Err(err) => Err(format!("Failed to copy sight folder: {}", err)),
+ Err(err) => {
+ warn!("Copy sight folder failed: {:?}", err);
+ Err(RetCode::InstallUserSightFailed)
+ }
}
} else {
- let temp_dir =
- tempfile::tempdir().map_err(|e| format!("Failed to create temp dir: {}", e))?;
+ let temp_dir = tempfile::tempdir().unwrap();
let temp_path = temp_dir.path();
- decompress_file(sight_pb, temp_path).unwrap();
+ tools::fs::decompress_file(sight_pb.as_path(), temp_path).unwrap();
// 查找解压后的文件夹
let mut extracted_folder = None;
- for entry in
- fs::read_dir(&temp_path).map_err(|e| format!("Failed to read temp dir: {}", e))?
- {
- let entry = entry.map_err(|e| format!("Failed to read temp dir entry: {}", e))?;
+ for entry in fs::read_dir(&temp_path).unwrap() {
+ let entry = entry.unwrap();
if entry.path().is_dir() {
extracted_folder = Some(entry.path());
break;
@@ -180,112 +209,31 @@ pub fn install_user_sight(sight_path: String, wt_install_path: String) -> Result
if let Some(extracted_folder) = extracted_folder {
if !check_is_folder_contains_blk_file(&extracted_folder) {
- return Err(format!(
- "Invalid sight folder in archive: {}",
- extracted_folder.display()
- ));
+ warn!(
+ "Extracted folder not contains blk file: {:?}",
+ extracted_folder
+ );
+ return Err(RetCode::InstallUserSightFailed);
}
- let new_path = Path::new(&wt_install_path)
+ let new_path = Path::new(&wt_root_path)
.join("UserSights")
.join(extracted_folder.file_name().unwrap());
if new_path.exists() {
- return Err(format!(
- "Sight folder already exists: {}",
- new_path.display()
- ));
+ warn!("Sight folder already exists: {:?}", new_path);
+ return Err(RetCode::InstallUserSightFailed);
}
- match copy_everything_to(&extracted_folder, &new_path) {
+ match tools::fs::copy_folder(&extracted_folder, &new_path) {
Ok(_) => Ok(()),
- Err(err) => Err(format!("Failed to copy extracted sight folder: {}", err)),
- }
- } else {
- Err("No folder found in extracted archive".to_string())
- }
- }
-}
-
-fn decompress_file(pb: PathBuf, temp_path: &Path) -> Result<(), String> {
- let extension: &str = pb.extension().and_then(|e| e.to_str()).unwrap_or("");
- match extension.to_lowercase().as_str() {
- "zip" => {
- let file =
- fs::File::open(&pb).map_err(|e| format!("Failed to open zip file: {}", e))?;
- let mut archive =
- ZipArchive::new(file).map_err(|e| format!("Failed to read zip file: {}", e))?;
- archive
- .extract(&temp_path)
- .map_err(|e| format!("Failed to extract zip file: {}", e))?;
- }
- "rar" => return Err(format!("not support rar file now")),
- "7z" => {
- sevenz_rust::decompress_file(&pb, &temp_path)
- .map_err(|e| format!("Failed to extract 7z file: {}", e))?;
- }
- _ => return Err(format!("Unsupported file extension: {}", extension)),
- }
- Ok(())
-}
-
-#[tauri::command]
-pub fn check_is_valid_wt_install_path(path: String) -> bool {
- let path = Path::new(&path);
- check_is_folder_contains_wt_launcher(path)
-}
-
-// Check if the folder contains the War Thunder launcher
-// TODO: Maybe not working on Linux and MacOS
-fn check_is_folder_contains_wt_launcher(path: &Path) -> bool {
- if !path.exists() {
- return false;
- }
- if !path.is_dir() {
- return false;
- }
- let launcher_names = vec!["launcher.exe"];
-
- for name in launcher_names {
- let launcher_path = path.join(name);
- if launcher_path.exists() {
- return true;
- }
- }
- return false;
-}
-
-fn copy_everything_to(old_path: &Path, new_path: &Path) -> io::Result<()> {
- if old_path.is_dir() {
- fs::create_dir(new_path)?;
- for entry in fs::read_dir(old_path)? {
- let entry = entry?;
- let path = entry.path();
- let new_path = new_path.join(path.file_name().unwrap());
- copy_everything_to(&path, &new_path)?;
- }
- } else {
- fs::copy(old_path, new_path)?;
- }
- Ok(())
-}
-
-fn check_is_folder_contains_blk_file(path: &PathBuf) -> bool {
- if !path.exists() {
- return false;
- }
- if !path.is_dir() {
- return false;
- }
- for entry in fs::read_dir(path).unwrap() {
- let entry = entry.unwrap();
- let path = entry.path();
- if path.is_file() {
- if let Some(ext) = path.extension() {
- if ext == "blk" {
- return true;
+ Err(_err) => {
+ warn!("Copy sight folder failed: {:?}", _err);
+ Err(RetCode::InstallUserSightFailed)
}
}
+ } else {
+ warn!("Extracted folder not found");
+ Err(RetCode::InstallUserSightFailed)
}
}
- return false;
}
#[derive(Debug, serde::Serialize)]
@@ -297,23 +245,22 @@ pub struct UserSkinInfo {
folder_size: u64,
}
-fn calculate_directory_size(path: &Path) -> io::Result {
- let mut total_size = 0;
- for entry in WalkDir::new(path) {
- let entry = entry?;
- if entry.file_type().is_file() {
- total_size += entry.metadata()?.len();
- }
- }
- Ok(total_size)
-}
-
#[tauri::command]
-pub fn get_user_skins(wt_install_path: String) -> Result, String> {
- let skin_base_path = Path::new(&wt_install_path).join("UserSkins");
+pub fn get_user_skins(state: tauri::State) -> Result, RetCode> {
+ let wt_root_path = state
+ .lock()
+ .unwrap()
+ .as_ref()
+ .unwrap()
+ .config
+ .clone()
+ .wt_root_path;
+
+ let skin_base_path = Path::new(&wt_root_path).join("UserSkins");
if !skin_base_path.exists() {
- return Err("UserSkins folder not found".to_string());
+ warn!("UserSkins folder not found");
+ return Err(RetCode::GetUserSkinsFailed);
}
let mut infos = Vec::new();
for entry in WalkDir::new(&skin_base_path).min_depth(1).max_depth(10) {
@@ -349,7 +296,7 @@ pub fn get_user_skins(wt_install_path: String) -> Result, Stri
.to_string_lossy()
.to_string();
let full_path = entry.path().to_string_lossy().to_string();
- let folder_size = calculate_directory_size(entry.path()).unwrap();
+ let folder_size = fs_extra::dir::get_size(entry.path()).unwrap();
infos.push(UserSkinInfo {
vehicle_id,
skin_name,
@@ -373,11 +320,21 @@ pub struct UserSightInfo {
}
#[tauri::command]
-pub fn get_user_sights(wt_install_path: String) -> Result, String> {
- let skin_base_path = Path::new(&wt_install_path).join("UserSights");
+pub fn get_user_sights(state: tauri::State) -> Result, RetCode> {
+ let wt_root_path = state
+ .lock()
+ .unwrap()
+ .as_ref()
+ .unwrap()
+ .config
+ .clone()
+ .wt_root_path;
+
+ let skin_base_path = Path::new(&wt_root_path).join("UserSights");
if !skin_base_path.exists() {
- return Err("UserSights folder not found".to_string());
+ warn!("UserSights folder not found");
+ return Err(RetCode::GetUserSightsFailed);
}
let mut infos = Vec::new();
for entry in WalkDir::new(&skin_base_path).min_depth(1).max_depth(10) {
@@ -417,7 +374,7 @@ pub fn get_user_sights(wt_install_path: String) -> Result, St
.to_string_lossy()
.to_string();
let full_path = entry.path().to_string_lossy().to_string();
- let folder_size = calculate_directory_size(entry.path()).unwrap();
+ let folder_size = fs_extra::dir::get_size(entry.path()).unwrap();
infos.push(UserSightInfo {
vehicle_id,
folder_name,
@@ -430,3 +387,44 @@ pub fn get_user_sights(wt_install_path: String) -> Result, St
}
Ok(infos)
}
+
+// Check if the folder contains the War Thunder launcher
+// TODO: Maybe not working on Linux and MacOS
+fn check_is_folder_contains_wt_launcher(path: &Path) -> bool {
+ if !path.exists() {
+ return false;
+ }
+ if !path.is_dir() {
+ return false;
+ }
+ let launcher_names = vec!["launcher.exe"];
+
+ for name in launcher_names {
+ let launcher_path = path.join(name);
+ if launcher_path.exists() {
+ return true;
+ }
+ }
+ return false;
+}
+
+fn check_is_folder_contains_blk_file(path: &PathBuf) -> bool {
+ if !path.exists() {
+ return false;
+ }
+ if !path.is_dir() {
+ return false;
+ }
+ for entry in fs::read_dir(path).unwrap() {
+ let entry = entry.unwrap();
+ let path = entry.path();
+ if path.is_file() {
+ if let Some(ext) = path.extension() {
+ if ext == "blk" {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs
new file mode 100644
index 0000000..2b9231c
--- /dev/null
+++ b/src-tauri/src/config.rs
@@ -0,0 +1,75 @@
+use std::path::PathBuf;
+
+use log::debug;
+use serde::{Deserialize, Serialize};
+use serde_json::json;
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct AppConfig {
+ pub wt_root_path: String,
+ pub wt_setting_path: String,
+}
+
+const SETTING_FILE: &str = "config.json";
+
+/// check config file exists
+///
+/// 检查配置文件是否存在
+pub fn check_config_file(base_path: &PathBuf) -> bool {
+ debug!("check config file at {:?}", base_path);
+ let config_full_path = base_path.join(SETTING_FILE);
+ let exists = config_full_path.exists();
+ debug!("config file exists: {}", exists);
+ exists
+}
+
+/// check config file exists, if not, create it
+///
+/// 检查配置文件是否存在,如果不存在则创建
+pub fn check_and_create_config_file(base_path: &PathBuf) -> Result<(), Box> {
+ if check_config_file(base_path) {
+ return Ok(());
+ }
+ if !base_path.exists() {
+ std::fs::create_dir_all(&base_path)?;
+ }
+ let config_full_path = base_path.join(SETTING_FILE);
+ if !config_full_path.exists() {
+ debug!("config file not exists, create it");
+ let default_config = json!(AppConfig {
+ wt_root_path: "".to_string(),
+ wt_setting_path: "".to_string(),
+ });
+ let default_config_str = serde_json::to_string_pretty(&default_config)?;
+ std::fs::write(&config_full_path, default_config_str)?;
+ }
+ debug!("check config file done");
+ Ok(())
+}
+
+/// get config from file
+///
+/// 从文件中获取配置
+pub fn get_config(base_path: &PathBuf) -> Result> {
+ debug!("get config from file");
+ let config_full_path = base_path.join(SETTING_FILE);
+ let config_str = std::fs::read_to_string(&config_full_path)?;
+ let config: AppConfig = serde_json::from_str(&config_str)?;
+ debug!("get config from file done");
+ Ok(config)
+}
+
+/// save config to file
+///
+/// 保存配置到文件
+pub fn save_config(
+ base_path: &PathBuf,
+ config: &AppConfig,
+) -> Result<(), Box> {
+ debug!("save config {:?} to file", config);
+ let config_full_path = base_path.join(SETTING_FILE);
+ let config_str = serde_json::to_string_pretty(&config)?;
+ std::fs::write(&config_full_path, config_str)?;
+ debug!("save config to file done");
+ Ok(())
+}
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 6632734..42db5e9 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -1,24 +1,84 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
-mod commands;
+use std::{fs::File, sync::Mutex};
-// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
+use config::AppConfig;
+use log::{info, LevelFilter};
+use serde::{Deserialize, Serialize};
+use simplelog::{ColorChoice, CombinedLogger, Config, TermLogger, TerminalMode, WriteLogger};
+use tauri::Manager;
+
+mod commands {
+ pub mod log;
+ pub mod os;
+ pub mod setting;
+ pub mod war_thunder;
+}
+
+mod tools {
+ pub mod fs;
+}
+
+mod config;
+mod ret_code;
+
+type WrappedState = Mutex