feat: use unp4k_rs

This commit is contained in:
xkeyC
2025-12-04 15:28:56 +08:00
parent a0290cf28a
commit e3c3986379
16 changed files with 1952 additions and 276 deletions

View File

@@ -6,3 +6,4 @@ pub mod rs_process;
pub mod win32_api;
pub mod asar_api;
pub mod ort_api;
pub mod unp4k_api;

196
rust/src/api/unp4k_api.rs Normal file
View File

@@ -0,0 +1,196 @@
use anyhow::{anyhow, Result};
use flutter_rust_bridge::frb;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use unp4k::{P4kEntry, P4kFile};
/// P4K 文件项信息
#[frb(dart_metadata=("freezed"))]
pub struct P4kFileItem {
/// 文件名/路径
pub name: String,
/// 是否为目录
pub is_directory: bool,
/// 文件大小(字节)
pub size: u64,
/// 压缩后大小(字节)
pub compressed_size: u64,
}
// 全局 P4K 读取器实例(用于保持状态)
static GLOBAL_P4K_READER: once_cell::sync::Lazy<Arc<Mutex<Option<P4kFile>>>> =
once_cell::sync::Lazy::new(|| Arc::new(Mutex::new(None)));
static GLOBAL_P4K_FILES: once_cell::sync::Lazy<Arc<Mutex<HashMap<String, P4kEntry>>>> =
once_cell::sync::Lazy::new(|| Arc::new(Mutex::new(HashMap::new())));
/// 打开 P4K 文件
pub async fn p4k_open(p4k_path: String) -> Result<usize> {
let path = PathBuf::from(&p4k_path);
if !path.exists() {
return Err(anyhow!("P4K file not found: {}", p4k_path));
}
// 在后台线程执行阻塞操作
let (reader, file_count, files_map) = tokio::task::spawn_blocking(move || {
let reader = P4kFile::open(&path)?;
let entries = reader.entries();
let file_count = entries.len();
let mut files_map = HashMap::new();
for entry in entries {
// 将路径转换为 Windows 风格,以 \ 开头
let name = if entry.name.starts_with("\\") {
entry.name.clone()
} else {
format!("\\{}", entry.name.replace("/", "\\"))
};
files_map.insert(name, entry.clone());
}
Ok::<_, anyhow::Error>((reader, file_count, files_map))
})
.await??;
*GLOBAL_P4K_READER.lock().unwrap() = Some(reader);
*GLOBAL_P4K_FILES.lock().unwrap() = files_map;
Ok(file_count)
}
/// 获取所有文件列表
pub async fn p4k_get_all_files() -> Result<Vec<P4kFileItem>> {
let files = GLOBAL_P4K_FILES.lock().unwrap();
let mut result = Vec::with_capacity(files.len());
for (name, entry) in files.iter() {
result.push(P4kFileItem {
name: name.clone(),
is_directory: false,
size: entry.uncompressed_size,
compressed_size: entry.compressed_size,
});
}
Ok(result)
}
/// 获取指定目录下的文件列表
pub async fn p4k_get_files_in_directory(directory: String) -> Result<Vec<P4kFileItem>> {
let files = GLOBAL_P4K_FILES.lock().unwrap();
let mut result = Vec::new();
let mut dirs = std::collections::HashSet::new();
// 确保目录路径以 \ 开头和结尾
let dir_path = if !directory.starts_with("\\") {
format!("\\{}", directory)
} else {
directory.clone()
};
let dir_path = if !dir_path.ends_with("\\") {
format!("{}\\", dir_path)
} else {
dir_path
};
for (name, entry) in files.iter() {
if name.starts_with(&dir_path) {
let relative = &name[dir_path.len()..];
if let Some(slash_pos) = relative.find("\\") {
// 这是一个子目录
let subdir = &relative[..slash_pos];
if !dirs.contains(subdir) {
dirs.insert(subdir.to_string());
result.push(P4kFileItem {
name: format!("{}{}\\", dir_path, subdir),
is_directory: true,
size: 0,
compressed_size: 0,
});
}
} else {
// 这是一个文件
result.push(P4kFileItem {
name: name.clone(),
is_directory: false,
size: entry.uncompressed_size,
compressed_size: entry.compressed_size,
});
}
}
}
// 按目录优先,然后按名称排序
result.sort_by(|a, b| {
if a.is_directory && !b.is_directory {
std::cmp::Ordering::Less
} else if !a.is_directory && b.is_directory {
std::cmp::Ordering::Greater
} else {
a.name.cmp(&b.name)
}
});
Ok(result)
}
/// 提取文件到内存
pub async fn p4k_extract_to_memory(file_path: String) -> Result<Vec<u8>> {
// 规范化路径
let normalized_path = if file_path.starts_with("\\") {
file_path.clone()
} else {
format!("\\{}", file_path)
};
// 获取文件 entry 的克隆
let entry = {
let files = GLOBAL_P4K_FILES.lock().unwrap();
files
.get(&normalized_path)
.ok_or_else(|| anyhow!("File not found: {}", file_path))?
.clone()
};
// 在后台线程执行阻塞的提取操作
let data = tokio::task::spawn_blocking(move || {
let mut reader = GLOBAL_P4K_READER.lock().unwrap();
if reader.is_none() {
return Err(anyhow!("P4K reader not initialized"));
}
let data = reader.as_mut().unwrap().extract_entry(&entry)?;
Ok::<_, anyhow::Error>(data)
})
.await??;
Ok(data)
}
/// 提取文件到磁盘
pub async fn p4k_extract_to_disk(file_path: String, output_path: String) -> Result<()> {
let data = p4k_extract_to_memory(file_path).await?;
// 在后台线程执行阻塞的文件写入操作
tokio::task::spawn_blocking(move || {
let output = PathBuf::from(&output_path);
// 创建父目录
if let Some(parent) = output.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(output, data)?;
Ok::<_, anyhow::Error>(())
})
.await??;
Ok(())
}
/// 关闭 P4K 读取器
pub async fn p4k_close() -> Result<()> {
*GLOBAL_P4K_READER.lock().unwrap() = None;
GLOBAL_P4K_FILES.lock().unwrap().clear();
Ok(())
}

