fix(PR): Multiple fixes by Kaladin
- Auto Open Engine refactor and implemented the engine stop button in the notification card. - Media downloader stabilization fixes including Batch download fix. - Implemented a new log filtering menu. - Cargo Dependency updated to latest stable versions. - Minor bug fixes.
This commit is contained in:
36
native/rust/Cargo.lock
generated
36
native/rust/Cargo.lock
generated
@@ -107,9 +107,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.9.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
@@ -563,9 +563,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
@@ -821,18 +821,28 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -948,22 +958,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.44"
|
||||
version = "0.3.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
|
||||
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.6"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
|
||||
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
|
||||
@@ -8,12 +8,17 @@ pub fn native_config() -> NativeConfig {
|
||||
NATIVE_CONFIG.lock().unwrap().as_ref().expect("NativeConfig not loaded").clone()
|
||||
}
|
||||
|
||||
/// Native configuration structure mirrored from 'NativeConfig.kt'.
|
||||
///
|
||||
/// CRITICAL: Fields must maintain 1:1 parity with the Kotlin implementation.
|
||||
/// Mismatches in field names, types, or order will result in a JNI SIGABRT.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct NativeConfig {
|
||||
pub disable_bitmoji: bool,
|
||||
pub disable_metrics: bool,
|
||||
pub valdi_hooks: bool,
|
||||
pub custom_emoji_font_path: Option<String>,
|
||||
pub debug_font_redirect: bool,
|
||||
}
|
||||
|
||||
impl NativeConfig {
|
||||
@@ -41,6 +46,7 @@ impl NativeConfig {
|
||||
disable_metrics: get_boolean!("disableMetrics"),
|
||||
valdi_hooks: get_boolean!("valdiHooks"),
|
||||
custom_emoji_font_path: get_string!("customEmojiFontPath"),
|
||||
debug_font_redirect: get_boolean!("debugFontRedirect"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use std::{cell::Cell, ffi::{CStr, CString}};
|
||||
|
||||
use nix::libc::{self, c_uint};
|
||||
|
||||
use crate::{config, def_hook, dobby_hook_sym};
|
||||
|
||||
thread_local! {
|
||||
@@ -23,6 +21,8 @@ fn should_redirect_font(pathname: &str) -> bool {
|
||||
file_name.contains("emoji")
|
||||
|| file_name == "noto_color_emoji.ttf"
|
||||
|| file_name == "samsungcoloremoji.ttf"
|
||||
|| file_name == "coloremojifont.ttf"
|
||||
|| file_name == "coloros_color_emoji.ttf"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ fn open_custom_font_fd(flags: i32, mode: c_uint) -> Option<i32> {
|
||||
Ok(c_font_path) => {
|
||||
let fd = FONT_REDIRECT_IN_PROGRESS.with(|guard| {
|
||||
let was_active = guard.replace(true);
|
||||
let fd = unsafe { libc::openat(libc::AT_FDCWD, c_font_path.as_ptr() as *const u8, flags, mode) };
|
||||
let fd = unsafe { libc::openat(libc::AT_FDCWD, c_font_path.as_ptr() as *const libc::c_char, flags, mode) };
|
||||
guard.set(was_active);
|
||||
fd
|
||||
});
|
||||
@@ -41,6 +41,9 @@ fn open_custom_font_fd(flags: i32, mode: c_uint) -> Option<i32> {
|
||||
debug!("redirected emoji font open to {}", font_path);
|
||||
Some(fd)
|
||||
} else {
|
||||
if config::native_config().debug_font_redirect {
|
||||
panic!("Failed to open custom emoji font: {}", font_path);
|
||||
}
|
||||
debug!("failed to open custom emoji font path (fd={}): {}", fd, font_path);
|
||||
None
|
||||
}
|
||||
@@ -61,7 +64,7 @@ def_hook!(
|
||||
}
|
||||
|
||||
if !path.is_null() {
|
||||
if let Ok(pathname) = CStr::from_ptr(path).to_str() {
|
||||
if let Ok(pathname) = unsafe { CStr::from_ptr(path as *const libc::c_char) }.to_str() {
|
||||
if should_redirect_font(pathname) {
|
||||
if let Some(fd) = open_custom_font_fd(flags, mode) {
|
||||
return fd;
|
||||
@@ -74,10 +77,33 @@ def_hook!(
|
||||
}
|
||||
);
|
||||
|
||||
def_hook!(
|
||||
openat_hook,
|
||||
i32,
|
||||
|dirfd: i32, path: *const u8, flags: i32, mode: c_uint| {
|
||||
if FONT_REDIRECT_IN_PROGRESS.with(|guard| guard.get()) {
|
||||
return openat_hook_original.unwrap()(dirfd, path, flags, mode);
|
||||
}
|
||||
|
||||
if !path.is_null() {
|
||||
if let Ok(pathname) = unsafe { CStr::from_ptr(path as *const libc::c_char) }.to_str() {
|
||||
if should_redirect_font(pathname) {
|
||||
if let Some(fd) = open_custom_font_fd(flags, mode) {
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
openat_hook_original.unwrap()(dirfd, path, flags, mode)
|
||||
}
|
||||
);
|
||||
|
||||
pub fn init() {
|
||||
if config::native_config().custom_emoji_font_path.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
dobby_hook_sym!("libc.so", "open", open_hook);
|
||||
dobby_hook_sym!("libc.so", "openat", openat_hook);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@ pub struct ValdiModule {
|
||||
|
||||
impl ValdiModule {
|
||||
pub fn parse(buffer: Vec<u8>) -> Result<ValdiModule, Error> {
|
||||
if buffer.len() < 8 {
|
||||
return Err(Error::new(std::io::ErrorKind::InvalidData, "Buffer too small"));
|
||||
}
|
||||
let mut offset = 0;
|
||||
let magic = u32::from_be_bytes([buffer[offset], buffer[offset + 1], buffer[offset + 2], buffer[offset + 3]]);
|
||||
|
||||
@@ -62,14 +65,13 @@ impl ValdiModule {
|
||||
}
|
||||
|
||||
fn read_u32(buffer: &Vec<u8>, offset: &mut usize) -> Result<(u32, bool), Error> {
|
||||
let b1 = buffer[*offset] as u32;
|
||||
let b2 = buffer[*offset + 1] as u32;
|
||||
let b3 = buffer[*offset + 2] as u32;
|
||||
let b4 = (buffer[*offset + 3] & 0x7f) as u32;
|
||||
let has_padding = (buffer[*offset + 3] & 0x80) != 0;
|
||||
let bytes = [buffer[*offset], buffer[*offset + 1], buffer[*offset + 2], buffer[*offset + 3]];
|
||||
let value = u32::from_be_bytes(bytes);
|
||||
let has_padding = (value & 0x80000000) != 0;
|
||||
let tag_size = value & 0x7FFFFFFF;
|
||||
|
||||
*offset += 4;
|
||||
Ok((b1 | (b2 << 8) | (b3 << 16) | (b4 << 24), has_padding))
|
||||
Ok((tag_size, has_padding))
|
||||
}
|
||||
|
||||
let (tag_size, has_padding) = read_u32(&buffer, &mut offset)?;
|
||||
@@ -98,10 +100,8 @@ impl ValdiModule {
|
||||
let mut tag_buffer = Vec::new();
|
||||
|
||||
fn write_u32(buffer: &mut Vec<u8>, value: u32, has_padding: bool) {
|
||||
buffer.push(value as u8);
|
||||
buffer.push(((value >> 8) & 0xff) as u8);
|
||||
buffer.push(((value >> 16) & 0xff) as u8);
|
||||
buffer.push(((value >> 24) & 0x7f) as u8 | if has_padding { 0x80 } else { 0x00 });
|
||||
let encoded_value = (value & 0x7FFFFFFF) | if has_padding { 0x80000000 } else { 0 };
|
||||
buffer.extend_from_slice(&encoded_value.to_be_bytes());
|
||||
}
|
||||
|
||||
fn write_tag(buffer: &mut Vec<u8>, tag: ModuleTag) {
|
||||
@@ -125,7 +125,7 @@ impl ValdiModule {
|
||||
let mut buffer = Vec::new();
|
||||
|
||||
buffer.extend_from_slice(&[0x33, 0xc6, 0, 1]);
|
||||
buffer.extend_from_slice(&(tag_buffer.len() as u32).to_le_bytes());
|
||||
buffer.extend_from_slice(&(tag_buffer.len() as u32).to_be_bytes());
|
||||
buffer.extend(tag_buffer);
|
||||
|
||||
buffer
|
||||
|
||||
@@ -16,7 +16,10 @@ def_hook!(
|
||||
if let Some(buffer) = AASSET_MAP.lock().unwrap().get(&(arg0 as usize)) {
|
||||
return buffer.len() as i32;
|
||||
}
|
||||
aasset_get_length_original.unwrap()(arg0)
|
||||
if let Some(original) = aasset_get_length_original {
|
||||
return original(arg0);
|
||||
}
|
||||
0
|
||||
}
|
||||
);
|
||||
|
||||
@@ -27,7 +30,10 @@ def_hook!(
|
||||
if let Some(buffer) = AASSET_MAP.lock().unwrap().get(&(arg0 as usize)) {
|
||||
return buffer.as_ptr() as *const c_void;
|
||||
}
|
||||
aasset_get_buffer_original.unwrap()(arg0)
|
||||
if let Some(original) = aasset_get_buffer_original {
|
||||
return original(arg0);
|
||||
}
|
||||
std::ptr::null()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -35,53 +41,89 @@ def_hook!(
|
||||
aasset_manager_open,
|
||||
*mut c_void,
|
||||
|arg0: *mut c_void, arg1: *const u8, arg2: i32| {
|
||||
let handle = aasset_manager_open_original.unwrap()(arg0, arg1, arg2);
|
||||
let original_fn = match aasset_manager_open_original {
|
||||
Some(f) => f,
|
||||
None => return std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
let path = std::ffi::CStr::from_ptr(arg1).to_str().unwrap_or_default();
|
||||
if !handle.is_null() && path.starts_with("bridge_observables") {
|
||||
let asset_buffer = aasset_get_buffer_original.unwrap()(handle);
|
||||
let asset_length = aasset_get_length_original.unwrap()(handle);
|
||||
debug!("asset buffer: {:p}, length: {}", asset_buffer, asset_length);
|
||||
let handle = original_fn(arg0, arg1, arg2);
|
||||
if handle.is_null() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
let loader_data = LOADER_DATA.lock().unwrap().clone().expect("No loader data");
|
||||
let path_cstr = unsafe { std::ffi::CStr::from_ptr(arg1 as *const std::os::raw::c_char) };
|
||||
let path = path_cstr.to_str().unwrap_or_default();
|
||||
|
||||
// Only target compressed Valdi bridge observables
|
||||
if path.ends_with(".zst") && path.contains("bridge_observables") {
|
||||
let get_buffer_fn = match aasset_get_buffer_original {
|
||||
Some(f) => f,
|
||||
None => return handle,
|
||||
};
|
||||
let get_length_fn = match aasset_get_length_original {
|
||||
Some(f) => f,
|
||||
None => return handle,
|
||||
};
|
||||
|
||||
let archive_buffer: Vec<u8> = std::slice::from_raw_parts(asset_buffer as *const u8, asset_length as usize).to_vec();
|
||||
let decompressed = zstd::stream::decode_all(&archive_buffer[..]).expect("Failed to decompress valdi archive");
|
||||
let mut valdi_module = ValdiModule::parse(decompressed).expect("Failed to parse valdi module");
|
||||
|
||||
let mut tags = valdi_module.get_tags();
|
||||
let mut new_tags = Vec::new();
|
||||
|
||||
for (tag1, _) in tags.iter_mut() {
|
||||
let name = tag1.to_string().unwrap_or_default();
|
||||
if !name.ends_with("src/utils/converter.js") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let old_file_name = name.split_once(".").unwrap().0.to_owned() + rand::random::<u32>().to_string().as_str();
|
||||
tag1.set_buffer((old_file_name.to_owned() + ".js").as_bytes().to_vec());
|
||||
let original_module_path = path.split_once(".").unwrap().0.to_owned() + "/" + &old_file_name;
|
||||
|
||||
let hooked_module = format!("{};module.exports = require(\"{}\");", loader_data, original_module_path);
|
||||
|
||||
new_tags.push(
|
||||
(
|
||||
ModuleTag::new(true, name.as_bytes().to_vec()),
|
||||
ModuleTag::new(true, hooked_module.as_bytes().to_vec())
|
||||
)
|
||||
);
|
||||
|
||||
debug!("Valdi loader injected in {}", name);
|
||||
break;
|
||||
let asset_buffer = get_buffer_fn(handle);
|
||||
let asset_length = get_length_fn(handle);
|
||||
|
||||
if asset_buffer.is_null() || asset_length <= 0 {
|
||||
return handle;
|
||||
}
|
||||
|
||||
tags.extend(new_tags);
|
||||
valdi_module.set_tags(tags);
|
||||
let loader_data = match LOADER_DATA.lock().unwrap().clone() {
|
||||
Some(data) => data,
|
||||
None => {
|
||||
warn!("Valdi loader data not yet initialized for {}", path);
|
||||
return handle;
|
||||
}
|
||||
};
|
||||
|
||||
let compressed = valdi_module.to_bytes();
|
||||
let compressed = zstd::stream::encode_all(&compressed[..], 3).expect("Failed to compress");
|
||||
let archive_buffer: Vec<u8> = unsafe {
|
||||
std::slice::from_raw_parts(asset_buffer as *const u8, asset_length as usize).to_vec()
|
||||
};
|
||||
|
||||
let decompressed = match zstd::stream::decode_all(&archive_buffer[..]) {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!("Failed to decompress Valdi archive {}: {}", path, e);
|
||||
return handle;
|
||||
}
|
||||
};
|
||||
|
||||
AASSET_MAP.lock().unwrap().insert(handle as usize, compressed);
|
||||
let valdi_module = match ValdiModule::parse(decompressed) {
|
||||
Ok(module) => module,
|
||||
Err(e) => {
|
||||
error!("Failed to parse Valdi module {}: {}", path, e);
|
||||
return handle;
|
||||
}
|
||||
};
|
||||
|
||||
let mut tags = valdi_module.get_tags();
|
||||
let mut found = false;
|
||||
|
||||
for (tag1, tag2) in tags.iter_mut() {
|
||||
let name = tag1.to_string().unwrap_or_default();
|
||||
if name.ends_with("src/utils/converter.js") {
|
||||
let mut hooked_content = loader_data.as_bytes().to_vec();
|
||||
hooked_content.extend_from_slice(tag2.get_buffer());
|
||||
*tag2 = ModuleTag::new(true, hooked_content);
|
||||
found = true;
|
||||
debug!("Valdi loader prepended to {}", name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
let compressed = valdi_module.to_bytes();
|
||||
match zstd::stream::encode_all(&compressed[..], 3) {
|
||||
Ok(compressed_data) => {
|
||||
AASSET_MAP.lock().unwrap().insert(handle as usize, compressed_data);
|
||||
},
|
||||
Err(e) => error!("Failed to re-compress Valdi module: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
handle
|
||||
}
|
||||
@@ -89,16 +131,19 @@ def_hook!(
|
||||
|
||||
def_hook!(
|
||||
aasset_close,
|
||||
c_void,
|
||||
(),
|
||||
|handle: *mut c_void| {
|
||||
AASSET_MAP.lock().unwrap().remove(&(handle as usize));
|
||||
aasset_close_original.unwrap()(handle)
|
||||
if let Some(original) = aasset_close_original {
|
||||
original(handle);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
pub fn set_valdi_loader(mut env: JNIEnv, _: *mut c_void, code: JString) {
|
||||
let new_code = get_jni_string(&mut env, code).expect("Failed to get loader code");
|
||||
LOADER_DATA.lock().unwrap().replace(new_code);
|
||||
if let Ok(new_code) = get_jni_string(&mut env, code) {
|
||||
LOADER_DATA.lock().unwrap().replace(new_code);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
package me.eternal.purrfectsnap.nativelib
|
||||
|
||||
/**
|
||||
* Configuration schema for the native layer.
|
||||
*
|
||||
* CRITICAL: This class MUST maintain 1:1 field parity with 'native/rust/src/config.rs'.
|
||||
* Any modification to field names, types, or order without a corresponding change
|
||||
* in the Rust implementation will cause a JNI SIGABRT (crash on launch).
|
||||
*/
|
||||
data class NativeConfig(
|
||||
@JvmField
|
||||
val disableBitmoji: Boolean = false,
|
||||
@@ -9,4 +16,6 @@ data class NativeConfig(
|
||||
val valdiHooks: Boolean = false,
|
||||
@JvmField
|
||||
val customEmojiFontPath: String? = null,
|
||||
@JvmField
|
||||
val debugFontRedirect: Boolean = false,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user