mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-06 12:58:45 +08:00
Add decode support for crx
This commit is contained in:
62
Cargo.lock
generated
62
Cargo.lock
generated
@@ -130,6 +130,17 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -570,6 +581,16 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json"
|
||||
version = "0.12.4"
|
||||
@@ -648,6 +669,7 @@ dependencies = [
|
||||
"url",
|
||||
"utf16string",
|
||||
"windows-sys",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -681,6 +703,12 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.16"
|
||||
@@ -843,6 +871,12 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
@@ -1149,3 +1183,31 @@ dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.15+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
@@ -27,9 +27,10 @@ sha1 = { version = "0.10", optional = true }
|
||||
unicode-segmentation = "1.12"
|
||||
url = { version = "2.5", optional = true }
|
||||
utf16string = "0.2"
|
||||
zstd = { version = "0.13", optional = true }
|
||||
|
||||
[features]
|
||||
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"]
|
||||
default = ["artemis", "artemis-arc", "bgi", "bgi-arc", "bgi-img", "cat-system", "cat-system-arc", "cat-system-img", "circus", "circus-img", "escude", "escude-arc", "hexen-haus", "kirikiri", "kirikiri-img", "will-plus", "yaneurao", "yaneurao-itufuru"]
|
||||
artemis = ["utils-escape"]
|
||||
artemis-arc = ["artemis", "msg_tool_macro/artemis-arc", "sha1"]
|
||||
bgi = []
|
||||
@@ -39,6 +40,7 @@ cat-system = ["fancy-regex", "flate2", "int-enum"]
|
||||
cat-system-arc = ["cat-system", "blowfish", "utils-crc32"]
|
||||
cat-system-img = ["cat-system", "flate2", "image", "utils-bit-stream"]
|
||||
circus = []
|
||||
circus-img = ["circus", "image", "flate2", "zstd"]
|
||||
escude = ["int-enum"]
|
||||
escude-arc = ["escude", "rand", "utils-bit-stream"]
|
||||
hexen-haus = ["memchr", "utils-str"]
|
||||
|
||||
@@ -74,7 +74,10 @@ pub fn struct_unpack_impl_for_num(item: TokenStream) -> TokenStream {
|
||||
/// * `fvec = <len>` attribute can be used to specify a fixed vector length for Vec<_> fields.
|
||||
/// * `pstring(<len_type>)` attribute can be used to specify a packed string length for String fields, where `<len_type>` can be `u8`, `u16`, `u32`, or `u64`.
|
||||
/// Length is read as a prefix before the string data.
|
||||
#[proc_macro_derive(StructPack, attributes(skip_pack, fstring, fstring_pad, fvec, pstring))]
|
||||
/// * `pvec(<len_type>)` attribute can be used to specify a packed vector length for Vec<_> fields, where `<len_type>` can be `u8`, `u16`, `u32`, or `u64`.
|
||||
/// Length is read as a prefix before the vector data.
|
||||
/// * `skip_pack_if(<expr>)` attribute can be used to skip packing a field if the expression evaluates to true. The expression must be a valid Rust expression that evaluates to a boolean.
|
||||
#[proc_macro_derive(StructPack, attributes(skip_pack, fstring, fstring_pad, fvec, pstring, pvec, skip_pack_if))]
|
||||
pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
|
||||
let a = syn::parse_macro_input!(input as PackStruct);
|
||||
match a {
|
||||
@@ -87,6 +90,9 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
|
||||
let mut fixed_vec: Option<usize> = None;
|
||||
let mut fstring_pad = 0u8; // Default padding byte
|
||||
let mut pstring_type: Option<syn::Ident> = None;
|
||||
let mut pvec_type: Option<syn::Ident> = None;
|
||||
let mut cur = None;
|
||||
let mut skip_if = None;
|
||||
for attr in &field.attrs {
|
||||
let path = attr.path();
|
||||
if path.is_ident("skip_pack") {
|
||||
@@ -132,6 +138,27 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}
|
||||
} else if path.is_ident("pvec") {
|
||||
if let syn::Meta::List(list) = &attr.meta {
|
||||
list.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("u8") {
|
||||
pvec_type = Some(syn::Ident::new("u8", meta.path.span()));
|
||||
} else if meta.path.is_ident("u16") {
|
||||
pvec_type = Some(syn::Ident::new("u16", meta.path.span()));
|
||||
} else if meta.path.is_ident("u32") {
|
||||
pvec_type = Some(syn::Ident::new("u32", meta.path.span()));
|
||||
} else if meta.path.is_ident("u64") {
|
||||
pvec_type = Some(syn::Ident::new("u64", meta.path.span()));
|
||||
} else {
|
||||
return Err(meta.error("Expected u8, u16, or u32 for pvec"));
|
||||
}
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}
|
||||
} else if path.is_ident("skip_pack_if") {
|
||||
if let syn::Meta::List(list) = &attr.meta {
|
||||
skip_if = Some(list.parse_args::<syn::Expr>().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
if skipped {
|
||||
@@ -150,7 +177,7 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
|
||||
if let Some(segment) = type_path.path.segments.last() {
|
||||
if segment.ident == "String" {
|
||||
if let Some(fixed_string) = fixed_string {
|
||||
return quote::quote! {
|
||||
cur = Some(quote::quote! {
|
||||
let s = encode_string(encoding, &self.#field_name, true)?;
|
||||
let mut slen = s.len();
|
||||
if slen > #fixed_string {
|
||||
@@ -164,35 +191,54 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
|
||||
for _ in slen..#fixed_string {
|
||||
writer.write_all(&[#fstring_pad])?;
|
||||
}
|
||||
};
|
||||
}
|
||||
if let Some(pstring_type) = pstring_type {
|
||||
});
|
||||
} else if let Some(pstring_type) = pstring_type {
|
||||
let write_fn = syn::Ident::new(format!("write_{}", pstring_type).as_str(), pstring_type.span());
|
||||
return quote::quote! {
|
||||
cur = Some(quote::quote! {
|
||||
let encoded = crate::utils::encoding::encode_string(encoding, &self.#field_name, true)?;
|
||||
writer.#write_fn(encoded.len() as #pstring_type)?;
|
||||
writer.write_all(&encoded)?;
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(segment) = type_path.path.segments.first() {
|
||||
if segment.ident == "Vec" {
|
||||
if let Some(fixed_vec) = fixed_vec {
|
||||
return quote::quote! {
|
||||
cur = Some(quote::quote! {
|
||||
if self.#field_name.len() != #fixed_vec {
|
||||
return Err(anyhow::anyhow!("Vector length was not equal to {}", #fixed_vec));
|
||||
}
|
||||
for item in &self.#field_name {
|
||||
item.pack(writer, big, encoding)?;
|
||||
}
|
||||
};
|
||||
});
|
||||
} else if let Some(pvec_type) = pvec_type {
|
||||
let write_fn = syn::Ident::new(format!("write_{}", pvec_type).as_str(), pvec_type.span());
|
||||
cur = Some(quote::quote! {
|
||||
let len = self.#field_name.len() as #pvec_type;
|
||||
writer.#write_fn(len)?;
|
||||
for item in &self.#field_name {
|
||||
item.pack(writer, big, encoding)?;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
quote::quote! {
|
||||
self.#field_name.pack(writer, big, encoding)?;
|
||||
let p = cur.unwrap_or_else(|| {
|
||||
quote::quote! {
|
||||
self.#field_name.pack(writer, big, encoding)?;
|
||||
}
|
||||
});
|
||||
if let Some(skip_if) = skip_if {
|
||||
quote::quote! {
|
||||
if !(#skip_if) {
|
||||
#p
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p
|
||||
}
|
||||
});
|
||||
let output = quote::quote! {
|
||||
@@ -226,6 +272,10 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
|
||||
let mut fixed_string: Option<usize> = None;
|
||||
let mut fixed_vec: Option<usize> = None;
|
||||
let mut fstring_pad = 0u8; // Default padding byte
|
||||
let mut pstring_type: Option<syn::Ident> = None;
|
||||
let mut pvec_type: Option<syn::Ident> = None;
|
||||
let mut cur = None;
|
||||
let mut skip_if = None;
|
||||
for attr in &field.attrs {
|
||||
let path = attr.path();
|
||||
if path.is_ident("skip_pack") {
|
||||
@@ -254,6 +304,44 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if path.is_ident("pstring") {
|
||||
if let syn::Meta::List(list) = &attr.meta {
|
||||
list.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("u8") {
|
||||
pstring_type = Some(syn::Ident::new("u8", meta.path.span()));
|
||||
} else if meta.path.is_ident("u16") {
|
||||
pstring_type = Some(syn::Ident::new("u16", meta.path.span()));
|
||||
} else if meta.path.is_ident("u32") {
|
||||
pstring_type = Some(syn::Ident::new("u32", meta.path.span()));
|
||||
} else if meta.path.is_ident("u64") {
|
||||
pstring_type = Some(syn::Ident::new("u64", meta.path.span()));
|
||||
} else {
|
||||
return Err(meta.error("Expected u8, u16, or u32 for pstring"));
|
||||
}
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}
|
||||
} else if path.is_ident("pvec") {
|
||||
if let syn::Meta::List(list) = &attr.meta {
|
||||
list.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("u8") {
|
||||
pvec_type = Some(syn::Ident::new("u8", meta.path.span()));
|
||||
} else if meta.path.is_ident("u16") {
|
||||
pvec_type = Some(syn::Ident::new("u16", meta.path.span()));
|
||||
} else if meta.path.is_ident("u32") {
|
||||
pvec_type = Some(syn::Ident::new("u32", meta.path.span()));
|
||||
} else if meta.path.is_ident("u64") {
|
||||
pvec_type = Some(syn::Ident::new("u64", meta.path.span()));
|
||||
} else {
|
||||
return Err(meta.error("Expected u8, u16, or u32 for pvec"));
|
||||
}
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}
|
||||
} else if path.is_ident("skip_pack_if") {
|
||||
if let syn::Meta::List(list) = &attr.meta {
|
||||
skip_if = Some(list.parse_args::<syn::Expr>().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
if skipped {
|
||||
@@ -273,7 +361,7 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
|
||||
if let Some(segment) = type_path.path.segments.last() {
|
||||
if segment.ident == "String" {
|
||||
if let Some(fixed_string) = fixed_string {
|
||||
return quote::quote! {
|
||||
cur = Some(quote::quote! {
|
||||
let s = encode_string(encoding, &#field_name, true)?;
|
||||
let mut slen = s.len();
|
||||
if slen > #fixed_string {
|
||||
@@ -287,27 +375,54 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
|
||||
for _ in slen..#fixed_string {
|
||||
writer.write_all(&[#fstring_pad])?;
|
||||
}
|
||||
};
|
||||
});
|
||||
} else if let Some(pstring_type) = pstring_type {
|
||||
let write_fn = syn::Ident::new(format!("write_{}", pstring_type).as_str(), pstring_type.span());
|
||||
cur = Some(quote::quote! {
|
||||
let encoded = crate::utils::encoding::encode_string(encoding, &#field_name, true)?;
|
||||
writer.#write_fn(encoded.len() as #pstring_type)?;
|
||||
writer.write_all(&encoded)?;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(segment) = type_path.path.segments.first() {
|
||||
if segment.ident == "Vec" {
|
||||
if let Some(fixed_vec) = fixed_vec {
|
||||
return quote::quote! {
|
||||
cur = Some(quote::quote! {
|
||||
if #field_name.len() != #fixed_vec {
|
||||
return Err(anyhow::anyhow!("Vector length was not equal to {}", #fixed_vec));
|
||||
}
|
||||
for item in &#field_name {
|
||||
item.pack(writer, big, encoding)?;
|
||||
}
|
||||
};
|
||||
});
|
||||
} else if let Some(pvec_type) = pvec_type {
|
||||
let write_fn = syn::Ident::new(format!("write_{}", pvec_type).as_str(), pvec_type.span());
|
||||
cur = Some(quote::quote! {
|
||||
let len = #field_name.len() as #pvec_type;
|
||||
writer.#write_fn(len)?;
|
||||
for item in &#field_name {
|
||||
item.pack(writer, big, encoding)?;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
quote::quote! {
|
||||
#field_name.pack(writer, big, encoding)?;
|
||||
let p = cur.unwrap_or_else(|| {
|
||||
quote::quote! {
|
||||
#field_name.pack(writer, big, encoding)?;
|
||||
}
|
||||
});
|
||||
if let Some(skip_if) = skip_if {
|
||||
quote::quote! {
|
||||
if !(#skip_if) {
|
||||
#p
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p
|
||||
}
|
||||
}).collect();
|
||||
let idents = if is_struct_like {
|
||||
@@ -351,7 +466,10 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
|
||||
/// * `fvec = <len>` attribute can be used to specify a fixed vector length for Vec<_> fields.
|
||||
/// * `pstring(<number_type>)` attribute can be used to specify a packed string length for String fields, where `<number_type>` can be `u8`, `u16`, `u32` or `u64`.
|
||||
/// length is read as a prefix before the string data.
|
||||
#[proc_macro_derive(StructUnpack, attributes(skip_unpack, fstring, fstring_no_trim, fvec, pstring))]
|
||||
/// * `pvec(<number_type>)` attribute can be used to specify a packed vector length for Vec<_> fields, where `<number_type>` can be `u8`, `u16`, `u32` or `u64`.
|
||||
/// length is read as a prefix before the vector data.
|
||||
/// * `skip_unpack_if(<expr>)` attribute can be used to skip unpacking a field if the expression evaluates to true. The expression must be a valid Rust expression that evaluates to a boolean.
|
||||
#[proc_macro_derive(StructUnpack, attributes(skip_unpack, fstring, fstring_no_trim, fvec, pstring, pvec, skip_unpack_if))]
|
||||
pub fn struct_unpack_derive(input: TokenStream) -> TokenStream {
|
||||
let sut = syn::parse_macro_input!(input as syn::ItemStruct);
|
||||
let name = sut.ident;
|
||||
@@ -364,6 +482,9 @@ pub fn struct_unpack_derive(input: TokenStream) -> TokenStream {
|
||||
let mut fstring_no_trim = false;
|
||||
let mut fixed_vec: Option<usize> = None;
|
||||
let mut pstring_type: Option<syn::Ident> = None;
|
||||
let mut pvec_type: Option<syn::Ident> = None;
|
||||
let mut cur = None;
|
||||
let mut skip_if: Option<syn::Expr> = None;
|
||||
for attr in &field.attrs {
|
||||
let path = attr.path();
|
||||
if path.is_ident("skip_unpack") {
|
||||
@@ -403,6 +524,27 @@ pub fn struct_unpack_derive(input: TokenStream) -> TokenStream {
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}
|
||||
} else if path.is_ident("pvec") {
|
||||
if let syn::Meta::List(list) = &attr.meta {
|
||||
list.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("u8") {
|
||||
pvec_type = Some(syn::Ident::new("u8", meta.path.span()));
|
||||
} else if meta.path.is_ident("u16") {
|
||||
pvec_type = Some(syn::Ident::new("u16", meta.path.span()));
|
||||
} else if meta.path.is_ident("u32") {
|
||||
pvec_type = Some(syn::Ident::new("u32", meta.path.span()));
|
||||
} else if meta.path.is_ident("u64") {
|
||||
pvec_type = Some(syn::Ident::new("u64", meta.path.span()));
|
||||
} else {
|
||||
return Err(meta.error("Expected u8, u16, or u32 for pvec"));
|
||||
}
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}
|
||||
} else if path.is_ident("skip_unpack_if") {
|
||||
if let syn::Meta::List(list) = &attr.meta {
|
||||
skip_if = Some(list.parse_args::<syn::Expr>().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
let field_name = match &field.ident {
|
||||
@@ -426,32 +568,51 @@ pub fn struct_unpack_derive(input: TokenStream) -> TokenStream {
|
||||
if segment.ident == "String" {
|
||||
if let Some(fixed_string) = fixed_string {
|
||||
let trim = syn::LitBool::new(!fstring_no_trim, field.span());
|
||||
return quote::quote! {
|
||||
cur = Some(quote::quote! {
|
||||
let #field_name = reader.read_fstring(#fixed_string, encoding, #trim)?;
|
||||
};
|
||||
}
|
||||
if let Some(pstring_type) = pstring_type {
|
||||
});
|
||||
} else if let Some(pstring_type) = pstring_type {
|
||||
let read_fn = syn::Ident::new(format!("read_{}", pstring_type).as_str(), pstring_type.span());
|
||||
return quote::quote! {
|
||||
cur = Some(quote::quote! {
|
||||
let len = reader.#read_fn()? as usize;
|
||||
let #field_name = reader.read_exact_vec(len)?;
|
||||
let #field_name = crate::utils::encoding::decode_to_string(encoding, &#field_name, true)?;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(segment) = type_path.path.segments.first() {
|
||||
if segment.ident == "Vec" {
|
||||
if let Some(fixed_vec) = fixed_vec {
|
||||
return quote::quote! {
|
||||
cur = Some(quote::quote! {
|
||||
let #field_name = reader.read_struct_vec(#fixed_vec, big, encoding)?;
|
||||
};
|
||||
});
|
||||
} else if let Some(pvec_type) = pvec_type {
|
||||
let read_fn = syn::Ident::new(format!("read_{}", pvec_type).as_str(), pvec_type.span());
|
||||
cur = Some(quote::quote! {
|
||||
let len = reader.#read_fn()? as usize;
|
||||
let #field_name = reader.read_struct_vec(len, big, encoding)?;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
quote::quote! {
|
||||
let #field_name = #field_type::unpack(&mut reader, big, encoding)?;
|
||||
let p = cur.unwrap_or_else(|| {
|
||||
quote::quote! {
|
||||
let #field_name = #field_type::unpack(&mut reader, big, encoding)?;
|
||||
}
|
||||
});
|
||||
if let Some(skip_if) = skip_if {
|
||||
quote::quote! {
|
||||
let #field_name = if !(#skip_if) {
|
||||
#p
|
||||
#field_name
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
}
|
||||
} else {
|
||||
p
|
||||
}
|
||||
}).collect();
|
||||
let fields = if is_tuple_struct {
|
||||
|
||||
@@ -922,6 +922,19 @@ impl<T: Write + Seek> WriteAt for T {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SeekExt {
|
||||
fn stream_length(&mut self) -> Result<u64>;
|
||||
}
|
||||
|
||||
impl<T: Seek> SeekExt for T{
|
||||
fn stream_length(&mut self) -> Result<u64> {
|
||||
let current_pos = self.stream_position()?;
|
||||
let length = self.seek(SeekFrom::End(0))?;
|
||||
self.seek(SeekFrom::Start(current_pos))?;
|
||||
Ok(length)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemReader {
|
||||
pub data: Vec<u8>,
|
||||
pub pos: usize,
|
||||
|
||||
391
src/scripts/circus/image/crx.rs
Normal file
391
src/scripts/circus/image/crx.rs
Normal file
@@ -0,0 +1,391 @@
|
||||
use crate::ext::io::*;
|
||||
use crate::scripts::base::*;
|
||||
use crate::types::*;
|
||||
use crate::utils::struct_pack::*;
|
||||
use anyhow::Result;
|
||||
use msg_tool_macro::*;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CrxImageBuilder {}
|
||||
|
||||
impl CrxImageBuilder {
|
||||
pub const fn new() -> Self {
|
||||
CrxImageBuilder {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScriptBuilder for CrxImageBuilder {
|
||||
fn default_encoding(&self) -> Encoding {
|
||||
Encoding::Cp932
|
||||
}
|
||||
|
||||
fn build_script(
|
||||
&self,
|
||||
data: Vec<u8>,
|
||||
_filename: &str,
|
||||
_encoding: Encoding,
|
||||
_archive_encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
) -> Result<Box<dyn Script>> {
|
||||
Ok(Box::new(CrxImage::new(MemReader::new(data), config)?))
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &'static [&'static str] {
|
||||
&["crx"]
|
||||
}
|
||||
|
||||
fn script_type(&self) -> &'static ScriptType {
|
||||
&ScriptType::CircusCrx
|
||||
}
|
||||
|
||||
fn is_image(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
|
||||
if buf_len >= 4 && buf.starts_with(b"CRXG") {
|
||||
return Some(255);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, StructPack, StructUnpack)]
|
||||
struct Clip {
|
||||
field_0: u32,
|
||||
clip_width: u16,
|
||||
clip_height: u16,
|
||||
field_8: u16,
|
||||
field_a: u16,
|
||||
width: u16,
|
||||
height: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, StructPack, StructUnpack)]
|
||||
struct Header {
|
||||
inner_x: u16,
|
||||
inner_y: u16,
|
||||
width: u16,
|
||||
height: u16,
|
||||
version: u16,
|
||||
flags: u16,
|
||||
bpp: u16,
|
||||
mode: u16,
|
||||
#[skip_pack_if(self.version != 3)]
|
||||
#[skip_unpack_if(version != 3)]
|
||||
#[pvec(u32)]
|
||||
clips: Vec<Clip>,
|
||||
}
|
||||
|
||||
pub struct CrxImage {
|
||||
header: Header,
|
||||
color_type: ImageColorType,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for CrxImage {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("CrxImage")
|
||||
.field("header", &self.header)
|
||||
.field("color_type", &self.color_type)
|
||||
.field("data_length", &self.data.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl CrxImage {
|
||||
pub fn new<T: Read + Seek>(data: T, _config: &ExtraConfig) -> Result<Self> {
|
||||
let mut reader = data;
|
||||
let mut magic = [0; 4];
|
||||
reader.read_exact(&mut magic)?;
|
||||
if magic != *b"CRXG" {
|
||||
return Err(anyhow::anyhow!("Invalid CRX image magic"));
|
||||
}
|
||||
let header: Header = reader.read_struct(false, Encoding::Utf8)?;
|
||||
if header.version < 2 || header.version > 3 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Unsupported CRX version: {}",
|
||||
header.version
|
||||
));
|
||||
}
|
||||
let color_type = if header.bpp == 0 {
|
||||
ImageColorType::Bgr
|
||||
} else if header.bpp == 1 {
|
||||
ImageColorType::Bgra
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Unsupported CRX bpp: {}", header.bpp));
|
||||
};
|
||||
let compressed_size = if (header.flags & 0x10) == 0 {
|
||||
let len = reader.stream_length()?;
|
||||
(len - reader.stream_position()?) as u32
|
||||
} else {
|
||||
reader.read_u32()?
|
||||
};
|
||||
let compressed_data = reader.read_exact_vec(compressed_size as usize)?;
|
||||
let uncompessed = if compressed_data.starts_with(&[0x28, 0xb5, 0x2f, 0xfd]) {
|
||||
let mut decoder = zstd::Decoder::new(MemReaderRef::new(&compressed_data))?;
|
||||
let mut decompressed_data = Vec::new();
|
||||
decoder.read_to_end(&mut decompressed_data)?;
|
||||
decompressed_data
|
||||
} else {
|
||||
let mut decompressed_data = Vec::new();
|
||||
flate2::read::ZlibDecoder::new(MemReaderRef::new(&compressed_data))
|
||||
.read_to_end(&mut decompressed_data)?;
|
||||
decompressed_data
|
||||
};
|
||||
Ok(CrxImage {
|
||||
header,
|
||||
color_type,
|
||||
data: uncompessed,
|
||||
})
|
||||
}
|
||||
|
||||
fn decode_row0(
|
||||
dst: &mut Vec<u8>,
|
||||
mut dst_p: usize,
|
||||
src: &[u8],
|
||||
mut src_p: usize,
|
||||
width: u16,
|
||||
pixel_size: u8,
|
||||
) -> Result<usize> {
|
||||
let mut prev_p = dst_p;
|
||||
for _ in 0..pixel_size {
|
||||
dst[dst_p] = src[src_p];
|
||||
dst_p += 1;
|
||||
src_p += 1;
|
||||
}
|
||||
let remaining = width - 1;
|
||||
for _ in 0..remaining {
|
||||
for _ in 0..pixel_size {
|
||||
dst[dst_p] = src[src_p].overflowing_add(dst[prev_p]).0;
|
||||
dst_p += 1;
|
||||
src_p += 1;
|
||||
prev_p += 1;
|
||||
}
|
||||
}
|
||||
Ok(src_p)
|
||||
}
|
||||
|
||||
fn decode_row1(
|
||||
dst: &mut Vec<u8>,
|
||||
mut dst_p: usize,
|
||||
src: &[u8],
|
||||
mut src_p: usize,
|
||||
width: u16,
|
||||
pixel_size: u8,
|
||||
mut prev_row_p: usize,
|
||||
) -> Result<usize> {
|
||||
for _ in 0..width {
|
||||
for _ in 0..pixel_size {
|
||||
dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
|
||||
dst_p += 1;
|
||||
src_p += 1;
|
||||
prev_row_p += 1;
|
||||
}
|
||||
}
|
||||
Ok(src_p)
|
||||
}
|
||||
|
||||
fn decode_row2(
|
||||
dst: &mut Vec<u8>,
|
||||
mut dst_p: usize,
|
||||
src: &[u8],
|
||||
mut src_p: usize,
|
||||
width: u16,
|
||||
pixel_size: u8,
|
||||
mut prev_row_p: usize,
|
||||
) -> Result<usize> {
|
||||
for _ in 0..pixel_size {
|
||||
dst[dst_p] = src[src_p];
|
||||
dst_p += 1;
|
||||
src_p += 1;
|
||||
}
|
||||
let remaining = width - 1;
|
||||
for _ in 0..remaining {
|
||||
for _ in 0..pixel_size {
|
||||
dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
|
||||
dst_p += 1;
|
||||
src_p += 1;
|
||||
prev_row_p += 1;
|
||||
}
|
||||
}
|
||||
Ok(src_p)
|
||||
}
|
||||
|
||||
fn decode_row3(
|
||||
dst: &mut Vec<u8>,
|
||||
mut dst_p: usize,
|
||||
src: &[u8],
|
||||
mut src_p: usize,
|
||||
width: u16,
|
||||
pixel_size: u8,
|
||||
mut prev_row_p: usize,
|
||||
) -> Result<usize> {
|
||||
let count = width - 1;
|
||||
prev_row_p += pixel_size as usize;
|
||||
for _ in 0..count {
|
||||
for _ in 0..pixel_size {
|
||||
dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
|
||||
dst_p += 1;
|
||||
src_p += 1;
|
||||
prev_row_p += 1;
|
||||
}
|
||||
}
|
||||
for _ in 0..pixel_size {
|
||||
dst[dst_p] = src[src_p];
|
||||
dst_p += 1;
|
||||
src_p += 1;
|
||||
}
|
||||
Ok(src_p)
|
||||
}
|
||||
|
||||
fn decode_row4(
|
||||
dst: &mut Vec<u8>,
|
||||
dst_p: usize,
|
||||
src: &[u8],
|
||||
mut src_p: usize,
|
||||
width: u16,
|
||||
pixel_size: u8,
|
||||
) -> Result<usize> {
|
||||
for offset in 0..pixel_size {
|
||||
let mut dst_c = dst_p + offset as usize;
|
||||
let mut remaining = width;
|
||||
let value = src[src_p];
|
||||
src_p += 1;
|
||||
dst[dst_c] = value;
|
||||
dst_c += pixel_size as usize;
|
||||
remaining -= 1;
|
||||
if remaining == 0 {
|
||||
continue;
|
||||
}
|
||||
if value == src[src_p] {
|
||||
src_p += 1;
|
||||
let count = src[src_p] as u16;
|
||||
src_p += 1;
|
||||
remaining -= count;
|
||||
for _ in 0..count {
|
||||
dst[dst_c] = value;
|
||||
dst_c += pixel_size as usize;
|
||||
}
|
||||
}
|
||||
while remaining > 0 {
|
||||
let value = src[src_p];
|
||||
src_p += 1;
|
||||
dst[dst_c] = value;
|
||||
dst_c += pixel_size as usize;
|
||||
remaining -= 1;
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
if value == src[src_p] {
|
||||
src_p += 1;
|
||||
let count = src[src_p] as u16;
|
||||
src_p += 1;
|
||||
remaining -= count;
|
||||
for _ in 0..count {
|
||||
dst[dst_c] = value;
|
||||
dst_c += pixel_size as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(src_p)
|
||||
}
|
||||
|
||||
fn decode_image(
|
||||
dst: &mut Vec<u8>,
|
||||
src: &[u8],
|
||||
width: u16,
|
||||
height: u16,
|
||||
pixel_size: u8,
|
||||
encode_type: &mut Vec<u8>,
|
||||
) -> Result<()> {
|
||||
let mut src_p = 0;
|
||||
let mut dst_p = 0;
|
||||
let mut prev_row_p = 0;
|
||||
for _ in 0..height {
|
||||
let data = src[src_p];
|
||||
encode_type.push(data);
|
||||
src_p += 1;
|
||||
match data {
|
||||
0 => {
|
||||
src_p = Self::decode_row0(dst, dst_p, src, src_p, width, pixel_size)?;
|
||||
}
|
||||
1 => {
|
||||
src_p =
|
||||
Self::decode_row1(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
|
||||
}
|
||||
2 => {
|
||||
src_p =
|
||||
Self::decode_row2(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
|
||||
}
|
||||
3 => {
|
||||
src_p =
|
||||
Self::decode_row3(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
|
||||
}
|
||||
4 => {
|
||||
src_p = Self::decode_row4(dst, dst_p, src, src_p, width, pixel_size)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!("Invalid row type: {}", data));
|
||||
}
|
||||
}
|
||||
prev_row_p = dst_p;
|
||||
dst_p += pixel_size as usize * width as usize;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Script for CrxImage {
|
||||
fn default_output_script_type(&self) -> OutputScriptType {
|
||||
OutputScriptType::Json
|
||||
}
|
||||
|
||||
fn default_format_type(&self) -> FormatOptions {
|
||||
FormatOptions::None
|
||||
}
|
||||
|
||||
fn is_image(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn export_image(&self) -> Result<ImageData> {
|
||||
let data_size = self.color_type.bpp(1) as usize * self.header.width as usize * self.header.height as usize;
|
||||
let mut data = vec![0; data_size];
|
||||
let mut encode_type = Vec::new();
|
||||
Self::decode_image(
|
||||
&mut data,
|
||||
&self.data,
|
||||
self.header.width,
|
||||
self.header.height,
|
||||
self.color_type.bpp(1) as u8,
|
||||
&mut encode_type,
|
||||
)?;
|
||||
if self.color_type.bpp(1) == 4 && self.header.mode != 1 {
|
||||
let alpha_flip = if self.header.mode == 2 {
|
||||
0
|
||||
} else {
|
||||
0xFF
|
||||
};
|
||||
for i in (0..data_size).step_by(4) {
|
||||
let a = data[i];
|
||||
let b = data[i + 1];
|
||||
let g = data[i + 2];
|
||||
let r = data[i + 3];
|
||||
data[i] = b;
|
||||
data[i + 1] = g;
|
||||
data[i + 2] = r;
|
||||
data[i + 3] = a ^ alpha_flip;
|
||||
}
|
||||
}
|
||||
Ok(ImageData {
|
||||
width: self.header.width as u32,
|
||||
height: self.header.height as u32,
|
||||
depth: 8,
|
||||
color_type: self.color_type,
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
1
src/scripts/circus/image/mod.rs
Normal file
1
src/scripts/circus/image/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod crx;
|
||||
@@ -1,2 +1,4 @@
|
||||
#[cfg(feature = "circus-img")]
|
||||
pub mod image;
|
||||
mod info;
|
||||
pub mod script;
|
||||
|
||||
@@ -80,6 +80,8 @@ lazy_static::lazy_static! {
|
||||
Box::new(artemis::asb::ArtemisAsbBuilder::new()),
|
||||
#[cfg(feature = "hexen-haus")]
|
||||
Box::new(hexen_haus::bin::BinScriptBuilder::new()),
|
||||
#[cfg(feature = "circus-img")]
|
||||
Box::new(circus::image::crx::CrxImageBuilder::new()),
|
||||
];
|
||||
pub static ref ALL_EXTS: Vec<String> =
|
||||
BUILDER.iter().flat_map(|b| b.extensions()).map(|s| s.to_string()).collect();
|
||||
|
||||
@@ -292,6 +292,9 @@ pub enum ScriptType {
|
||||
#[cfg(feature = "circus")]
|
||||
/// Circus MES script
|
||||
Circus,
|
||||
#[cfg(feature = "circus-img")]
|
||||
/// Circus CRX Image
|
||||
CircusCrx,
|
||||
#[cfg(feature = "escude-arc")]
|
||||
/// Escude bin archive
|
||||
EscudeArc,
|
||||
|
||||
Reference in New Issue
Block a user