实现成就数据读取

This commit is contained in:
BTMuli
2025-12-01 20:05:03 +08:00
committed by 目棃
parent ccb4730c82
commit aca47f822b
4 changed files with 943 additions and 614 deletions

View File

@@ -5,20 +5,27 @@ pub mod inject;
pub mod proto;
use inject::{call_yaemain, create_named_pipe, find_module_base, inject_dll, spawn_process};
use prost::encoding::{decode_key, WireType};
use prost::Message;
use proto::{parse_achi_list, AchievementInfo};
use serde_json::Value;
use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::io::{Read, Write};
use std::os::windows::io::{FromRawHandle, OwnedHandle, RawHandle};
use std::sync::Arc;
use tauri::{AppHandle, Manager};
use tauri::{AppHandle, Emitter, Manager};
use windows_sys::Win32::Storage::FileSystem::ReadFile;
use windows_sys::Win32::System::Pipes::ConnectNamedPipe;
// 读取配置值
fn read_rva(key: &str) -> i32 {
let path = format!("nativeConfig.methodRva.chinese.{}", key);
read_conf(&path)
}
// 读取配置文件
fn read_conf(path: &str) -> i32 {
// 编译时嵌入 JSON 文件值都是32位整数
let data = include_str!("../../lib/conf.json");
@@ -35,9 +42,6 @@ fn read_conf(path: &str) -> i32 {
current.as_i64().unwrap_or(0) as i32
}
use prost::encoding::{decode_key, WireType};
use std::collections::HashMap;
pub fn parse_achievement_data(bytes: &[u8]) -> Vec<HashMap<u32, u32>> {
let mut cursor = std::io::Cursor::new(bytes);
let mut data = Vec::new();
@@ -155,13 +159,25 @@ pub fn call_yae_dll(app_handle: AppHandle, game_path: String) -> () {
println!("收到命令: {}", cmd[0]);
match cmd[0] {
0x01 => {
println!("AchievementNotify");
// 读取剩余数据
match read_u32_le(&mut file) {
Ok(len) => match read_exact_vec(&mut file, len as usize) {
Ok(data) => println!("长度: {}", len),
Err(e) => println!("读取数据失败: {:?}", e),
},
Ok(len) => {
// 再读数据
match read_exact_vec(&mut file, len as usize) {
Ok(data) => {
println!("长度: {}", len);
// 解码成 AchievementInfo
match parse_achi_list(&data) {
Ok(list) => {
println!("解码成功,成就列表长度: {}", list.len());
let json = serde_json::to_string_pretty(&list).unwrap();
app_handle.emit("yae_achi_list", json);
}
Err(e) => println!("解析失败: {:?}", e),
}
}
Err(e) => println!("读取数据失败: {:?}", e),
}
}
Err(e) => println!("读取长度失败: {:?}", e),
}
}

View File

@@ -1,10 +1,15 @@
//! Yae 成就信息的 Protobuf 定义
//! @since Beta v0.9.0
use prost::encoding::{decode_key, WireType};
use prost::DecodeError;
use prost::Message;
use serde::Serialize;
use std::collections::HashMap;
use std::io::Cursor;
use std::io::Read;
#[derive(Clone, PartialEq, Message)]
#[derive(Clone, PartialEq, Message, Serialize)]
pub struct AchievementProtoFieldInfo {
#[prost(uint32, tag = "1")]
pub id: u32,
@@ -22,7 +27,7 @@ pub struct AchievementProtoFieldInfo {
pub finish_timestamp: u32,
}
#[derive(Clone, PartialEq, Message)]
#[derive(Clone, PartialEq, Message, Serialize)]
pub struct AchievementItem {
#[prost(uint32, tag = "1")]
pub pre: u32,
@@ -37,7 +42,7 @@ pub struct AchievementItem {
pub description: String,
}
#[derive(Clone, PartialEq, Message)]
#[derive(Clone, PartialEq, Message, Serialize)]
pub struct MethodRvaConfig {
#[prost(uint32, tag = "1")]
pub do_cmd: u32,
@@ -70,7 +75,7 @@ pub struct MethodRvaConfig {
pub decompress: u32,
}
#[derive(Clone, PartialEq, Message)]
#[derive(Clone, PartialEq, Message, Serialize)]
pub struct NativeLibConfig {
#[prost(uint32, tag = "1")]
pub store_cmd_id: u32,
@@ -82,7 +87,7 @@ pub struct NativeLibConfig {
pub method_rva: HashMap<u32, MethodRvaConfig>,
}
#[derive(Clone, PartialEq, Message)]
#[derive(Clone, PartialEq, Message, Serialize)]
pub struct AchievementInfo {
#[prost(string, tag = "1")]
pub version: String,
@@ -99,3 +104,56 @@ pub struct AchievementInfo {
#[prost(message, tag = "5")]
pub native_config: Option<NativeLibConfig>,
}
#[derive(Debug, Default, Serialize)]
pub struct AchievementEntry {
pub id: u32,
pub total_progress: u32,
pub current_progress: u32,
pub finish_timestamp: u32,
pub status: u32, // 数值类型
}
pub fn parse_achi_list(bytes: &[u8]) -> Result<Vec<AchievementEntry>, DecodeError> {
let mut cursor = Cursor::new(bytes);
let mut dicts: Vec<HashMap<u32, u32>> = Vec::new();
while let Ok((_, wire_type)) = decode_key(&mut cursor) {
if wire_type == WireType::LengthDelimited {
let len = prost::encoding::decode_varint(&mut cursor)? as usize;
let mut buf = vec![0u8; len];
if cursor.read_exact(&mut buf).is_err() {
continue;
}
let mut inner = Cursor::new(&buf);
let mut dict = HashMap::new();
while let Ok((tag, wire_type)) = decode_key(&mut inner) {
if wire_type != WireType::Varint {
break;
}
let value = prost::encoding::decode_varint(&mut inner)? as u32;
dict.insert(tag, value);
}
if !dict.is_empty() {
println!("Raw dict: {:?}", dict);
dicts.push(dict);
}
}
}
let achievements = dicts
.into_iter()
.map(|d| AchievementEntry {
id: d.get(&15).copied().unwrap_or(0),
status: d.get(&11).copied().unwrap_or(0),
total_progress: d.get(&8).copied().unwrap_or(0),
current_progress: d.get(&13).copied().unwrap_or(0),
finish_timestamp: d.get(&7).copied().unwrap_or(0),
})
.collect();
Ok(achievements)
}