mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-07-08 01:31:53 +08:00
add kr simpile crypt support
This commit is contained in:
@@ -32,7 +32,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"]
|
||||||
kirikiri = ["emote-psb"]
|
kirikiri = ["emote-psb", "flate2"]
|
||||||
yaneurao = []
|
yaneurao = []
|
||||||
yaneurao-itufuru = ["yaneurao"]
|
yaneurao-itufuru = ["yaneurao"]
|
||||||
# basic feature
|
# basic feature
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ pub trait Script: std::fmt::Debug {
|
|||||||
!matches!(output, OutputScriptType::Custom)
|
!matches!(output, OutputScriptType::Custom)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn custom_output_extension(&self) -> &'static str {
|
fn custom_output_extension<'a>(&'a self) -> &'a str {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod scn;
|
pub mod scn;
|
||||||
|
pub mod simple_crypt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|||||||
166
src/scripts/kirikiri/simple_crypt.rs
Normal file
166
src/scripts/kirikiri/simple_crypt.rs
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
use crate::ext::io::*;
|
||||||
|
use crate::scripts::base::*;
|
||||||
|
use crate::types::*;
|
||||||
|
use anyhow::Result;
|
||||||
|
use overf::wrapping;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SimpleCryptBuilder {}
|
||||||
|
|
||||||
|
impl SimpleCryptBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScriptBuilder for SimpleCryptBuilder {
|
||||||
|
fn default_encoding(&self) -> Encoding {
|
||||||
|
Encoding::Utf8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_script(
|
||||||
|
&self,
|
||||||
|
buf: Vec<u8>,
|
||||||
|
filename: &str,
|
||||||
|
_encoding: Encoding,
|
||||||
|
_archive_encoding: Encoding,
|
||||||
|
_config: &ExtraConfig,
|
||||||
|
) -> Result<Box<dyn Script>> {
|
||||||
|
Ok(Box::new(SimpleCrypt::new(buf, filename)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extensions(&self) -> &'static [&'static str] {
|
||||||
|
&[]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn script_type(&self) -> &'static ScriptType {
|
||||||
|
&ScriptType::KirikiriSimpleCrypt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
|
||||||
|
if buf_len >= 5
|
||||||
|
&& buf[0] == 0xfe
|
||||||
|
&& buf[1] == 0xfe
|
||||||
|
&& (buf[2] == 0 || buf[2] == 1 || buf[2] == 2)
|
||||||
|
&& buf[3] == 0xff
|
||||||
|
&& buf[4] == 0xfe
|
||||||
|
{
|
||||||
|
Some(10)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SimpleCrypt {
|
||||||
|
/// Crypt mode
|
||||||
|
crypt: u8,
|
||||||
|
data: MemReader,
|
||||||
|
ext: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleCrypt {
|
||||||
|
pub fn new(buf: Vec<u8>, filename: &str) -> Result<Self> {
|
||||||
|
let mut reader = MemReader::new(buf);
|
||||||
|
let mut header = [0u8; 5];
|
||||||
|
reader.read_exact(&mut header)?;
|
||||||
|
if header[0] != 0xfe
|
||||||
|
|| header[1] != 0xfe
|
||||||
|
|| (header[2] != 0 && header[2] != 1 && header[2] != 2)
|
||||||
|
|| header[3] != 0xff
|
||||||
|
|| header[4] != 0xfe
|
||||||
|
{
|
||||||
|
return Err(anyhow::anyhow!("Invalid SimpleCrypt header"));
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
crypt: header[2],
|
||||||
|
data: reader,
|
||||||
|
ext: std::path::Path::new(filename)
|
||||||
|
.extension()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.unwrap_or("")
|
||||||
|
.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unpack(crypt: u8, data: MemReaderRef) -> Result<Vec<u8>> {
|
||||||
|
match crypt {
|
||||||
|
0 => Self::unpack_mode0(data),
|
||||||
|
1 => Self::unpack_mode1(data),
|
||||||
|
2 => Self::unpack_mode2(data),
|
||||||
|
_ => Err(anyhow::anyhow!("Unsupported SimpleCrypt mode: {}", crypt)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack_mode0(input: MemReaderRef) -> Result<Vec<u8>> {
|
||||||
|
let mut data = Vec::with_capacity(input.data.len() - 3);
|
||||||
|
data.push(0xff);
|
||||||
|
data.push(0xfe);
|
||||||
|
data.extend_from_slice(&input.data[5..]);
|
||||||
|
for i in 2..data.len() {
|
||||||
|
let ch = data[i] as u16;
|
||||||
|
if ch >= 20 {
|
||||||
|
data[i] = wrapping! {ch ^ (((ch & 0xfe) << 8) ^ 1)} as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack_mode1(input: MemReaderRef) -> Result<Vec<u8>> {
|
||||||
|
let mut data = Vec::with_capacity(input.data.len() - 3);
|
||||||
|
data.push(0xff);
|
||||||
|
data.push(0xfe);
|
||||||
|
data.extend_from_slice(&input.data[5..]);
|
||||||
|
for i in 2..data.len() {
|
||||||
|
let mut ch = data[i] as u32;
|
||||||
|
ch = wrapping! {((ch & 0xaaaaaaaa) >> 1) | ((ch & 0x55555555) << 1)};
|
||||||
|
data[i] = ch as u8;
|
||||||
|
}
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack_mode2(mut reader: MemReaderRef) -> Result<Vec<u8>> {
|
||||||
|
reader.pos = 5;
|
||||||
|
let compressed = reader.read_u64()?;
|
||||||
|
debug_assert!(compressed + 5 == reader.data.len() as u64);
|
||||||
|
let uncompressed = reader.read_u64()?;
|
||||||
|
let mut stream = flate2::Decompress::new(false);
|
||||||
|
let mut data = Vec::with_capacity(uncompressed as usize + 2);
|
||||||
|
data.push(0xff);
|
||||||
|
data.push(0xfe);
|
||||||
|
data.resize(uncompressed as usize, 0);
|
||||||
|
stream.decompress(
|
||||||
|
&reader.data[reader.pos..],
|
||||||
|
&mut data[2..],
|
||||||
|
flate2::FlushDecompress::Finish,
|
||||||
|
)?;
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Script for SimpleCrypt {
|
||||||
|
fn default_output_script_type(&self) -> OutputScriptType {
|
||||||
|
OutputScriptType::Custom
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_format_type(&self) -> FormatOptions {
|
||||||
|
FormatOptions::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_output_supported(&self, output: OutputScriptType) -> bool {
|
||||||
|
matches!(output, OutputScriptType::Custom)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn custom_output_extension<'a>(&'a self) -> &'a str {
|
||||||
|
&self.ext
|
||||||
|
}
|
||||||
|
|
||||||
|
fn custom_export(&self, filename: &std::path::Path, _encoding: Encoding) -> Result<()> {
|
||||||
|
let data = Self::unpack(self.crypt, self.data.to_ref())?;
|
||||||
|
let mut writer = crate::utils::files::write_file(filename)?;
|
||||||
|
writer.write_all(&data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,8 @@ lazy_static::lazy_static! {
|
|||||||
Box::new(cat_system::image::hg3::Hg3ImageBuilder::new()),
|
Box::new(cat_system::image::hg3::Hg3ImageBuilder::new()),
|
||||||
#[cfg(feature = "kirikiri")]
|
#[cfg(feature = "kirikiri")]
|
||||||
Box::new(kirikiri::scn::ScnScriptBuilder::new()),
|
Box::new(kirikiri::scn::ScnScriptBuilder::new()),
|
||||||
|
#[cfg(feature = "kirikiri")]
|
||||||
|
Box::new(kirikiri::simple_crypt::SimpleCryptBuilder::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();
|
||||||
|
|||||||
@@ -272,6 +272,10 @@ pub enum ScriptType {
|
|||||||
#[value(alias("kr-scn"))]
|
#[value(alias("kr-scn"))]
|
||||||
/// Kirikiri SCN script
|
/// Kirikiri SCN script
|
||||||
KirikiriScn,
|
KirikiriScn,
|
||||||
|
#[cfg(feature = "kirikiri")]
|
||||||
|
#[value(alias("kr-simple-crypt"))]
|
||||||
|
/// Kirikiri SimpleCrypt's text file
|
||||||
|
KirikiriSimpleCrypt,
|
||||||
#[cfg(feature = "yaneurao-itufuru")]
|
#[cfg(feature = "yaneurao-itufuru")]
|
||||||
#[value(alias("itufuru"))]
|
#[value(alias("itufuru"))]
|
||||||
/// Yaneurao Itufuru script
|
/// Yaneurao Itufuru script
|
||||||
|
|||||||
Reference in New Issue
Block a user