View File

@@ -37,7 +37,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
default_rust_auto_opaque = RustAutoOpaqueNom,
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -518970253;
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -737964996;
// Section: executor
@@ -291,6 +291,155 @@ fn wire__crate__api__ort_api__load_translation_model_impl(
},
)
}
fn wire__crate__api__unp4k_api__p4k_close_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "p4k_close",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
move |context| async move {
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
(move || async move {
let output_ok = crate::api::unp4k_api::p4k_close().await?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__unp4k_api__p4k_extract_to_disk_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
file_path: impl CstDecode<String>,
output_path: impl CstDecode<String>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "p4k_extract_to_disk",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_file_path = file_path.cst_decode();
let api_output_path = output_path.cst_decode();
move |context| async move {
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
(move || async move {
let output_ok = crate::api::unp4k_api::p4k_extract_to_disk(
api_file_path,
api_output_path,
)
.await?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__unp4k_api__p4k_extract_to_memory_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
file_path: impl CstDecode<String>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "p4k_extract_to_memory",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_file_path = file_path.cst_decode();
move |context| async move {
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
(move || async move {
let output_ok =
crate::api::unp4k_api::p4k_extract_to_memory(api_file_path).await?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__unp4k_api__p4k_get_all_files_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "p4k_get_all_files",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
move |context| async move {
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
(move || async move {
let output_ok = crate::api::unp4k_api::p4k_get_all_files().await?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__unp4k_api__p4k_get_files_in_directory_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
directory: impl CstDecode<String>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "p4k_get_files_in_directory",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_directory = directory.cst_decode();
move |context| async move {
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
(move || async move {
let output_ok =
crate::api::unp4k_api::p4k_get_files_in_directory(api_directory)
.await?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__unp4k_api__p4k_open_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
p4k_path: impl CstDecode<String>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "p4k_open",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_p4k_path = p4k_path.cst_decode();
move |context| async move {
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
(move || async move {
let output_ok = crate::api::unp4k_api::p4k_open(api_p4k_path).await?;
Ok(output_ok)
})()
.await,
)
}
},
)
}
fn wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
that: impl CstDecode<crate::api::asar_api::RsiLauncherAsarData>,
@@ -630,6 +779,12 @@ impl CstDecode<u8> for u8 {
self
}
}
impl CstDecode<usize> for usize {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> usize {
self
}
}
impl SseDecode for flutter_rust_bridge::for_generated::anyhow::Error {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -693,6 +848,20 @@ impl SseDecode for Vec<String> {
}
}
impl SseDecode for Vec<crate::api::unp4k_api::P4kFileItem> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut len_ = <i32>::sse_decode(deserializer);
let mut ans_ = vec![];
for idx_ in 0..len_ {
ans_.push(<crate::api::unp4k_api::P4kFileItem>::sse_decode(
deserializer,
));
}
return ans_;
}
}
impl SseDecode for Vec<u8> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -823,6 +992,22 @@ impl SseDecode for Option<Vec<u8>> {
}
}
impl SseDecode for crate::api::unp4k_api::P4kFileItem {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut var_name = <String>::sse_decode(deserializer);
let mut var_isDirectory = <bool>::sse_decode(deserializer);
let mut var_size = <u64>::sse_decode(deserializer);
let mut var_compressedSize = <u64>::sse_decode(deserializer);
return crate::api::unp4k_api::P4kFileItem {
name: var_name,
is_directory: var_isDirectory,
size: var_size,
compressed_size: var_compressedSize,
};
}
}
impl SseDecode for crate::api::win32_api::ProcessInfo {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -943,6 +1128,13 @@ impl SseDecode for () {
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {}
}
impl SseDecode for usize {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
deserializer.cursor.read_u64::<NativeEndian>().unwrap() as _
}
}
fn pde_ffi_dispatcher_primary_impl(
func_id: i32,
port: flutter_rust_bridge::for_generated::MessagePort,
@@ -1024,6 +1216,29 @@ impl flutter_rust_bridge::IntoIntoDart<crate::api::http_api::MyMethod>
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::api::unp4k_api::P4kFileItem {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[
self.name.into_into_dart().into_dart(),
self.is_directory.into_into_dart().into_dart(),
self.size.into_into_dart().into_dart(),
self.compressed_size.into_into_dart().into_dart(),
]
.into_dart()
}
}
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive
for crate::api::unp4k_api::P4kFileItem
{
}
impl flutter_rust_bridge::IntoIntoDart<crate::api::unp4k_api::P4kFileItem>
for crate::api::unp4k_api::P4kFileItem
{
fn into_into_dart(self) -> crate::api::unp4k_api::P4kFileItem {
self
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::api::win32_api::ProcessInfo {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[
@@ -1195,6 +1410,16 @@ impl SseEncode for Vec<String> {
}
}
impl SseEncode for Vec<crate::api::unp4k_api::P4kFileItem> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<i32>::sse_encode(self.len() as _, serializer);
for item in self {
<crate::api::unp4k_api::P4kFileItem>::sse_encode(item, serializer);
}
}
}
impl SseEncode for Vec<u8> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
@@ -1318,6 +1543,16 @@ impl SseEncode for Option<Vec<u8>> {
}
}
impl SseEncode for crate::api::unp4k_api::P4kFileItem {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<String>::sse_encode(self.name, serializer);
<bool>::sse_encode(self.is_directory, serializer);
<u64>::sse_encode(self.size, serializer);
<u64>::sse_encode(self.compressed_size, serializer);
}
}
impl SseEncode for crate::api::win32_api::ProcessInfo {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
@@ -1416,6 +1651,16 @@ impl SseEncode for () {
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {}
}
impl SseEncode for usize {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
serializer
.cursor
.write_u64::<NativeEndian>(self as _)
.unwrap();
}
}
#[cfg(not(target_family = "wasm"))]
mod io {
// This file is automatically generated, so please do not edit it.
@@ -1508,6 +1753,16 @@ mod io {
vec.into_iter().map(CstDecode::cst_decode).collect()
}
}
impl CstDecode<Vec<crate::api::unp4k_api::P4kFileItem>> for *mut wire_cst_list_p_4_k_file_item {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> Vec<crate::api::unp4k_api::P4kFileItem> {
let vec = unsafe {
let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self);
flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len)
};
vec.into_iter().map(CstDecode::cst_decode).collect()
}
}
impl CstDecode<Vec<u8>> for *mut wire_cst_list_prim_u_8_loose {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> Vec<u8> {
@@ -1546,6 +1801,17 @@ mod io {
vec.into_iter().map(CstDecode::cst_decode).collect()
}
}
impl CstDecode<crate::api::unp4k_api::P4kFileItem> for wire_cst_p_4_k_file_item {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::api::unp4k_api::P4kFileItem {
crate::api::unp4k_api::P4kFileItem {
name: self.name.cst_decode(),
is_directory: self.is_directory.cst_decode(),
size: self.size.cst_decode(),
compressed_size: self.compressed_size.cst_decode(),
}
}
}
impl CstDecode<crate::api::win32_api::ProcessInfo> for wire_cst_process_info {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::api::win32_api::ProcessInfo {
@@ -1596,6 +1862,21 @@ mod io {
}
}
}
impl NewWithNullPtr for wire_cst_p_4_k_file_item {
fn new_with_null_ptr() -> Self {
Self {
name: core::ptr::null_mut(),
is_directory: Default::default(),
size: Default::default(),
compressed_size: Default::default(),
}
}
}
impl Default for wire_cst_p_4_k_file_item {
fn default() -> Self {
Self::new_with_null_ptr()
}
}
impl NewWithNullPtr for wire_cst_process_info {
fn new_with_null_ptr() -> Self {
Self {
@@ -1764,6 +2045,51 @@ mod io {
)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_close(port_: i64) {
wire__crate__api__unp4k_api__p4k_close_impl(port_)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_extract_to_disk(
port_: i64,
file_path: *mut wire_cst_list_prim_u_8_strict,
output_path: *mut wire_cst_list_prim_u_8_strict,
) {
wire__crate__api__unp4k_api__p4k_extract_to_disk_impl(port_, file_path, output_path)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_extract_to_memory(
port_: i64,
file_path: *mut wire_cst_list_prim_u_8_strict,
) {
wire__crate__api__unp4k_api__p4k_extract_to_memory_impl(port_, file_path)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_get_all_files(
port_: i64,
) {
wire__crate__api__unp4k_api__p4k_get_all_files_impl(port_)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_get_files_in_directory(
port_: i64,
directory: *mut wire_cst_list_prim_u_8_strict,
) {
wire__crate__api__unp4k_api__p4k_get_files_in_directory_impl(port_, directory)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_open(
port_: i64,
p4k_path: *mut wire_cst_list_prim_u_8_strict,
) {
wire__crate__api__unp4k_api__p4k_open_impl(port_, p4k_path)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js(
port_: i64,
@@ -1884,6 +2210,20 @@ mod io {
flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_cst_new_list_p_4_k_file_item(
len: i32,
) -> *mut wire_cst_list_p_4_k_file_item {
let wrap = wire_cst_list_p_4_k_file_item {
ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(
<wire_cst_p_4_k_file_item>::new_with_null_ptr(),
len,
),
len,
};
flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_cst_new_list_prim_u_8_loose(
len: i32,
@@ -1942,6 +2282,12 @@ mod io {
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_list_p_4_k_file_item {
ptr: *mut wire_cst_p_4_k_file_item,
len: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_list_prim_u_8_loose {
ptr: *mut u8,
len: i32,
@@ -1966,6 +2312,14 @@ mod io {
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_p_4_k_file_item {
name: *mut wire_cst_list_prim_u_8_strict,
is_directory: bool,
size: u64,
compressed_size: u64,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_process_info {
pid: u32,
name: *mut wire_cst_list_prim_u_8_strict,