diff --git a/Cargo.lock b/Cargo.lock index 3132050..f9312df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index c5dda3a..bbd8632 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/msg_tool_macro/src/lib.rs b/msg_tool_macro/src/lib.rs index 954fe0a..b7f1097 100644 --- a/msg_tool_macro/src/lib.rs +++ b/msg_tool_macro/src/lib.rs @@ -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: diff --git a/src/scripts/kirikiri/archive/xp3/crypt.json b/src/scripts/kirikiri/archive/xp3/crypt.json index b8b9172..3b57cfa 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt.json +++ b/src/scripts/kirikiri/archive/xp3/crypt.json @@ -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": "そらいろの雫" } } diff --git a/src/scripts/kirikiri/archive/xp3/crypt.rs b/src/scripts/kirikiri/archive/xp3/crypt.rs index bb84029..96cbd6c 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt.rs @@ -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, - ) -> Result> { - Ok(Box::new(FateCryptReader::new(stream, cur_seg))) - } - - fn decrypt_with_seek<'a>( - &self, - _entry: &Xp3Entry, - cur_seg: &Segment, - stream: Box, - ) -> Result> { - Ok(Box::new(FateCryptReader::new(stream, cur_seg))) - } -} - -struct FateCryptReader { - inner: R, - /// Start offset of the current xp3 entry. - seg_start: u64, - seg_size: u64, - pos: u64, -} - -impl FateCryptReader { - 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 { + 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 std::fmt::Debug for FateCryptReader { - 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, + ) -> Result> { + Ok(Box::new($reader::new(stream, cur_seg))) + } + fn decrypt_with_seek<'a>( + &self, + _entry: &Xp3Entry, + cur_seg: &Segment, + stream: Box, + ) -> Result> { + Ok(Box::new($reader::new(stream, cur_seg))) + } + } + seek_reader_impl!($reader<$t>); + }; +} + +seek_crypt_impl!(FateCrypt, FateCryptReader); + impl Read for FateCryptReader { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { const XOR1_OFFSET: u64 = 0x13; @@ -225,19 +244,26 @@ impl Read for FateCryptReader { } } -impl Seek for FateCryptReader { - fn seek(&mut self, pos: SeekFrom) -> std::io::Result { - 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); + +impl Read for MizukakeCryptReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + 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) } }