mirror of
https://github.com/lifegpc/garbro-rs.git
synced 2026-06-26 05:37:21 +08:00
Compare commits
4 Commits
de335e6323
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c3ebfa894 | |||
| 81df6cc2df | |||
| b8a921022b | |||
| f213fc6d8b |
145
src-tauri/Cargo.lock
generated
145
src-tauri/Cargo.lock
generated
@@ -126,6 +126,18 @@ version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures 0.2.17",
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
@@ -321,6 +333,12 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
@@ -351,6 +369,15 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@@ -593,9 +620,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cedarwood"
|
||||
version = "0.4.6"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d910bedd62c24733263d0bed247460853c9d22e8956bd4cd964302095e04e90"
|
||||
checksum = "c0524a528a6a0288df1863c3c20fe92c301875b4941e7b6c4b394ab08c5a4c55"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
@@ -646,6 +673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures 0.3.0",
|
||||
"rand_core 0.10.0",
|
||||
]
|
||||
@@ -657,8 +685,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
@@ -668,6 +698,7 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea"
|
||||
dependencies = [
|
||||
"block-buffer 0.12.0",
|
||||
"crypto-common 0.2.1",
|
||||
"inout",
|
||||
]
|
||||
@@ -1219,6 +1250,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.4",
|
||||
"crypto-common 0.1.7",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1564,9 +1596,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.17.0"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
|
||||
checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex-automata",
|
||||
@@ -1575,9 +1607,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fastcdc"
|
||||
version = "3.2.1"
|
||||
version = "4.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf51ceb43e96afbfe4dd5c6f6082af5dfd60e220820b8123792d61963f2ce6bc"
|
||||
checksum = "77af40d8a8dadb92dc178569a5f5edb5f3056e98255c2de48ab5d59a52892e0c"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
@@ -2725,19 +2757,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jieba-macros"
|
||||
version = "0.8.1"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "348294e44ee7e3c42685da656490f8febc7359632544019621588902216da95c"
|
||||
checksum = "46adade69b634535a8f495cf87710ed893cff53e1dbc9dd750c2ab81c5defb82"
|
||||
dependencies = [
|
||||
"phf_codegen 0.13.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jieba-rs"
|
||||
version = "0.8.1"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "766bd7012aa5ba49411ebdf4e93bddd59b182d2918e085d58dec5bb9b54b7105"
|
||||
checksum = "11b53580aaa8ec8b713da271da434f8947409242c537a9ab3f7b76bdbb19e8a9"
|
||||
dependencies = [
|
||||
"bytecount",
|
||||
"cedarwood",
|
||||
"include-flate",
|
||||
"jieba-macros",
|
||||
@@ -2840,6 +2873,16 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keyboard-types"
|
||||
version = "0.7.0"
|
||||
@@ -3176,16 +3219,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "msg_tool"
|
||||
version = "0.3.1"
|
||||
version = "0.4.0-alpha.2"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"aes",
|
||||
"anyhow",
|
||||
"argon2",
|
||||
"base64 0.22.1",
|
||||
"blake2",
|
||||
"block_compression",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"cbc",
|
||||
"chacha20",
|
||||
"chrono",
|
||||
"clap 4.6.0",
|
||||
"crc32fast",
|
||||
"crossbeam",
|
||||
@@ -3197,6 +3244,7 @@ dependencies = [
|
||||
"fancy-regex",
|
||||
"fastcdc",
|
||||
"flate2",
|
||||
"hex",
|
||||
"include-flate",
|
||||
"int-enum",
|
||||
"jieba-rs",
|
||||
@@ -3217,6 +3265,7 @@ dependencies = [
|
||||
"parse-size",
|
||||
"pelite",
|
||||
"png 0.18.1",
|
||||
"qoi",
|
||||
"rand 0.10.1",
|
||||
"rust-ini",
|
||||
"serde",
|
||||
@@ -3224,6 +3273,9 @@ dependencies = [
|
||||
"serde_yaml_ng",
|
||||
"sha1",
|
||||
"sha2 0.11.0",
|
||||
"sha3",
|
||||
"shake",
|
||||
"siphasher 1.0.2",
|
||||
"stylua",
|
||||
"tendril 0.5.0",
|
||||
"unicode-segmentation",
|
||||
@@ -3232,13 +3284,16 @@ dependencies = [
|
||||
"webp",
|
||||
"windows-sys 0.61.2",
|
||||
"xml5ever",
|
||||
"xxhash-rust",
|
||||
"zopfli",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "msg_tool_build"
|
||||
version = "0.3.1"
|
||||
version = "0.4.0-alpha.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8386042ba7f224865098b365961d45df240b4f43ac6c8e279d16616ae25188ae"
|
||||
dependencies = [
|
||||
"json",
|
||||
"zstd",
|
||||
@@ -3246,7 +3301,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "msg_tool_macro"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0-alpha.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "177ed201ff17ef103ad65d1f5a679d105e463a4e36e61a977978bdb97b5d04bc"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
@@ -3254,7 +3311,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "msg_tool_xp3data"
|
||||
version = "0.3.1"
|
||||
version = "0.4.0-alpha.2"
|
||||
dependencies = [
|
||||
"msg_tool_build",
|
||||
"zstd",
|
||||
@@ -3648,6 +3705,17 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b"
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
@@ -4096,6 +4164,15 @@ version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d"
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.1"
|
||||
@@ -4730,6 +4807,28 @@ dependencies = [
|
||||
"digest 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc9bad02c26382724b2d2692c6f179285e4b54eeecd7968f52a50059c3c11759"
|
||||
dependencies = [
|
||||
"digest 0.11.2",
|
||||
"keccak",
|
||||
"sponge-cursor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shake"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09057cb2149ad4cbd2da1e26b351f9a4c354219421229c69c3063e6f61947c4a"
|
||||
dependencies = [
|
||||
"digest 0.11.2",
|
||||
"keccak",
|
||||
"sponge-cursor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
@@ -4854,6 +4953,12 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sponge-cursor"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a0219bd7d979d58245a4f41f695e1ac9f8befdffadd7f61f1bae9e39abc6620"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
@@ -4948,6 +5053,12 @@ dependencies = [
|
||||
"toml 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "swift-rs"
|
||||
version = "1.0.7"
|
||||
@@ -6809,6 +6920,12 @@ dependencies = [
|
||||
"markup5ever 0.38.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xxhash-rust"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.2"
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
use anyhow::Result;
|
||||
use msg_tool::ext::io::MutexWrapper;
|
||||
use msg_tool::ext::mutex::*;
|
||||
use msg_tool::scripts::base::ReadSeek;
|
||||
use msg_tool::scripts::{BUILDER, ScriptBuilder};
|
||||
use msg_tool::types::{ExtraConfig, ImageOutputType, ScriptType};
|
||||
use msg_tool::scripts::{BUILDER, Script, ScriptBuilder};
|
||||
use msg_tool::types::{ExtraConfig, ImageOutputType, PngCompressionLevel, ScriptType};
|
||||
use msg_tool::utils::img::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::io::{BufReader, Read};
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri::ipc::Response;
|
||||
|
||||
const PNG_SIGNATURE: &[u8] = b"\x89PNG\r\n\x1a\n";
|
||||
|
||||
@@ -17,12 +20,172 @@ lazy_static::lazy_static! {
|
||||
static ref ENTRY_TYPE_CACHE: Mutex<BTreeMap<ScriptType, EntryType>> = Mutex::new(BTreeMap::new());
|
||||
}
|
||||
|
||||
struct CacheEntry<'a> {
|
||||
archive: Box<dyn Script + Send + Sync + 'a>,
|
||||
path: String,
|
||||
option: FileOptions,
|
||||
}
|
||||
|
||||
impl<'a> CacheEntry<'a> {
|
||||
fn new(
|
||||
mut reader: Box<dyn ReadSeek + Send + Sync + 'a>,
|
||||
filename: &str,
|
||||
option: FileOptions,
|
||||
script_type: Option<ScriptType>,
|
||||
) -> Result<Self> {
|
||||
let (entry_type, msg_tool_type) = if let Some(typ) = script_type {
|
||||
(query_entry_type(&typ), Some(typ.clone()))
|
||||
} else {
|
||||
let mut buffer = [0; 1024];
|
||||
let n = reader.read(&mut buffer)?;
|
||||
reader.rewind()?;
|
||||
detect_file_type(filename, &buffer[..n])
|
||||
};
|
||||
if entry_type != EntryType::Archive {
|
||||
return Err(anyhow::anyhow!("不是归档文件"));
|
||||
}
|
||||
let script_type = msg_tool_type.ok_or_else(|| anyhow::anyhow!("无法识别的归档格式"))?;
|
||||
let builder = BUILDER
|
||||
.iter()
|
||||
.find(|b| b.script_type() == &script_type)
|
||||
.ok_or_else(|| anyhow::anyhow!("不支持的归档格式"))?;
|
||||
let extra_config = option.to_extra_config();
|
||||
let encoding = builder.default_encoding();
|
||||
let archive_encoding = builder.default_archive_encoding().unwrap_or(encoding);
|
||||
let archive = builder.build_script_from_reader(
|
||||
reader,
|
||||
filename,
|
||||
encoding,
|
||||
archive_encoding,
|
||||
&extra_config,
|
||||
None,
|
||||
)?;
|
||||
Ok(CacheEntry {
|
||||
archive,
|
||||
path: filename.to_string(),
|
||||
option,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct CacheManager {
|
||||
file: Option<Arc<Mutex<BufReader<File>>>>,
|
||||
cache: Option<CacheEntry<'static>>,
|
||||
}
|
||||
|
||||
impl CacheManager {
|
||||
fn list_archive(
|
||||
&mut self,
|
||||
path: &str,
|
||||
option: Option<&Vec<FileOptions>>,
|
||||
) -> Result<Vec<Entry>> {
|
||||
let paths = path.split("|").collect::<Vec<_>>();
|
||||
let cache = self.get_entry(path, option)?;
|
||||
if paths.len() == 1 {
|
||||
let mut result = Vec::new();
|
||||
let mut index = 0;
|
||||
for entry in cache.archive.iter_archive_filename()? {
|
||||
let name = entry?;
|
||||
let mut entry = cache.archive.open_file(index)?;
|
||||
let file_size = entry.size();
|
||||
index += 1;
|
||||
let (entry_type, msg_tool_type) = if let Some(typ) = entry.script_type() {
|
||||
(query_entry_type(&typ), Some(typ.clone()))
|
||||
} else {
|
||||
let mut buffer = [0; 1024];
|
||||
let n = entry.read(&mut buffer)?;
|
||||
detect_file_type(&name, &buffer[..n])
|
||||
};
|
||||
// 扁平结构,不区分文件夹,前端根据路径解析出文件夹结构
|
||||
result.push(Entry {
|
||||
name,
|
||||
is_dir: false,
|
||||
entry_type,
|
||||
msg_tool_type,
|
||||
size: file_size,
|
||||
});
|
||||
}
|
||||
Ok(result)
|
||||
} else {
|
||||
let filename = path.split("|").nth(1).unwrap();
|
||||
let mut entry = cache.archive.open_file_by_name(filename, false)?;
|
||||
let typ = entry.script_type().map(|t| t.clone());
|
||||
let entry = entry.to_data()?;
|
||||
let path = path.splitn(2, "|").nth(1).unwrap();
|
||||
list_archive_directory_in_archive(path, entry, filename, option, typ, 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn preview_image(&mut self, path: &str, option: Option<&Vec<FileOptions>>) -> Result<Vec<u8>> {
|
||||
let cache = self.get_entry(path, option)?;
|
||||
let paths = path.split("|").collect::<Vec<_>>();
|
||||
if paths.len() == 2 {
|
||||
let filename = paths[1];
|
||||
let mut entry = cache.archive.open_file_by_name(filename, false)?;
|
||||
let typ = entry.script_type().map(|t| t.clone());
|
||||
let entry = entry.to_data()?;
|
||||
preview_image_in_directory(entry, filename, option, typ, 1)
|
||||
} else {
|
||||
let filename = paths[1];
|
||||
let mut entry = cache.archive.open_file_by_name(filename, false)?;
|
||||
let typ = entry.script_type().map(|t| t.clone());
|
||||
let entry = entry.to_data()?;
|
||||
let path = path.splitn(2, "|").nth(1).unwrap();
|
||||
preview_image_in_archive(path, entry, filename, option, typ, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 目前只能缓存根归档文件
|
||||
fn get_entry(
|
||||
&mut self,
|
||||
path: &str,
|
||||
option: Option<&Vec<FileOptions>>,
|
||||
) -> Result<&CacheEntry<'static>> {
|
||||
let paths = path.split("|").collect::<Vec<_>>();
|
||||
let is_hit = self.cache.as_ref().map_or(false, |cache| {
|
||||
if paths[0] == cache.path {
|
||||
let opt = option
|
||||
.and_then(|opts| opts.get(0).cloned())
|
||||
.unwrap_or_default();
|
||||
if opt == cache.option {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
});
|
||||
let entry = if is_hit {
|
||||
self.cache.as_ref().unwrap()
|
||||
} else {
|
||||
// 关闭当前缓存的文件和归档
|
||||
self.cache.take();
|
||||
self.file.take();
|
||||
let file = Arc::new(Mutex::new(BufReader::new(File::open(paths[0])?)));
|
||||
let option = option
|
||||
.and_then(|opts| opts.get(0).cloned())
|
||||
.unwrap_or_default();
|
||||
let mfile = MutexWrapper::new(file.clone(), 0);
|
||||
let cache_entry = CacheEntry::new(Box::new(mfile), paths[0], option, None)?;
|
||||
self.file = Some(file.clone());
|
||||
self.cache = Some(cache_entry);
|
||||
self.cache.as_ref().unwrap()
|
||||
};
|
||||
Ok(entry)
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static::lazy_static!(
|
||||
static ref CACHE_MANAGER: Mutex<CacheManager> = Mutex::new(CacheManager {
|
||||
file: None,
|
||||
cache: None,
|
||||
});
|
||||
);
|
||||
|
||||
fn query_entry_type(script_type: &ScriptType) -> EntryType {
|
||||
let mut cache = ENTRY_TYPE_CACHE.lock().unwrap();
|
||||
if let Some(entry_type) = cache.get(script_type) {
|
||||
return entry_type.clone();
|
||||
}
|
||||
let entry_type = if script_type.is_audio() {
|
||||
let entry_type = if matches!(script_type, ScriptType::BGIAudio | ScriptType::CircusPcm) {
|
||||
EntryType::Audio
|
||||
} else {
|
||||
let builder = BUILDER
|
||||
@@ -35,17 +198,6 @@ fn query_entry_type(script_type: &ScriptType) -> EntryType {
|
||||
entry_type
|
||||
}
|
||||
|
||||
/// 到时候可能考虑把识别写到msg_tool那里
|
||||
trait ScriptTypeExt {
|
||||
fn is_audio(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ScriptTypeExt for ScriptType {
|
||||
fn is_audio(&self) -> bool {
|
||||
matches!(self, ScriptType::BGIAudio | ScriptType::CircusPcm)
|
||||
}
|
||||
}
|
||||
|
||||
trait ScriptBuilderExt {
|
||||
fn entry_type(&self) -> EntryType;
|
||||
}
|
||||
@@ -56,7 +208,7 @@ impl<T: ScriptBuilder + ?Sized> ScriptBuilderExt for T {
|
||||
EntryType::Image
|
||||
} else if self.is_archive() {
|
||||
EntryType::Archive
|
||||
} else if self.script_type().is_audio() {
|
||||
} else if self.is_audio() {
|
||||
EntryType::Audio
|
||||
} else {
|
||||
EntryType::Unknown
|
||||
@@ -90,7 +242,7 @@ pub struct GameTitle {
|
||||
alias: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Clone)]
|
||||
#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Xp3Option {
|
||||
/// 设置游戏标题,用于解密xp3文件
|
||||
game_title: Option<String>,
|
||||
@@ -98,7 +250,7 @@ pub struct Xp3Option {
|
||||
force_decrypt: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Clone)]
|
||||
#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct FileOptions {
|
||||
xp3: Option<Xp3Option>,
|
||||
}
|
||||
@@ -199,11 +351,16 @@ fn detect_file_type(filename: &str, data: &[u8]) -> (EntryType, Option<ScriptTyp
|
||||
(EntryType::Unknown, None)
|
||||
}
|
||||
|
||||
fn try_detect_file_type<P: AsRef<std::path::Path> + ?Sized>(path: &P) -> Result<(EntryType, Option<ScriptType>)> {
|
||||
fn try_detect_file_type<P: AsRef<std::path::Path> + ?Sized>(
|
||||
path: &P,
|
||||
) -> Result<(EntryType, Option<ScriptType>)> {
|
||||
let mut file = File::open(path)?;
|
||||
let mut buffer = [0; 1024];
|
||||
let n = file.read(&mut buffer)?;
|
||||
Ok(detect_file_type(&path.as_ref().to_string_lossy(), &buffer[..n]))
|
||||
Ok(detect_file_type(
|
||||
&path.as_ref().to_string_lossy(),
|
||||
&buffer[..n],
|
||||
))
|
||||
}
|
||||
|
||||
fn list_fs_directory(path: &Path) -> Result<Vec<Entry>> {
|
||||
@@ -242,64 +399,6 @@ fn list_fs_directory(path: &Path) -> Result<Vec<Entry>> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn list_archive_directory(path: &Path, option: Option<&Vec<FileOptions>>) -> Result<Vec<Entry>> {
|
||||
let option = option
|
||||
.and_then(|opts| opts.get(0).cloned())
|
||||
.unwrap_or_default();
|
||||
let mut header = [0; 1024];
|
||||
let n = {
|
||||
let mut file = File::open(path)?;
|
||||
file.read(&mut header)?
|
||||
};
|
||||
let (entry_type, msg_tool_type) = detect_file_type(&path.to_string_lossy(), &header[..n]);
|
||||
if entry_type != EntryType::Archive {
|
||||
return Err(anyhow::anyhow!("不是归档文件"));
|
||||
}
|
||||
let script_type = msg_tool_type.ok_or_else(|| anyhow::anyhow!("无法识别的归档格式"))?;
|
||||
let builder = BUILDER
|
||||
.iter()
|
||||
.find(|b| b.script_type() == &script_type)
|
||||
.ok_or_else(|| anyhow::anyhow!("不支持的归档格式"))?;
|
||||
let extra_config = option.to_extra_config();
|
||||
let encoding = builder.default_encoding();
|
||||
let archive_encoding = builder.default_archive_encoding().unwrap_or(encoding);
|
||||
let archive = builder.build_script_from_file(
|
||||
&path.to_string_lossy(),
|
||||
encoding,
|
||||
archive_encoding,
|
||||
&extra_config,
|
||||
None,
|
||||
)?;
|
||||
let mut result = Vec::new();
|
||||
let mut index = 0;
|
||||
for entry in archive.iter_archive_filename()? {
|
||||
let name = entry?;
|
||||
let mut entry = archive.open_file(index)?;
|
||||
index += 1;
|
||||
let (entry_type, msg_tool_type) = if let Some(typ) = entry.script_type() {
|
||||
let entry_type = if typ.is_audio() {
|
||||
EntryType::Audio
|
||||
} else {
|
||||
query_entry_type(&typ)
|
||||
};
|
||||
(entry_type, Some(typ.clone()))
|
||||
} else {
|
||||
let mut buffer = [0; 1024];
|
||||
let n = entry.read(&mut buffer)?;
|
||||
detect_file_type(&name, &buffer[..n])
|
||||
};
|
||||
// 扁平结构,不区分文件夹,前端根据路径解析出文件夹结构
|
||||
result.push(Entry {
|
||||
name,
|
||||
is_dir: false,
|
||||
entry_type,
|
||||
msg_tool_type,
|
||||
size: None,
|
||||
});
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn list_archive_directory_in_archive<'a>(
|
||||
path: &str,
|
||||
mut reader: Box<dyn ReadSeek + Send + Sync + 'a>,
|
||||
@@ -312,12 +411,7 @@ fn list_archive_directory_in_archive<'a>(
|
||||
.and_then(|opts| opts.get(index).cloned())
|
||||
.unwrap_or_default();
|
||||
let (entry_type, msg_tool_type) = if let Some(typ) = typ {
|
||||
let entry_type = if typ.is_audio() {
|
||||
EntryType::Audio
|
||||
} else {
|
||||
query_entry_type(&typ)
|
||||
};
|
||||
(entry_type, Some(typ.clone()))
|
||||
(query_entry_type(&typ), Some(typ.clone()))
|
||||
} else {
|
||||
let mut buffer = [0; 1024];
|
||||
let n = reader.read(&mut buffer)?;
|
||||
@@ -363,14 +457,10 @@ fn list_archive_directory_in_archive<'a>(
|
||||
for entry in archive.iter_archive_filename()? {
|
||||
let name = entry?;
|
||||
let mut entry = archive.open_file(index)?;
|
||||
let file_size = entry.size();
|
||||
index += 1;
|
||||
let (entry_type, msg_tool_type) = if let Some(typ) = entry.script_type() {
|
||||
let entry_type = if typ.is_audio() {
|
||||
EntryType::Audio
|
||||
} else {
|
||||
query_entry_type(&typ)
|
||||
};
|
||||
(entry_type, Some(typ.clone()))
|
||||
(query_entry_type(&typ), Some(typ.clone()))
|
||||
} else {
|
||||
let mut buffer = [0; 1024];
|
||||
let n = entry.read(&mut buffer)?;
|
||||
@@ -382,7 +472,7 @@ fn list_archive_directory_in_archive<'a>(
|
||||
is_dir: false,
|
||||
entry_type,
|
||||
msg_tool_type,
|
||||
size: None,
|
||||
size: file_size,
|
||||
});
|
||||
}
|
||||
Ok(result)
|
||||
@@ -415,25 +505,13 @@ pub fn list_directory(
|
||||
options: Option<Vec<FileOptions>>,
|
||||
) -> Result<Vec<Entry>, ErrorMsg> {
|
||||
if path.contains("|") {
|
||||
let filename = path.split("|").nth(0).unwrap();
|
||||
let reader = Box::new(std::io::BufReader::new(File::open(filename).map_err(
|
||||
|e| ErrorMsg {
|
||||
typ: ErrorType::NotFound,
|
||||
msg: format!("无法打开文件: {}", e),
|
||||
},
|
||||
)?));
|
||||
return list_archive_directory_in_archive(
|
||||
path,
|
||||
reader,
|
||||
filename,
|
||||
options.as_ref(),
|
||||
None,
|
||||
0,
|
||||
)
|
||||
.map_err(|e| ErrorMsg {
|
||||
typ: ErrorType::Other,
|
||||
msg: e.to_string(),
|
||||
});
|
||||
return CACHE_MANAGER
|
||||
.lock_blocking()
|
||||
.list_archive(path, options.as_ref())
|
||||
.map_err(|e| ErrorMsg {
|
||||
typ: ErrorType::Other,
|
||||
msg: e.to_string(),
|
||||
});
|
||||
}
|
||||
let path = std::path::Path::new(path);
|
||||
if !path.exists() {
|
||||
@@ -446,10 +524,13 @@ pub fn list_directory(
|
||||
if let Some(parent) = path.parent() {
|
||||
let _ = set_last_directory(&app, parent.to_string_lossy().as_ref());
|
||||
}
|
||||
return list_archive_directory(path, options.as_ref()).map_err(|e| ErrorMsg {
|
||||
typ: ErrorType::Other,
|
||||
msg: e.to_string(),
|
||||
});
|
||||
return CACHE_MANAGER
|
||||
.lock_blocking()
|
||||
.list_archive(&path.to_string_lossy(), options.as_ref())
|
||||
.map_err(|e| ErrorMsg {
|
||||
typ: ErrorType::Other,
|
||||
msg: e.to_string(),
|
||||
});
|
||||
}
|
||||
let _ = set_last_directory(&app, path.to_string_lossy().as_ref());
|
||||
list_fs_directory(path).map_err(|e| ErrorMsg {
|
||||
@@ -479,12 +560,7 @@ fn preview_image_in_directory<'a>(
|
||||
index: usize,
|
||||
) -> Result<Vec<u8>> {
|
||||
let (entry_type, msg_tool_type) = if let Some(typ) = script_type {
|
||||
let entry_type = if typ.is_audio() {
|
||||
EntryType::Audio
|
||||
} else {
|
||||
query_entry_type(&typ)
|
||||
};
|
||||
(entry_type, Some(typ.clone()))
|
||||
(query_entry_type(&typ), Some(typ.clone()))
|
||||
} else {
|
||||
let mut buffer = [0; 1024];
|
||||
let n = reader.read(&mut buffer)?;
|
||||
@@ -503,7 +579,8 @@ fn preview_image_in_directory<'a>(
|
||||
.as_ref()
|
||||
.and_then(|opts| opts.get(index).cloned())
|
||||
.unwrap_or_default();
|
||||
let extra_config = option.to_extra_config();
|
||||
let mut extra_config = option.to_extra_config();
|
||||
extra_config.png_compression_level = PngCompressionLevel::Fast;
|
||||
let encoding = builder.default_encoding();
|
||||
let archive_encoding = builder.default_archive_encoding().unwrap_or(encoding);
|
||||
let image = builder.build_script_from_reader(
|
||||
@@ -538,21 +615,13 @@ fn preview_image_in_archive<'a>(
|
||||
.and_then(|opts| opts.get(index).cloned())
|
||||
.unwrap_or_default();
|
||||
let (entry_type, msg_tool_type) = if let Some(typ) = typ {
|
||||
let entry_type = if typ.is_audio() {
|
||||
EntryType::Audio
|
||||
} else {
|
||||
query_entry_type(&typ)
|
||||
};
|
||||
(entry_type, Some(typ.clone()))
|
||||
(query_entry_type(&typ), Some(typ.clone()))
|
||||
} else {
|
||||
let mut buffer = [0; 1024];
|
||||
let n = reader.read(&mut buffer)?;
|
||||
reader.rewind()?;
|
||||
detect_file_type("", &buffer[..n])
|
||||
};
|
||||
if entry_type != EntryType::Archive {
|
||||
return Err(anyhow::anyhow!("不是归档文件"));
|
||||
}
|
||||
let msg_tool_type = msg_tool_type.ok_or_else(|| anyhow::anyhow!("无法识别的归档格式"))?;
|
||||
let builder = BUILDER
|
||||
.iter()
|
||||
@@ -570,6 +639,9 @@ fn preview_image_in_archive<'a>(
|
||||
None,
|
||||
)?;
|
||||
if path.contains("|") {
|
||||
if !archive.is_archive() {
|
||||
return Err(anyhow::anyhow!("不是归档文件"));
|
||||
}
|
||||
let filename = path.split("|").nth(0).unwrap();
|
||||
let mut entry = archive.open_file_by_name(filename, false)?;
|
||||
let typ = entry.script_type().map(|t| t.clone());
|
||||
@@ -592,20 +664,16 @@ fn preview_image_in_archive<'a>(
|
||||
/// path: /path/to/archive.zip|image.png 预览archive.zip内的image.png,options[0] 会用于打开archive.zip, options[1] 会用于打开image.png(如果需要的话)
|
||||
/// path: /path/to/archive.zip|inner/archive2.zip|image.png 预览archive2.zip内的image.png,options[0] 会用于打开archive.zip, options[1] 会用于打开archive2.zip, options[2] 会用于打开image.png(如果需要的话)
|
||||
#[tauri::command]
|
||||
pub fn preview_image(path: &str, options: Option<Vec<FileOptions>>) -> Result<Vec<u8>, ErrorMsg> {
|
||||
pub fn preview_image(path: &str, options: Option<Vec<FileOptions>>) -> Result<Response, ErrorMsg> {
|
||||
if path.contains("|") {
|
||||
let filename = path.split("|").nth(0).unwrap();
|
||||
let reader = Box::new(std::io::BufReader::new(File::open(filename).map_err(
|
||||
|e| ErrorMsg {
|
||||
typ: ErrorType::NotFound,
|
||||
msg: format!("无法打开文件: {}", e),
|
||||
},
|
||||
)?));
|
||||
let path = path.splitn(2, "|").nth(1).unwrap();
|
||||
return preview_image_in_archive(path, reader, filename, options.as_ref(), None, 0)
|
||||
return CACHE_MANAGER
|
||||
.lock_blocking()
|
||||
.preview_image(path, options.as_ref())
|
||||
.map_err(|e| ErrorMsg {
|
||||
typ: ErrorType::Other,
|
||||
msg: format!("预览图片失败: {}", e),
|
||||
msg: e.to_string(),
|
||||
}).map(|data| {
|
||||
Response::new(data)
|
||||
});
|
||||
}
|
||||
let path = std::path::Path::new(path);
|
||||
@@ -636,5 +704,7 @@ pub fn preview_image(path: &str, options: Option<Vec<FileOptions>>) -> Result<Ve
|
||||
.map_err(|e| ErrorMsg {
|
||||
typ: ErrorType::Other,
|
||||
msg: format!("预览图片失败: {}", e),
|
||||
}).map(|data| {
|
||||
Response::new(data)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,6 +14,5 @@ export async function listDirectory(path: string, options?: FileOptions[]): Prom
|
||||
}
|
||||
|
||||
export async function previewImage(path: string, options?: FileOptions[]): Promise<Uint8Array> {
|
||||
const bytes: number[] = await invoke("preview_image", { path, options: options ?? null });
|
||||
return new Uint8Array(bytes);
|
||||
return await invoke("preview_image", { path, options: options ?? null });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user