mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-06 12:58:45 +08:00
Add MizukakeCrypt support (untested)
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1464,8 +1464,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "msg_tool_macro"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4d149ce067a4e8d9a4061c545d6b6ce22c48598ff07603983bcb2f1c961d7e2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
|
||||
@@ -36,7 +36,7 @@ markup5ever = { version = "0.38", optional = true }
|
||||
markup5ever_rcdom = { version = "0.38", optional = true }
|
||||
memchr = { version = "2.7", optional = true }
|
||||
mozjpeg = { version = "0.10", optional = true }
|
||||
msg_tool_macro = { version = "0.3.0" }
|
||||
msg_tool_macro = { path = "./msg_tool_macro" }
|
||||
num_cpus = "1.17"
|
||||
overf = "0.1"
|
||||
parse-size = { version = "1.1", optional = true }
|
||||
|
||||
@@ -61,6 +61,96 @@ pub fn struct_unpack_impl_for_num(item: TokenStream) -> TokenStream {
|
||||
output.into()
|
||||
}
|
||||
|
||||
fn has_skip_fmt_attr(field: &syn::Field) -> bool {
|
||||
field.attrs.iter().any(|attr| attr.path().is_ident("skip_fmt"))
|
||||
}
|
||||
|
||||
#[proc_macro_derive(MyDebug, attributes(skip_fmt))]
|
||||
pub fn debug_macro_derive(input: TokenStream) -> TokenStream {
|
||||
let ast = syn::parse_macro_input!(input as syn::DeriveInput);
|
||||
let syn::DeriveInput {
|
||||
ident,
|
||||
generics,
|
||||
data,
|
||||
..
|
||||
} = ast;
|
||||
|
||||
let data_struct = match data {
|
||||
syn::Data::Struct(data_struct) => data_struct,
|
||||
_ => {
|
||||
return syn::Error::new(ident.span(), "`Debug` derive only supports structs")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
|
||||
let mut generics = generics;
|
||||
{
|
||||
let where_clause = generics.make_where_clause();
|
||||
for field in data_struct.fields.iter().filter(|field| !has_skip_fmt_attr(field)) {
|
||||
let ty = &field.ty;
|
||||
where_clause
|
||||
.predicates
|
||||
.push(syn::parse_quote!(#ty: std::fmt::Debug));
|
||||
}
|
||||
}
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let fmt_body = match &data_struct.fields {
|
||||
syn::Fields::Named(fields) => {
|
||||
let fields_fmt = fields
|
||||
.named
|
||||
.iter()
|
||||
.filter(|field| !has_skip_fmt_attr(field))
|
||||
.filter_map(|field| {
|
||||
let ident = field.ident.as_ref()?;
|
||||
let field_name = ident.to_string();
|
||||
Some(quote::quote! {
|
||||
debug_struct.field(#field_name, &self.#ident);
|
||||
})
|
||||
});
|
||||
quote::quote! {
|
||||
let mut debug_struct = f.debug_struct(stringify!(#ident));
|
||||
#(#fields_fmt)*
|
||||
debug_struct.finish()
|
||||
}
|
||||
}
|
||||
syn::Fields::Unnamed(fields) => {
|
||||
let fields_fmt = fields
|
||||
.unnamed
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, field)| !has_skip_fmt_attr(field))
|
||||
.map(|(idx, _)| {
|
||||
let idx = syn::Index::from(idx);
|
||||
quote::quote! {
|
||||
debug_tuple.field(&self.#idx);
|
||||
}
|
||||
});
|
||||
quote::quote! {
|
||||
let mut debug_tuple = f.debug_tuple(stringify!(#ident));
|
||||
#(#fields_fmt)*
|
||||
debug_tuple.finish()
|
||||
}
|
||||
}
|
||||
syn::Fields::Unit => {
|
||||
quote::quote! {
|
||||
f.write_str(stringify!(#ident))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
quote::quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics std::fmt::Debug for #ident #ty_generics #where_clause {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
#fmt_body
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Macro to derive `StructPack` trait for structs.
|
||||
///
|
||||
/// make sure to import the necessary imports:
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"Fate/stay night": {
|
||||
"$type": "FateCrypt"
|
||||
},
|
||||
"Mizu no Kakera ~Once Summer of Islet~": {
|
||||
"$type": "MizukakeCrypt",
|
||||
"Title": "みずのかけら -once summer of islet-"
|
||||
},
|
||||
"Sorairo no Shizuku": {
|
||||
"$type": "MizukakeCrypt",
|
||||
"Title": "そらいろの雫"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ pub trait Crypt: std::fmt::Debug {
|
||||
enum CryptType {
|
||||
NoCrypt,
|
||||
FateCrypt,
|
||||
MizukakeCrypt,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
@@ -77,6 +78,7 @@ impl Schema {
|
||||
match self.crypt {
|
||||
CryptType::NoCrypt => Box::new(NoCrypt::new()),
|
||||
CryptType::FateCrypt => Box::new(FateCrypt::new()),
|
||||
CryptType::MizukakeCrypt => Box::new(MizukakeCrypt::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,73 +141,90 @@ impl NoCrypt {
|
||||
|
||||
impl Crypt for NoCrypt {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FateCrypt {}
|
||||
|
||||
impl FateCrypt {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Crypt for FateCrypt {
|
||||
fn decrypt_supported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decrypt_seek_supported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decrypt<'a>(
|
||||
&self,
|
||||
_entry: &Xp3Entry,
|
||||
cur_seg: &Segment,
|
||||
stream: Box<dyn Read + 'a>,
|
||||
) -> Result<Box<dyn ReadDebug + 'a>> {
|
||||
Ok(Box::new(FateCryptReader::new(stream, cur_seg)))
|
||||
}
|
||||
|
||||
fn decrypt_with_seek<'a>(
|
||||
&self,
|
||||
_entry: &Xp3Entry,
|
||||
cur_seg: &Segment,
|
||||
stream: Box<dyn ReadSeek + 'a>,
|
||||
) -> Result<Box<dyn ReadSeek + 'a>> {
|
||||
Ok(Box::new(FateCryptReader::new(stream, cur_seg)))
|
||||
}
|
||||
}
|
||||
|
||||
struct FateCryptReader<R: Read> {
|
||||
inner: R,
|
||||
/// Start offset of the current xp3 entry.
|
||||
seg_start: u64,
|
||||
seg_size: u64,
|
||||
pos: u64,
|
||||
}
|
||||
|
||||
impl<T: Read> FateCryptReader<T> {
|
||||
pub fn new(inner: T, seg: &Segment) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
seg_start: seg.offset_in_file,
|
||||
seg_size: seg.original_size,
|
||||
pos: 0,
|
||||
macro_rules! seek_impl {
|
||||
($reader:ident<$t:ident>) => {
|
||||
impl<$t: Read + Seek> Seek for $reader<$t> {
|
||||
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
|
||||
let new_pos: i64 = match pos {
|
||||
SeekFrom::Start(offset) => offset as i64,
|
||||
SeekFrom::End(offset) => self.seg_size as i64 + offset,
|
||||
SeekFrom::Current(offset) => self.pos as i64 + offset,
|
||||
};
|
||||
let offset = new_pos - self.pos as i64;
|
||||
if offset != 0 {
|
||||
self.inner.seek(SeekFrom::Current(offset))?;
|
||||
self.pos = new_pos as u64;
|
||||
}
|
||||
Ok(self.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl<T: Read> std::fmt::Debug for FateCryptReader<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("FateCryptReader")
|
||||
.field("seg_start", &self.seg_start)
|
||||
.field("seg_size", &self.seg_size)
|
||||
.field("pos", &self.pos)
|
||||
.finish()
|
||||
}
|
||||
macro_rules! seek_reader_impl {
|
||||
($reader:ident<$t:ident>) => {
|
||||
#[derive(msg_tool_macro::MyDebug)]
|
||||
struct $reader<$t: Read> {
|
||||
#[skip_fmt]
|
||||
inner: $t,
|
||||
/// Start offset of the current xp3 entry.
|
||||
seg_start: u64,
|
||||
seg_size: u64,
|
||||
pos: u64,
|
||||
}
|
||||
impl<$t: Read> $reader<$t> {
|
||||
pub fn new(inner: $t, seg: &Segment) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
seg_start: seg.offset_in_file,
|
||||
seg_size: seg.original_size,
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
seek_impl!($reader<$t>);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! seek_crypt_impl {
|
||||
($crypt:ident, $reader:ident<$t:ident>) => {
|
||||
#[derive(Debug)]
|
||||
pub struct $crypt {}
|
||||
impl $crypt {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
impl Crypt for $crypt {
|
||||
fn decrypt_supported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn decrypt_seek_supported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn decrypt<'a>(
|
||||
&self,
|
||||
_entry: &Xp3Entry,
|
||||
cur_seg: &Segment,
|
||||
stream: Box<dyn Read + 'a>,
|
||||
) -> Result<Box<dyn ReadDebug + 'a>> {
|
||||
Ok(Box::new($reader::new(stream, cur_seg)))
|
||||
}
|
||||
fn decrypt_with_seek<'a>(
|
||||
&self,
|
||||
_entry: &Xp3Entry,
|
||||
cur_seg: &Segment,
|
||||
stream: Box<dyn ReadSeek + 'a>,
|
||||
) -> Result<Box<dyn ReadSeek + 'a>> {
|
||||
Ok(Box::new($reader::new(stream, cur_seg)))
|
||||
}
|
||||
}
|
||||
seek_reader_impl!($reader<$t>);
|
||||
};
|
||||
}
|
||||
|
||||
seek_crypt_impl!(FateCrypt, FateCryptReader<T>);
|
||||
|
||||
impl<R: Read> Read for FateCryptReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
const XOR1_OFFSET: u64 = 0x13;
|
||||
@@ -225,19 +244,26 @@ impl<R: Read> Read for FateCryptReader<R> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read + Seek> Seek for FateCryptReader<T> {
|
||||
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
|
||||
let new_pos: i64 = match pos {
|
||||
SeekFrom::Start(offset) => offset as i64,
|
||||
SeekFrom::End(offset) => self.seg_size as i64 + offset,
|
||||
SeekFrom::Current(offset) => self.pos as i64 + offset,
|
||||
};
|
||||
let offset = new_pos - self.pos as i64;
|
||||
if offset != 0 {
|
||||
self.inner.seek(SeekFrom::Current(offset))?;
|
||||
self.pos = new_pos as u64;
|
||||
seek_crypt_impl!(MizukakeCrypt, MizukakeCryptReader<T>);
|
||||
|
||||
impl<R: Read> Read for MizukakeCryptReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let readed = self.inner.read(buf)?;
|
||||
for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
|
||||
let tpos = self.seg_start + self.pos + i as u64;
|
||||
if tpos == 0x103 {
|
||||
*t = (*t).wrapping_sub(1);
|
||||
}
|
||||
*t ^= 0xb6;
|
||||
if tpos == 0x3F82 {
|
||||
*t ^= 1;
|
||||
}
|
||||
if tpos == 0x83 {
|
||||
*t ^= 3;
|
||||
}
|
||||
}
|
||||
Ok(self.pos)
|
||||
self.pos += readed as u64;
|
||||
Ok(readed)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user