mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-23 12:17:04 +08:00
Add support to insert a new language to kirikiri scn script
This commit is contained in:
@@ -525,6 +525,10 @@ pub struct Arg {
|
|||||||
/// Workers count for packing file in Kirikiri XP3 archive in parallel.
|
/// Workers count for packing file in Kirikiri XP3 archive in parallel.
|
||||||
/// This not works when segment is disabled.
|
/// This not works when segment is disabled.
|
||||||
pub xp3_pack_workers: usize,
|
pub xp3_pack_workers: usize,
|
||||||
|
#[cfg(feature = "kirikiri")]
|
||||||
|
#[arg(long, global = true)]
|
||||||
|
/// Insert new language at the specified index in Kirikiri SCN script. If index is out of bounds, this flags will be ignored.
|
||||||
|
pub kirikiri_language_insert: bool,
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
/// Command
|
/// Command
|
||||||
pub command: Command,
|
pub command: Command,
|
||||||
|
|||||||
@@ -85,6 +85,26 @@ impl From<f32> for PsbValueFixed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<PsbObjectFixed> for PsbValueFixed {
|
||||||
|
fn from(value: PsbObjectFixed) -> Self {
|
||||||
|
PsbValueFixed::Object(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PsbListFixed> for PsbValueFixed {
|
||||||
|
fn from(value: PsbListFixed) -> Self {
|
||||||
|
PsbValueFixed::List(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&[PsbValueFixed]> for PsbValueFixed {
|
||||||
|
fn from(value: &[PsbValueFixed]) -> Self {
|
||||||
|
PsbValueFixed::List(PsbListFixed {
|
||||||
|
values: value.to_vec(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PsbValueFixed {
|
impl PsbValueFixed {
|
||||||
/// Converts this value to original PSB value type.
|
/// Converts this value to original PSB value type.
|
||||||
pub fn to_psb(self, warn_on_none: bool) -> PsbValue {
|
pub fn to_psb(self, warn_on_none: bool) -> PsbValue {
|
||||||
@@ -284,6 +304,33 @@ impl PsbValueFixed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clears all members in a list. If this value is not a list, it will be converted to an empty list.
|
||||||
|
pub fn clear_members(&mut self) {
|
||||||
|
match self {
|
||||||
|
PsbValueFixed::List(l) => {
|
||||||
|
l.clear();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
*self = PsbValueFixed::List(PsbListFixed { values: vec![] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new member at the specified index in a list. If this value is not a list, it will be converted to a list.
|
||||||
|
/// If the index is out of bounds, the value will be appended to the end of the list.
|
||||||
|
pub fn insert_member<T: Into<PsbValueFixed>>(&mut self, index: usize, value: T) {
|
||||||
|
match self {
|
||||||
|
PsbValueFixed::List(l) => {
|
||||||
|
l.insert(index, value);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
*self = PsbValueFixed::List(PsbListFixed {
|
||||||
|
values: vec![value.into()],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the resource ID if this value is a resource reference.
|
/// Returns the resource ID if this value is a resource reference.
|
||||||
pub fn resource_id(&self) -> Option<u64> {
|
pub fn resource_id(&self) -> Option<u64> {
|
||||||
match self {
|
match self {
|
||||||
@@ -615,6 +662,21 @@ impl PsbListFixed {
|
|||||||
self.values.len()
|
self.values.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clears all values in the list.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.values.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new value at the specified index in the list.
|
||||||
|
/// If the index is out of bounds, the value will be appended to the end of the list.
|
||||||
|
pub fn insert<V: Into<PsbValueFixed>>(&mut self, index: usize, value: V) {
|
||||||
|
if index <= self.values.len() {
|
||||||
|
self.values.insert(index, value.into());
|
||||||
|
} else {
|
||||||
|
self.values.push(value.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts this PSB list to a JSON value.
|
/// Converts this PSB list to a JSON value.
|
||||||
#[cfg(feature = "json")]
|
#[cfg(feature = "json")]
|
||||||
pub fn to_json(&self) -> JsonValue {
|
pub fn to_json(&self) -> JsonValue {
|
||||||
|
|||||||
@@ -2762,6 +2762,8 @@ fn main() {
|
|||||||
xp3_zstd: arg.xp3_zstd,
|
xp3_zstd: arg.xp3_zstd,
|
||||||
#[cfg(feature = "kirikiri-arc")]
|
#[cfg(feature = "kirikiri-arc")]
|
||||||
xp3_pack_workers: arg.xp3_pack_workers,
|
xp3_pack_workers: arg.xp3_pack_workers,
|
||||||
|
#[cfg(feature = "kirikiri")]
|
||||||
|
kirikiri_language_insert: arg.kirikiri_language_insert,
|
||||||
});
|
});
|
||||||
match &arg.command {
|
match &arg.command {
|
||||||
args::Command::Export { input, output } => {
|
args::Command::Export { input, output } => {
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ pub struct ScnScript {
|
|||||||
custom_yaml: bool,
|
custom_yaml: bool,
|
||||||
title: bool,
|
title: bool,
|
||||||
chat_multilang: bool,
|
chat_multilang: bool,
|
||||||
|
insert_language: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScnScript {
|
impl ScnScript {
|
||||||
@@ -157,6 +158,7 @@ impl ScnScript {
|
|||||||
custom_yaml: config.custom_yaml,
|
custom_yaml: config.custom_yaml,
|
||||||
title: config.kirikiri_title,
|
title: config.kirikiri_title,
|
||||||
chat_multilang: config.kirikiri_chat_multilang,
|
chat_multilang: config.kirikiri_chat_multilang,
|
||||||
|
insert_language: config.kirikiri_language_insert,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,6 +441,15 @@ impl Script for ScnScript {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
let ori_lang = if self.insert_language && self.language_index == 0 {
|
||||||
|
if let Some(lang) = root["languages"][0].as_str() {
|
||||||
|
Some(lang.to_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let scenes = &mut root["scenes"];
|
let scenes = &mut root["scenes"];
|
||||||
if !scenes.is_list() {
|
if !scenes.is_list() {
|
||||||
return Err(anyhow::anyhow!("scenes is not an array"));
|
return Err(anyhow::anyhow!("scenes is not an array"));
|
||||||
@@ -454,6 +465,11 @@ impl Script for ScnScript {
|
|||||||
None
|
None
|
||||||
},
|
},
|
||||||
self.filename.clone(),
|
self.filename.clone(),
|
||||||
|
if self.chat_multilang {
|
||||||
|
ori_lang.clone()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
for (i, scene) in scenes.members_mut().enumerate() {
|
for (i, scene) in scenes.members_mut().enumerate() {
|
||||||
@@ -477,13 +493,23 @@ impl Script for ScnScript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.language_index == 0 {
|
if self.language_index == 0 {
|
||||||
scene["title"].set_string(title);
|
if self.insert_language {
|
||||||
|
let ori_title = scene["title"].as_str().unwrap_or("").to_string();
|
||||||
|
scene["title"].push_member(title);
|
||||||
|
scene["title"].push_member(ori_title);
|
||||||
|
} else {
|
||||||
|
scene["title"].set_string(title);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let ori_title = scene["title"].as_str().unwrap_or("").to_string();
|
let ori_title = scene["title"].as_str().unwrap_or("").to_string();
|
||||||
while scene["title"].len() < self.language_index {
|
while scene["title"].len() < self.language_index {
|
||||||
scene["title"].push_member(ori_title.clone());
|
scene["title"].push_member(ori_title.clone());
|
||||||
}
|
}
|
||||||
scene["title"].push_member(title);
|
if self.insert_language {
|
||||||
|
scene["title"].insert_member(self.language_index, title);
|
||||||
|
} else {
|
||||||
|
scene["title"].push_member(title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cur_mes = mes.next();
|
cur_mes = mes.next();
|
||||||
} else if scene["title"].is_list() {
|
} else if scene["title"].is_list() {
|
||||||
@@ -502,10 +528,17 @@ impl Script for ScnScript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let ori_title = scene["title"][0].as_str().unwrap_or("").to_string();
|
let ori_title = scene["title"][0].as_str().unwrap_or("").to_string();
|
||||||
while scene["title"].len() <= self.language_index {
|
if self.insert_language {
|
||||||
scene["title"].push_member(ori_title.clone());
|
while scene["title"].len() < self.language_index {
|
||||||
|
scene["title"].push_member(ori_title.clone());
|
||||||
|
}
|
||||||
|
scene["title"].insert_member(self.language_index, title);
|
||||||
|
} else {
|
||||||
|
while scene["title"].len() <= self.language_index {
|
||||||
|
scene["title"].push_member(ori_title.clone());
|
||||||
|
}
|
||||||
|
scene["title"][self.language_index].set_string(title);
|
||||||
}
|
}
|
||||||
scene["title"][self.language_index].set_string(title);
|
|
||||||
cur_mes = mes.next();
|
cur_mes = mes.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,8 +557,16 @@ impl Script for ScnScript {
|
|||||||
let has_name = text[0].is_string();
|
let has_name = text[0].is_string();
|
||||||
let has_display_name;
|
let has_display_name;
|
||||||
if text[1].is_list() {
|
if text[1].is_list() {
|
||||||
while text[1].len() <= self.language_index {
|
if self.insert_language {
|
||||||
text[1][self.language_index] = text[1][0].clone();
|
let ori = text[1][0].clone();
|
||||||
|
while text[1].len() < self.language_index {
|
||||||
|
text[1].push_member(ori.clone());
|
||||||
|
}
|
||||||
|
text[1].insert_member(self.language_index, ori.clone());
|
||||||
|
} else {
|
||||||
|
while text[1].len() <= self.language_index {
|
||||||
|
text[1][self.language_index] = text[1][0].clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if text[1][self.language_index].is_list()
|
if text[1][self.language_index].is_list()
|
||||||
&& text[1][self.language_index].len() >= 2
|
&& text[1][self.language_index].len() >= 2
|
||||||
@@ -624,8 +665,16 @@ impl Script for ScnScript {
|
|||||||
}
|
}
|
||||||
text[2].set_string(message.replace("\n", "\\n"));
|
text[2].set_string(message.replace("\n", "\\n"));
|
||||||
} else if text[2].is_list() {
|
} else if text[2].is_list() {
|
||||||
while text[2].len() <= self.language_index {
|
if self.insert_language {
|
||||||
text[2][self.language_index] = text[2][0].clone();
|
let ori = text[2][0].clone();
|
||||||
|
while text[2].len() < self.language_index {
|
||||||
|
text[2].push_member(ori.clone());
|
||||||
|
}
|
||||||
|
text[2].insert_member(self.language_index, ori.clone());
|
||||||
|
} else {
|
||||||
|
while text[2].len() <= self.language_index {
|
||||||
|
text[2][self.language_index] = text[2][0].clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if text[2][self.language_index].is_list()
|
if text[2][self.language_index].is_list()
|
||||||
&& text[2][self.language_index].len() >= 2
|
&& text[2][self.language_index].len() >= 2
|
||||||
@@ -691,19 +740,42 @@ impl Script for ScnScript {
|
|||||||
}
|
}
|
||||||
if self.language_index != 0
|
if self.language_index != 0
|
||||||
&& {
|
&& {
|
||||||
while select["language"].len() <= self.language_index {
|
if self.insert_language {
|
||||||
// TenShiSouZou
|
while select["language"].len() < self.language_index {
|
||||||
// first block is null
|
// TenShiSouZou
|
||||||
if select["language"].len() == 0 {
|
// first block is null
|
||||||
select["language"].push_member(PsbValueFixed::Null);
|
if select["language"].len() == 0 {
|
||||||
continue;
|
select["language"].push_member(PsbValueFixed::Null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut obj = PsbObjectFixed::new();
|
||||||
|
obj["text"].set_str("");
|
||||||
|
obj["speechtext"].set_str("");
|
||||||
|
obj["searchtext"].set_str("");
|
||||||
|
obj["textlength"].set_i64(0);
|
||||||
|
select["language"][self.language_index].set_obj(obj);
|
||||||
}
|
}
|
||||||
let mut obj = PsbObjectFixed::new();
|
let mut obj = PsbObjectFixed::new();
|
||||||
obj["text"].set_str("");
|
obj["text"].set_str("");
|
||||||
obj["speechtext"].set_str("");
|
obj["speechtext"].set_str("");
|
||||||
obj["searchtext"].set_str("");
|
obj["searchtext"].set_str("");
|
||||||
obj["textlength"].set_i64(0);
|
obj["textlength"].set_i64(0);
|
||||||
select["language"][self.language_index].set_obj(obj);
|
select["language"].insert_member(self.language_index, obj);
|
||||||
|
} else {
|
||||||
|
while select["language"].len() <= self.language_index {
|
||||||
|
// TenShiSouZou
|
||||||
|
// first block is null
|
||||||
|
if select["language"].len() == 0 {
|
||||||
|
select["language"].push_member(PsbValueFixed::Null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut obj = PsbObjectFixed::new();
|
||||||
|
obj["text"].set_str("");
|
||||||
|
obj["speechtext"].set_str("");
|
||||||
|
obj["searchtext"].set_str("");
|
||||||
|
obj["textlength"].set_i64(0);
|
||||||
|
select["language"][self.language_index].set_obj(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -742,6 +814,18 @@ impl Script for ScnScript {
|
|||||||
text = text.replace(key, value);
|
text = text.replace(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.insert_language {
|
||||||
|
let ori_text = select["text"].as_str().unwrap_or("").to_string();
|
||||||
|
let mut obj = PsbObjectFixed::new();
|
||||||
|
obj["text"].set_string(ori_text.replace("\n", "\\n"));
|
||||||
|
obj["speechtext"].set_string(get_save_message(&ori_text, true));
|
||||||
|
obj["searchtext"].set_string(get_save_message(&ori_text, false));
|
||||||
|
obj["textlength"].set_i64(ori_text.chars().count() as i64);
|
||||||
|
if select["language"].len() < 1 {
|
||||||
|
select["language"].push_member(PsbValueFixed::Null);
|
||||||
|
}
|
||||||
|
select["language"].insert_member(1, obj);
|
||||||
|
}
|
||||||
select["text"].set_string(text.replace("\n", "\\n"));
|
select["text"].set_string(text.replace("\n", "\\n"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -918,6 +1002,7 @@ struct ImportMes<'a> {
|
|||||||
key: String,
|
key: String,
|
||||||
text_key: String,
|
text_key: String,
|
||||||
filename: String,
|
filename: String,
|
||||||
|
ori_text_key: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ImportMes<'a> {
|
impl<'a> ImportMes<'a> {
|
||||||
@@ -927,6 +1012,7 @@ impl<'a> ImportMes<'a> {
|
|||||||
key: String,
|
key: String,
|
||||||
lang: Option<String>,
|
lang: Option<String>,
|
||||||
filename: String,
|
filename: String,
|
||||||
|
ori_lang: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
messages,
|
messages,
|
||||||
@@ -937,6 +1023,7 @@ impl<'a> ImportMes<'a> {
|
|||||||
.file_stem()
|
.file_stem()
|
||||||
.map(|s| s.to_string_lossy().to_string())
|
.map(|s| s.to_string_lossy().to_string())
|
||||||
.unwrap_or_else(|| "global".to_string()),
|
.unwrap_or_else(|| "global".to_string()),
|
||||||
|
ori_text_key: ori_lang.map(|s| format!("text_{}", s)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -977,6 +1064,13 @@ impl<'a> ImportMes<'a> {
|
|||||||
text = text.replace(key, value);
|
text = text.replace(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.text_key == "text" {
|
||||||
|
if let Some(ori_key) = &self.ori_text_key {
|
||||||
|
let ori_text =
|
||||||
|
obj["text"].as_str().unwrap_or("").to_string();
|
||||||
|
obj[ori_key].set_string(ori_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
obj[&self.text_key].set_string(text.replace("\n", "\\n"));
|
obj[&self.text_key].set_string(text.replace("\n", "\\n"));
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
@@ -991,6 +1085,11 @@ impl<'a> ImportMes<'a> {
|
|||||||
text = text.replace(key, value);
|
text = text.replace(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(ori_key) = &self.ori_text_key {
|
||||||
|
let ori_text =
|
||||||
|
obj["text"].as_str().unwrap_or("").to_string();
|
||||||
|
obj[ori_key].set_string(ori_text);
|
||||||
|
}
|
||||||
obj[&self.text_key].set_string(text.replace("\n", "\\n"));
|
obj[&self.text_key].set_string(text.replace("\n", "\\n"));
|
||||||
} else {
|
} else {
|
||||||
warn_not_found(text.to_string());
|
warn_not_found(text.to_string());
|
||||||
@@ -1015,6 +1114,15 @@ impl<'a> ImportMes<'a> {
|
|||||||
text = text.replace(key, value);
|
text = text.replace(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.text_key == "text" {
|
||||||
|
if let Some(ori_key) = &self.ori_text_key {
|
||||||
|
let len = list.len();
|
||||||
|
let ori_text =
|
||||||
|
list[i].as_str().unwrap_or("").to_string();
|
||||||
|
list[len].set_str(ori_key);
|
||||||
|
list[len + 1].set_string(ori_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
list[i].set_string(text.replace("\n", "\\n"));
|
list[i].set_string(text.replace("\n", "\\n"));
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -503,6 +503,9 @@ pub struct ExtraConfig {
|
|||||||
/// Workers count for packing file in Kirikiri XP3 archive in parallel. Default is 1.
|
/// Workers count for packing file in Kirikiri XP3 archive in parallel. Default is 1.
|
||||||
/// This not works when segment is disabled.
|
/// This not works when segment is disabled.
|
||||||
pub xp3_pack_workers: usize,
|
pub xp3_pack_workers: usize,
|
||||||
|
#[cfg(feature = "kirikiri")]
|
||||||
|
/// Insert new language at the specified index in Kirikiri SCN script. If index is out of bounds, this flags will be ignored.
|
||||||
|
pub kirikiri_language_insert: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
|||||||
Reference in New Issue
Block a user