mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-07-08 01:31:53 +08:00
Add new script hexen_haus support
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -605,9 +605,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
@@ -635,6 +635,7 @@ dependencies = [
|
|||||||
"json",
|
"json",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libtlg-rs",
|
"libtlg-rs",
|
||||||
|
"memchr",
|
||||||
"msg_tool_macro",
|
"msg_tool_macro",
|
||||||
"overf",
|
"overf",
|
||||||
"png",
|
"png",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ int-enum = { version = "1.2", optional = true }
|
|||||||
json = { version = "0.12", optional = true }
|
json = { version = "0.12", optional = true }
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
libtlg-rs = { version = "0.1", optional = true }
|
libtlg-rs = { version = "0.1", optional = true }
|
||||||
|
memchr = { version = "2.7", optional = true }
|
||||||
msg_tool_macro = { path = "./msg_tool_macro" }
|
msg_tool_macro = { path = "./msg_tool_macro" }
|
||||||
overf = "0.1"
|
overf = "0.1"
|
||||||
png = { version = "0.17", optional = true }
|
png = { version = "0.17", optional = true }
|
||||||
@@ -28,7 +29,7 @@ url = { version = "2.5", optional = true }
|
|||||||
utf16string = "0.2"
|
utf16string = "0.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["artemis", "artemis-arc", "bgi", "bgi-arc", "bgi-img", "cat-system", "cat-system-arc", "cat-system-img", "circus", "escude", "escude-arc", "kirikiri", "kirikiri-img", "will-plus", "yaneurao", "yaneurao-itufuru"]
|
default = ["artemis", "artemis-arc", "bgi", "bgi-arc", "bgi-img", "cat-system", "cat-system-arc", "cat-system-img", "circus", "escude", "escude-arc", "hexen-haus", "kirikiri", "kirikiri-img", "will-plus", "yaneurao", "yaneurao-itufuru"]
|
||||||
artemis = ["utils-escape"]
|
artemis = ["utils-escape"]
|
||||||
artemis-arc = ["artemis", "msg_tool_macro/artemis-arc", "sha1"]
|
artemis-arc = ["artemis", "msg_tool_macro/artemis-arc", "sha1"]
|
||||||
bgi = []
|
bgi = []
|
||||||
@@ -40,6 +41,7 @@ cat-system-img = ["cat-system", "flate2", "image", "utils-bit-stream"]
|
|||||||
circus = []
|
circus = []
|
||||||
escude = ["int-enum"]
|
escude = ["int-enum"]
|
||||||
escude-arc = ["escude", "rand", "utils-bit-stream"]
|
escude-arc = ["escude", "rand", "utils-bit-stream"]
|
||||||
|
hexen-haus = ["memchr", "utils-str"]
|
||||||
kirikiri = ["emote-psb", "fancy-regex", "flate2", "json", "utils-escape"]
|
kirikiri = ["emote-psb", "fancy-regex", "flate2", "json", "utils-escape"]
|
||||||
kirikiri-img = ["kirikiri", "emote-psb", "image", "libtlg-rs", "url"]
|
kirikiri-img = ["kirikiri", "emote-psb", "image", "libtlg-rs", "url"]
|
||||||
will-plus = ["utils-str"]
|
will-plus = ["utils-str"]
|
||||||
|
|||||||
250
src/scripts/hexen_haus/bin.rs
Normal file
250
src/scripts/hexen_haus/bin.rs
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
use crate::ext::io::*;
|
||||||
|
use crate::scripts::base::*;
|
||||||
|
use crate::types::*;
|
||||||
|
use crate::utils::encoding::*;
|
||||||
|
use crate::utils::str::*;
|
||||||
|
use anyhow::Result;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BinScriptBuilder {}
|
||||||
|
|
||||||
|
impl BinScriptBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
BinScriptBuilder {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScriptBuilder for BinScriptBuilder {
|
||||||
|
fn default_encoding(&self) -> Encoding {
|
||||||
|
Encoding::Cp932
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_script(
|
||||||
|
&self,
|
||||||
|
buf: Vec<u8>,
|
||||||
|
_filename: &str,
|
||||||
|
encoding: Encoding,
|
||||||
|
_archive_encoding: Encoding,
|
||||||
|
config: &ExtraConfig,
|
||||||
|
) -> Result<Box<dyn Script>> {
|
||||||
|
Ok(Box::new(BinScript::new(buf, encoding, config)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extensions(&self) -> &'static [&'static str] {
|
||||||
|
&["bin"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn script_type(&self) -> &'static ScriptType {
|
||||||
|
&ScriptType::HexenHaus
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
|
||||||
|
if buf_len >= 4 && buf.starts_with(b"NORI") {
|
||||||
|
return Some(10);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BinString {
|
||||||
|
str: String,
|
||||||
|
pos: usize,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BinScript {
|
||||||
|
data: MemReader,
|
||||||
|
strs: Vec<BinString>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinScript {
|
||||||
|
pub fn new(buf: Vec<u8>, encoding: Encoding, _config: &ExtraConfig) -> Result<Self> {
|
||||||
|
let mut data = MemReader::new(buf);
|
||||||
|
let mut header = [0; 4];
|
||||||
|
data.read_exact(&mut header)?;
|
||||||
|
if header != *b"NORI" {
|
||||||
|
return Err(anyhow::anyhow!("Invalid HexenHaus bin script header"));
|
||||||
|
}
|
||||||
|
for c in data.data.iter_mut() {
|
||||||
|
*c ^= 0x53;
|
||||||
|
}
|
||||||
|
data.pos = memchr::memmem::find(&data.data, b"_beginrp")
|
||||||
|
.ok_or(anyhow::anyhow!("Failed to find _beginrp"))?;
|
||||||
|
data.pos += 16;
|
||||||
|
let mut p = [0; 2];
|
||||||
|
let mut s = Vec::new();
|
||||||
|
let data_len = data.data.len();
|
||||||
|
let mut start_pos = data.pos;
|
||||||
|
let mut strs = Vec::new();
|
||||||
|
while data.pos < data_len {
|
||||||
|
data.read_exact(&mut p)?;
|
||||||
|
if p[0] == 0x53 {
|
||||||
|
if s.len() > 2 {
|
||||||
|
if let Ok(c) = decode_to_string(encoding, &s[s.len() - 2..], true) {
|
||||||
|
if c != "」" && c != "。" && c != "』" {
|
||||||
|
s.pop();
|
||||||
|
s.pop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.pop();
|
||||||
|
s.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.len() > 2 {
|
||||||
|
let d = decode_to_string(encoding, &s, true)?;
|
||||||
|
strs.push(BinString {
|
||||||
|
str: d,
|
||||||
|
pos: start_pos,
|
||||||
|
len: s.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
start_pos = data.pos;
|
||||||
|
s.clear();
|
||||||
|
} else if p[1] == 0x53 {
|
||||||
|
if s.len() > 2 {
|
||||||
|
let d = decode_to_string(encoding, &s, true)?;
|
||||||
|
strs.push(BinString {
|
||||||
|
str: d,
|
||||||
|
pos: start_pos,
|
||||||
|
len: s.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
start_pos = data.pos;
|
||||||
|
s.clear();
|
||||||
|
} else {
|
||||||
|
s.extend_from_slice(&p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.len() > 2 {
|
||||||
|
s.pop();
|
||||||
|
s.pop();
|
||||||
|
if s.len() > 2 {
|
||||||
|
let d = decode_to_string(encoding, &s, true)?;
|
||||||
|
strs.push(BinString {
|
||||||
|
str: d,
|
||||||
|
pos: start_pos,
|
||||||
|
len: s.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(BinScript { data, strs })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Script for BinScript {
|
||||||
|
fn default_output_script_type(&self) -> OutputScriptType {
|
||||||
|
OutputScriptType::Json
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_format_type(&self) -> FormatOptions {
|
||||||
|
FormatOptions::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_messages(&self) -> Result<Vec<Message>> {
|
||||||
|
let mut messages: Vec<Message> = Vec::new();
|
||||||
|
for str in &self.strs {
|
||||||
|
let message = if let Some(ind) = str.str.find("「") {
|
||||||
|
let (name, mes) = str.str.split_at(ind);
|
||||||
|
let mut name = name.to_string();
|
||||||
|
if name.is_empty() {
|
||||||
|
if let Some(m) = messages.pop() {
|
||||||
|
name = m.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message {
|
||||||
|
name: Some(name.to_string()),
|
||||||
|
message: mes.to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Message {
|
||||||
|
name: None,
|
||||||
|
message: str.str.clone(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
messages.push(message);
|
||||||
|
}
|
||||||
|
Ok(messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_messages<'a>(
|
||||||
|
&'a self,
|
||||||
|
mut messages: Vec<Message>,
|
||||||
|
mut file: Box<dyn WriteSeek + 'a>,
|
||||||
|
encoding: Encoding,
|
||||||
|
replacement: Option<&'a ReplacementTable>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut data = MemWriter::from_vec(self.data.data.clone());
|
||||||
|
let mut i = 0;
|
||||||
|
for str in self.strs.iter() {
|
||||||
|
if i >= messages.len() {
|
||||||
|
return Err(anyhow::anyhow!("Not enough messages."));
|
||||||
|
}
|
||||||
|
if let Some(ind) = str.str.find("「") {
|
||||||
|
let (name, _) = str.str.split_at(ind);
|
||||||
|
let mut target = String::new();
|
||||||
|
if !name.is_empty() {
|
||||||
|
let mut name = match &messages[i].name {
|
||||||
|
Some(n) => n.to_owned(),
|
||||||
|
None => return Err(anyhow::anyhow!("Missing name for message.")),
|
||||||
|
};
|
||||||
|
if let Some(repl) = replacement {
|
||||||
|
for (k, v) in &repl.map {
|
||||||
|
name = name.replace(k, v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
target.push_str(&name);
|
||||||
|
}
|
||||||
|
let mut mes = messages[i].message.clone();
|
||||||
|
if let Some(repl) = replacement {
|
||||||
|
for (k, v) in &repl.map {
|
||||||
|
mes = mes.replace(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.push_str(&mes);
|
||||||
|
let mut encoded = encode_string(encoding, &target, false)?;
|
||||||
|
if encoded.len() > str.len {
|
||||||
|
eprintln!("Warning: Message '{}' is too long, truncating.", target);
|
||||||
|
crate::COUNTER.inc_warning();
|
||||||
|
encoded = truncate_string(&target, str.len, encoding, false)?;
|
||||||
|
}
|
||||||
|
while encoded.len() < str.len {
|
||||||
|
encoded.push(32); // Fill with spaces
|
||||||
|
}
|
||||||
|
data.write_all_at(str.pos, &encoded)?;
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
let mut target = if let Some(name) = messages[i].name.take() {
|
||||||
|
name
|
||||||
|
} else {
|
||||||
|
let s = messages[i].message.clone();
|
||||||
|
i += 1;
|
||||||
|
s
|
||||||
|
};
|
||||||
|
if let Some(repl) = replacement {
|
||||||
|
for (k, v) in &repl.map {
|
||||||
|
target = target.replace(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut encoded = encode_string(encoding, &target, false)?;
|
||||||
|
if encoded.len() > str.len {
|
||||||
|
eprintln!("Warning: Message '{}' is too long, truncating.", target);
|
||||||
|
crate::COUNTER.inc_warning();
|
||||||
|
encoded = truncate_string(&target, str.len, encoding, false)?;
|
||||||
|
}
|
||||||
|
while encoded.len() < str.len {
|
||||||
|
encoded.push(32); // Fill with spaces
|
||||||
|
}
|
||||||
|
data.write_all_at(str.pos, &encoded)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut data = data.into_inner();
|
||||||
|
for d in data.iter_mut() {
|
||||||
|
*d ^= 0x53;
|
||||||
|
}
|
||||||
|
file.write_all(&data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/scripts/hexen_haus/mod.rs
Normal file
1
src/scripts/hexen_haus/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod bin;
|
||||||
@@ -9,6 +9,8 @@ pub mod cat_system;
|
|||||||
pub mod circus;
|
pub mod circus;
|
||||||
#[cfg(feature = "escude")]
|
#[cfg(feature = "escude")]
|
||||||
pub mod escude;
|
pub mod escude;
|
||||||
|
#[cfg(feature = "hexen-haus")]
|
||||||
|
pub mod hexen_haus;
|
||||||
#[cfg(feature = "kirikiri")]
|
#[cfg(feature = "kirikiri")]
|
||||||
pub mod kirikiri;
|
pub mod kirikiri;
|
||||||
#[cfg(feature = "will-plus")]
|
#[cfg(feature = "will-plus")]
|
||||||
@@ -76,6 +78,8 @@ lazy_static::lazy_static! {
|
|||||||
Box::new(artemis::ast::AstScriptBuilder::new()),
|
Box::new(artemis::ast::AstScriptBuilder::new()),
|
||||||
#[cfg(feature = "artemis")]
|
#[cfg(feature = "artemis")]
|
||||||
Box::new(artemis::asb::ArtemisAsbBuilder::new()),
|
Box::new(artemis::asb::ArtemisAsbBuilder::new()),
|
||||||
|
#[cfg(feature = "hexen-haus")]
|
||||||
|
Box::new(hexen_haus::bin::BinScriptBuilder::new()),
|
||||||
];
|
];
|
||||||
pub static ref ALL_EXTS: Vec<String> =
|
pub static ref ALL_EXTS: Vec<String> =
|
||||||
BUILDER.iter().flat_map(|b| b.extensions()).map(|s| s.to_string()).collect();
|
BUILDER.iter().flat_map(|b| b.extensions()).map(|s| s.to_string()).collect();
|
||||||
|
|||||||
@@ -301,6 +301,9 @@ pub enum ScriptType {
|
|||||||
#[cfg(feature = "escude")]
|
#[cfg(feature = "escude")]
|
||||||
/// Escude list script
|
/// Escude list script
|
||||||
EscudeList,
|
EscudeList,
|
||||||
|
#[cfg(feature = "hexen-haus")]
|
||||||
|
/// HexenHaus bin script
|
||||||
|
HexenHaus,
|
||||||
#[cfg(feature = "kirikiri")]
|
#[cfg(feature = "kirikiri")]
|
||||||
#[value(alias("kr-scn"))]
|
#[value(alias("kr-scn"))]
|
||||||
/// Kirikiri SCN script
|
/// Kirikiri SCN script
|
||||||
|
|||||||
Reference in New Issue
Block a user