From 833be4fce0ea2e0e362583b723f285ce498e4d15 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 3 Jun 2025 19:08:00 +0800 Subject: [PATCH] Add import support for escude list --- src/main.rs | 17 ++++++++++++----- src/scripts/base.rs | 29 ++++++++++++++++++++++++++++- src/scripts/escude/list.rs | 34 +++++++++++++++++++++++++++++++++- src/utils/encoding_win.rs | 3 +++ 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index b851029..7265039 100644 --- a/src/main.rs +++ b/src/main.rs @@ -548,10 +548,13 @@ pub fn import_script( ) -> anyhow::Result { eprintln!("Importing {}", filename); let (script, builder) = parse_script(filename, arg, config)?; - let of = match &arg.output_type { + let mut of = match &arg.output_type { Some(t) => t.clone(), None => script.default_output_script_type(), }; + if !script.is_output_supported(of) { + of = script.default_output_script_type(); + } let out_f = if is_dir { let f = std::path::PathBuf::from(filename); let mut pb = std::path::PathBuf::from(&imp_cfg.output); @@ -581,12 +584,11 @@ pub fn import_script( let mut parser = output_scripts::m3t::M3tParser::new(&s); parser.parse()? } - _ => { - eprintln!("Unsupported output script type for import: {:?}", of); - return Ok(types::ScriptResult::Ignored); + types::OutputScriptType::Custom => { + Vec::new() // Custom scripts handle their own messages } }; - if mes.is_empty() { + if !of.is_custom() && mes.is_empty() { eprintln!("No messages found"); return Ok(types::ScriptResult::Ignored); } @@ -602,6 +604,11 @@ pub fn import_script( } else { imp_cfg.patched.clone() }; + if of.is_custom() { + let enc = get_output_encoding(arg); + script.custom_import_filename(&out_f, &patched_f, encoding, enc)?; + return Ok(types::ScriptResult::Ok); + } let fmt = match imp_cfg.patched_format { Some(fmt) => match fmt { types::FormatType::Fixed => types::FormatOptions::Fixed { diff --git a/src/scripts/base.rs b/src/scripts/base.rs index f2dc865..88f326f 100644 --- a/src/scripts/base.rs +++ b/src/scripts/base.rs @@ -1,11 +1,15 @@ use crate::types::*; use anyhow::Result; -use std::io::{Read, Seek}; +use std::io::{Read, Seek, Write}; pub trait ReadSeek: Read + Seek + std::fmt::Debug {} +pub trait WriteSeek: Write + Seek {} + impl ReadSeek for T {} +impl WriteSeek for T {} + pub trait ScriptBuilder: std::fmt::Debug { fn default_encoding(&self) -> Encoding; @@ -114,6 +118,29 @@ pub trait Script: std::fmt::Debug { )) } + fn custom_import( + &self, + _custom_filename: &str, + _file: Box, + _encoding: Encoding, + _output_encoding: Encoding, + ) -> Result<()> { + Err(anyhow::anyhow!( + "This script type does not support custom import." + )) + } + + fn custom_import_filename( + &self, + custom_filename: &str, + filename: &str, + encoding: Encoding, + output_encoding: Encoding, + ) -> Result<()> { + let f = std::fs::File::create(filename)?; + self.custom_import(custom_filename, Box::new(f), encoding, output_encoding) + } + fn is_archive(&self) -> bool { false } diff --git a/src/scripts/escude/list.rs b/src/scripts/escude/list.rs index 4ea71e8..6681c11 100644 --- a/src/scripts/escude/list.rs +++ b/src/scripts/escude/list.rs @@ -1,8 +1,8 @@ -use crate::ext::io::*; use crate::scripts::base::*; use crate::types::*; use crate::utils::encoding::encode_string; use crate::utils::struct_pack::*; +use crate::{ext::io::*, utils::encoding::decode_to_string}; use anyhow::Result; use msg_tool_macro::*; use serde::{Deserialize, Serialize}; @@ -283,6 +283,38 @@ impl Script for EscudeBinList { writer.flush()?; Ok(()) } + + fn custom_import( + &self, + custom_filename: &str, + mut writer: Box, + encoding: Encoding, + output_encoding: Encoding, + ) -> Result<()> { + let input = crate::utils::files::read_file(custom_filename)?; + let s = decode_to_string(output_encoding, &input)?; + let entries: Vec = serde_json::from_str(&s) + .map_err(|e| anyhow::anyhow!("Failed to read Escude list from JSON: {}", e))?; + writer.write_all(b"LIST")?; + writer.write_u32(0)?; // Placeholder for size + let mut total_size = 0; + for entry in entries { + let cur_pos = writer.stream_position()?; + writer.write_u32(entry.id)?; + writer.write_u32(0)?; // Placeholder for size + entry.data.pack(&mut writer, false, encoding)?; + let end_pos = writer.stream_position()?; + let size = (end_pos - cur_pos - 8) as u32; // 8 bytes for id and size + writer.seek(std::io::SeekFrom::Start(cur_pos + 4))?; // Seek to size position + writer.write_u32(size)?; + writer.seek(std::io::SeekFrom::Start(end_pos))?; // Seek to end + total_size += size + 8; + } + writer.seek(std::io::SeekFrom::Start(4))?; // Seek back to size position + writer.write_u32(total_size)?; + writer.flush()?; + Ok(()) + } } #[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)] diff --git a/src/utils/encoding_win.rs b/src/utils/encoding_win.rs index b1a8f10..336acb0 100644 --- a/src/utils/encoding_win.rs +++ b/src/utils/encoding_win.rs @@ -83,6 +83,9 @@ pub fn decode_to_string(cp: u32, data: &[u8]) -> Result { } pub fn encode_string(cp: u32, data: &str, check: bool) -> Result, WinError> { + if data.is_empty() { + return Ok(Vec::new()); + } let wstr = data.encode_utf16().collect::>(); let needed_len = unsafe { WideCharToMultiByte(