mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-02-11 17:50:23 +00:00
feat: 增加基础的游戏进程监听
This commit is contained in:
495
lib/common/helper/game_log_analyzer.dart
Normal file
495
lib/common/helper/game_log_analyzer.dart
Normal file
@@ -0,0 +1,495 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
|
||||
/// 日志分析结果数据类
|
||||
class LogAnalyzeLineData {
|
||||
final String type;
|
||||
final String title;
|
||||
final String? data;
|
||||
final String? dateTime;
|
||||
final String? tag; // 统计标签,用于定位日志(如 "game_start"),不依赖本地化
|
||||
|
||||
// 格式化后的字段
|
||||
final String? victimId; // 受害者ID (actor_death)
|
||||
final String? killerId; // 击杀者ID (actor_death)
|
||||
final String? location; // 位置信息 (request_location_inventory)
|
||||
final String? playerName; // 玩家名称 (player_login)
|
||||
|
||||
const LogAnalyzeLineData({
|
||||
required this.type,
|
||||
required this.title,
|
||||
this.data,
|
||||
this.dateTime,
|
||||
this.tag,
|
||||
this.victimId,
|
||||
this.killerId,
|
||||
this.location,
|
||||
this.playerName,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LogAnalyzeLineData(type: $type, title: $title, data: $data, dateTime: $dateTime)';
|
||||
}
|
||||
}
|
||||
|
||||
/// 日志分析统计数据
|
||||
class LogAnalyzeStatistics {
|
||||
final String playerName;
|
||||
final int killCount;
|
||||
final int deathCount;
|
||||
final int selfKillCount;
|
||||
final int vehicleDestructionCount;
|
||||
final int vehicleDestructionCountHard;
|
||||
final DateTime? gameStartTime;
|
||||
final int gameCrashLineNumber;
|
||||
final String? latestLocation; // 最新位置信息(全量查找)
|
||||
|
||||
const LogAnalyzeStatistics({
|
||||
required this.playerName,
|
||||
required this.killCount,
|
||||
required this.deathCount,
|
||||
required this.selfKillCount,
|
||||
required this.vehicleDestructionCount,
|
||||
required this.vehicleDestructionCountHard,
|
||||
this.gameStartTime,
|
||||
required this.gameCrashLineNumber,
|
||||
this.latestLocation,
|
||||
});
|
||||
}
|
||||
|
||||
/// 游戏日志分析器
|
||||
class GameLogAnalyzer {
|
||||
static const String unknownValue = "<Unknown>";
|
||||
|
||||
// 正则表达式定义
|
||||
static final _baseRegExp = RegExp(r'\[Notice\]\s+<([^>]+)>');
|
||||
static final _gameLoadingRegExp = RegExp(
|
||||
r'<[^>]+>\s+Loading screen for\s+(\w+)\s+:\s+SC_Frontend closed after\s+(\d+\.\d+)\s+seconds',
|
||||
);
|
||||
static final _logDateTimeRegExp = RegExp(r'<(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)>');
|
||||
static final DateFormat _dateTimeFormatter = DateFormat('yyyy-MM-dd HH:mm:ss:SSS');
|
||||
|
||||
// 致命碰撞解析
|
||||
static final _fatalCollisionPatterns = {
|
||||
'zone': RegExp(r'\[Part:[^\]]*?Zone:\s*([^,\]]+)'),
|
||||
'player_pilot': RegExp(r'PlayerPilot:\s*(\d)'),
|
||||
'hit_entity': RegExp(r'hitting entity:\s*(\w+)'),
|
||||
'hit_entity_vehicle': RegExp(r'hitting entity:[^\[]*\[Zone:\s*([^\s-]+)'),
|
||||
'distance': RegExp(r'Distance:\s*([\d.]+)'),
|
||||
};
|
||||
|
||||
// 载具损毁解析
|
||||
static final _vehicleDestructionPattern = RegExp(
|
||||
r"Vehicle\s+'([^']+)'.*?" // 载具型号
|
||||
r"in zone\s+'([^']+)'.*?" // Zone
|
||||
r"destroy level \d+ to (\d+).*?" // 损毁等级
|
||||
r"caused by\s+'([^']+)'", // 责任方
|
||||
);
|
||||
|
||||
// 角色死亡解析
|
||||
static final _actorDeathPattern = RegExp(
|
||||
r"CActor::Kill: '([^']+)'.*?" // 受害者ID
|
||||
r"in zone '([^']+)'.*?" // 死亡位置区域
|
||||
r"killed by '([^']+)'.*?" // 击杀者ID
|
||||
r"with damage type '([^']+)'", // 伤害类型
|
||||
);
|
||||
|
||||
// 角色名称解析
|
||||
static final _characterNamePattern = RegExp(r"name\s+([^-]+)");
|
||||
|
||||
// 本地库存请求解析
|
||||
static final _requestLocationInventoryPattern = RegExp(r"Player\[([^\]]+)\].*?Location\[([^\]]+)\]");
|
||||
|
||||
/// 分析整个日志文件
|
||||
///
|
||||
/// [logFile] 日志文件
|
||||
/// [startTime] 开始时间,如果提供则只统计此时间之后的数据
|
||||
/// 返回日志分析结果列表和统计数据
|
||||
static Future<(List<LogAnalyzeLineData>, LogAnalyzeStatistics)> analyzeLogFile(
|
||||
File logFile, {
|
||||
DateTime? startTime,
|
||||
}) async {
|
||||
if (!(await logFile.exists())) {
|
||||
return (
|
||||
[LogAnalyzeLineData(type: "error", title: S.current.log_analyzer_no_log_file)],
|
||||
LogAnalyzeStatistics(
|
||||
playerName: "",
|
||||
killCount: 0,
|
||||
deathCount: 0,
|
||||
selfKillCount: 0,
|
||||
vehicleDestructionCount: 0,
|
||||
vehicleDestructionCountHard: 0,
|
||||
gameCrashLineNumber: -1,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final logLines = utf8.decode((await logFile.readAsBytes()), allowMalformed: true).split("\n");
|
||||
return _analyzeLogLines(logLines, startTime: startTime);
|
||||
}
|
||||
|
||||
/// 分析日志行列表
|
||||
///
|
||||
/// [logLines] 日志行列表
|
||||
/// [startTime] 开始时间,如果提供则只影响计数统计,不影响 gameStartTime 和位置的全量查找
|
||||
/// 返回日志分析结果列表和统计数据
|
||||
static (List<LogAnalyzeLineData>, LogAnalyzeStatistics) _analyzeLogLines(
|
||||
List<String> logLines, {
|
||||
DateTime? startTime,
|
||||
}) {
|
||||
final results = <LogAnalyzeLineData>[];
|
||||
String playerName = "";
|
||||
int killCount = 0;
|
||||
int deathCount = 0;
|
||||
int selfKillCount = 0;
|
||||
int vehicleDestructionCount = 0;
|
||||
int vehicleDestructionCountHard = 0;
|
||||
DateTime? gameStartTime; // 全量查找,不受 startTime 影响
|
||||
String? latestLocation; // 全量查找最新位置
|
||||
int gameCrashLineNumber = -1;
|
||||
bool shouldCount = startTime == null; // 只影响计数
|
||||
|
||||
for (var i = 0; i < logLines.length; i++) {
|
||||
final line = logLines[i];
|
||||
if (line.isEmpty) continue;
|
||||
|
||||
// 如果设置了 startTime,检查当前行时间
|
||||
if (startTime != null && !shouldCount) {
|
||||
final lineTime = _getLogLineDateTime(line);
|
||||
if (lineTime != null && lineTime.isAfter(startTime)) {
|
||||
shouldCount = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理游戏开始(全量查找第一次出现)
|
||||
if (gameStartTime == null) {
|
||||
gameStartTime = _getLogLineDateTime(line);
|
||||
if (gameStartTime != null) {
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "info",
|
||||
title: S.current.log_analyzer_game_start,
|
||||
tag: "game_start", // 使用 tag 标识,不依赖本地化
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 游戏加载时间
|
||||
final gameLoading = _parseGameLoading(line);
|
||||
if (gameLoading != null) {
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "info",
|
||||
title: S.current.log_analyzer_game_loading,
|
||||
data: S.current.log_analyzer_mode_loading_time(gameLoading.$1, gameLoading.$2),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 基础事件解析
|
||||
final baseEvent = _parseBaseEvent(line);
|
||||
if (baseEvent != null) {
|
||||
LogAnalyzeLineData? data;
|
||||
switch (baseEvent) {
|
||||
case "AccountLoginCharacterStatus_Character":
|
||||
data = _parseCharacterName(line);
|
||||
if (data != null && data.playerName != null) {
|
||||
playerName = data.playerName!; // 全量更新玩家名称
|
||||
}
|
||||
break;
|
||||
case "FatalCollision":
|
||||
data = _parseFatalCollision(line);
|
||||
break;
|
||||
case "Vehicle Destruction":
|
||||
data = _parseVehicleDestruction(line, playerName, shouldCount, (isHard) {
|
||||
if (isHard) {
|
||||
vehicleDestructionCountHard++;
|
||||
} else {
|
||||
vehicleDestructionCount++;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "Actor Death":
|
||||
data = _parseActorDeath(line, playerName, shouldCount, (isKill, isDeath, isSelfKill) {
|
||||
if (isSelfKill) {
|
||||
selfKillCount++;
|
||||
} else {
|
||||
if (isKill) killCount++;
|
||||
if (isDeath) deathCount++;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "RequestLocationInventory":
|
||||
data = _parseRequestLocationInventory(line);
|
||||
if (data != null && data.location != null) {
|
||||
latestLocation = data.location; // 全量更新最新位置
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (data != null) {
|
||||
results.add(data);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 游戏关闭
|
||||
if (line.contains("[CIG] CCIGBroker::FastShutdown")) {
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "info",
|
||||
title: S.current.log_analyzer_game_close,
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 游戏崩溃
|
||||
if (line.contains("Cloud Imperium Games public crash handler")) {
|
||||
gameCrashLineNumber = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理崩溃信息
|
||||
if (gameCrashLineNumber > 0) {
|
||||
final lastLineDateTime = gameStartTime != null
|
||||
? _getLogLineDateTime(logLines.lastWhere((e) => e.startsWith("<20")))
|
||||
: null;
|
||||
final crashInfo = logLines.sublist(gameCrashLineNumber);
|
||||
final info = SCLoggerHelper.getGameRunningLogInfo(crashInfo);
|
||||
crashInfo.add(S.current.log_analyzer_one_click_diagnosis_header);
|
||||
if (info != null) {
|
||||
crashInfo.add(info.key);
|
||||
if (info.value.isNotEmpty) {
|
||||
crashInfo.add(S.current.log_analyzer_details_info(info.value));
|
||||
}
|
||||
} else {
|
||||
crashInfo.add(S.current.log_analyzer_no_crash_detected);
|
||||
}
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "game_crash",
|
||||
title: S.current.log_analyzer_game_crash,
|
||||
data: crashInfo.join("\n"),
|
||||
dateTime: lastLineDateTime != null ? _dateTimeFormatter.format(lastLineDateTime) : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 添加统计信息
|
||||
if (killCount > 0 || deathCount > 0) {
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "statistics",
|
||||
title: S.current.log_analyzer_kill_summary,
|
||||
data: S.current.log_analyzer_kill_death_suicide_count(
|
||||
killCount,
|
||||
deathCount,
|
||||
selfKillCount,
|
||||
vehicleDestructionCount,
|
||||
vehicleDestructionCountHard,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 统计游戏时长
|
||||
if (gameStartTime != null) {
|
||||
final lastLineDateTime = _getLogLineDateTime(logLines.lastWhere((e) => e.startsWith("<20"), orElse: () => ""));
|
||||
if (lastLineDateTime != null) {
|
||||
final duration = lastLineDateTime.difference(gameStartTime);
|
||||
results.add(
|
||||
LogAnalyzeLineData(
|
||||
type: "statistics",
|
||||
title: S.current.log_analyzer_play_time,
|
||||
data: S.current.log_analyzer_play_time_format(
|
||||
duration.inHours,
|
||||
duration.inMinutes.remainder(60),
|
||||
duration.inSeconds.remainder(60),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final statistics = LogAnalyzeStatistics(
|
||||
playerName: playerName,
|
||||
killCount: killCount,
|
||||
deathCount: deathCount,
|
||||
selfKillCount: selfKillCount,
|
||||
vehicleDestructionCount: vehicleDestructionCount,
|
||||
vehicleDestructionCountHard: vehicleDestructionCountHard,
|
||||
gameStartTime: gameStartTime,
|
||||
gameCrashLineNumber: gameCrashLineNumber,
|
||||
latestLocation: latestLocation,
|
||||
);
|
||||
|
||||
return (results, statistics);
|
||||
}
|
||||
|
||||
// ==================== 解析辅助方法 ====================
|
||||
|
||||
static String? _parseBaseEvent(String line) {
|
||||
final match = _baseRegExp.firstMatch(line);
|
||||
return match?.group(1);
|
||||
}
|
||||
|
||||
static (String, String)? _parseGameLoading(String line) {
|
||||
final match = _gameLoadingRegExp.firstMatch(line);
|
||||
if (match != null) {
|
||||
return (match.group(1) ?? "-", match.group(2) ?? "-");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static DateTime? _getLogLineDateTime(String line) {
|
||||
final match = _logDateTimeRegExp.firstMatch(line);
|
||||
if (match != null) {
|
||||
final dateTimeString = match.group(1);
|
||||
if (dateTimeString != null) {
|
||||
return DateTime.parse(dateTimeString).toLocal();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? _getLogLineDateTimeString(String line) {
|
||||
final dateTime = _getLogLineDateTime(line);
|
||||
if (dateTime != null) {
|
||||
return _dateTimeFormatter.format(dateTime);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? _safeExtract(RegExp pattern, String line) => pattern.firstMatch(line)?.group(1)?.trim();
|
||||
|
||||
static LogAnalyzeLineData? _parseFatalCollision(String line) {
|
||||
final zone = _safeExtract(_fatalCollisionPatterns['zone']!, line) ?? unknownValue;
|
||||
final playerPilot = (_safeExtract(_fatalCollisionPatterns['player_pilot']!, line) ?? '0') == '1';
|
||||
final hitEntity = _safeExtract(_fatalCollisionPatterns['hit_entity']!, line) ?? unknownValue;
|
||||
final hitEntityVehicle = _safeExtract(_fatalCollisionPatterns['hit_entity_vehicle']!, line) ?? unknownValue;
|
||||
final distance = double.tryParse(_safeExtract(_fatalCollisionPatterns['distance']!, line) ?? '') ?? 0.0;
|
||||
|
||||
return LogAnalyzeLineData(
|
||||
type: "fatal_collision",
|
||||
title: S.current.log_analyzer_filter_fatal_collision,
|
||||
data: S.current.log_analyzer_collision_details(
|
||||
zone,
|
||||
playerPilot ? '✅' : '❌',
|
||||
hitEntity,
|
||||
hitEntityVehicle,
|
||||
distance.toStringAsFixed(2),
|
||||
),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
);
|
||||
}
|
||||
|
||||
static LogAnalyzeLineData? _parseVehicleDestruction(
|
||||
String line,
|
||||
String playerName,
|
||||
bool shouldCount,
|
||||
void Function(bool isHard) onDestruction,
|
||||
) {
|
||||
final match = _vehicleDestructionPattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
final vehicleModel = match.group(1) ?? unknownValue;
|
||||
final zone = match.group(2) ?? unknownValue;
|
||||
final destructionLevel = int.tryParse(match.group(3) ?? '') ?? 0;
|
||||
final causedBy = match.group(4) ?? unknownValue;
|
||||
|
||||
final destructionLevelMap = {1: S.current.log_analyzer_soft_death, 2: S.current.log_analyzer_disintegration};
|
||||
|
||||
if (shouldCount && causedBy.trim() == playerName) {
|
||||
onDestruction(destructionLevel == 2);
|
||||
}
|
||||
|
||||
return LogAnalyzeLineData(
|
||||
type: "vehicle_destruction",
|
||||
title: S.current.log_analyzer_filter_vehicle_damaged,
|
||||
data: S.current.log_analyzer_vehicle_damage_details(
|
||||
vehicleModel,
|
||||
zone,
|
||||
destructionLevel.toString(),
|
||||
destructionLevelMap[destructionLevel] ?? unknownValue,
|
||||
causedBy,
|
||||
),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static LogAnalyzeLineData? _parseActorDeath(
|
||||
String line,
|
||||
String playerName,
|
||||
bool shouldCount,
|
||||
void Function(bool isKill, bool isDeath, bool isSelfKill) onDeath,
|
||||
) {
|
||||
final match = _actorDeathPattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
final victimId = match.group(1) ?? unknownValue;
|
||||
final zone = match.group(2) ?? unknownValue;
|
||||
final killerId = match.group(3) ?? unknownValue;
|
||||
final damageType = match.group(4) ?? unknownValue;
|
||||
|
||||
if (shouldCount) {
|
||||
if (victimId.trim() == killerId.trim()) {
|
||||
onDeath(false, false, true); // 自杀
|
||||
} else {
|
||||
final isDeath = victimId.trim() == playerName;
|
||||
final isKill = killerId.trim() == playerName;
|
||||
onDeath(isKill, isDeath, false);
|
||||
}
|
||||
}
|
||||
|
||||
return LogAnalyzeLineData(
|
||||
type: "actor_death",
|
||||
title: S.current.log_analyzer_filter_character_death,
|
||||
data: S.current.log_analyzer_death_details(victimId, damageType, killerId, zone),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
victimId: victimId, // 格式化字段
|
||||
killerId: killerId, // 格式化字段
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static LogAnalyzeLineData? _parseCharacterName(String line) {
|
||||
final match = _characterNamePattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
final characterName = match.group(1)?.trim() ?? unknownValue;
|
||||
return LogAnalyzeLineData(
|
||||
type: "player_login",
|
||||
title: S.current.log_analyzer_player_login(characterName),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
playerName: characterName, // 格式化字段
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static LogAnalyzeLineData? _parseRequestLocationInventory(String line) {
|
||||
final match = _requestLocationInventoryPattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
final playerId = match.group(1) ?? unknownValue;
|
||||
final location = match.group(2) ?? unknownValue;
|
||||
|
||||
return LogAnalyzeLineData(
|
||||
type: "request_location_inventory",
|
||||
title: S.current.log_analyzer_view_local_inventory,
|
||||
data: S.current.log_analyzer_player_location(playerId, location),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
location: location, // 格式化字段
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,9 @@
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `get_process_path`
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
|
||||
|
||||
Future<void> sendNotify({
|
||||
String? summary,
|
||||
String? body,
|
||||
@@ -22,3 +25,37 @@ Future<bool> setForegroundWindow({required String windowName}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiWin32ApiSetForegroundWindow(windowName: windowName);
|
||||
|
||||
Future<int> getProcessPidByName({required String processName}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiWin32ApiGetProcessPidByName(processName: processName);
|
||||
|
||||
Future<List<ProcessInfo>> getProcessListByName({required String processName}) =>
|
||||
RustLib.instance.api.crateApiWin32ApiGetProcessListByName(
|
||||
processName: processName,
|
||||
);
|
||||
|
||||
class ProcessInfo {
|
||||
final int pid;
|
||||
final String name;
|
||||
final String path;
|
||||
|
||||
const ProcessInfo({
|
||||
required this.pid,
|
||||
required this.name,
|
||||
required this.path,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode => pid.hashCode ^ name.hashCode ^ path.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ProcessInfo &&
|
||||
runtimeType == other.runtimeType &&
|
||||
pid == other.pid &&
|
||||
name == other.name &&
|
||||
path == other.path;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
String get codegenVersion => '2.11.1';
|
||||
|
||||
@override
|
||||
int get rustContentHash => -706588047;
|
||||
int get rustContentHash => 1227557070;
|
||||
|
||||
static const kDefaultExternalLibraryLoaderConfig =
|
||||
ExternalLibraryLoaderConfig(
|
||||
@@ -95,6 +95,14 @@ abstract class RustLibApi extends BaseApi {
|
||||
bool? withCustomDns,
|
||||
});
|
||||
|
||||
Future<List<ProcessInfo>> crateApiWin32ApiGetProcessListByName({
|
||||
required String processName,
|
||||
});
|
||||
|
||||
Future<int> crateApiWin32ApiGetProcessPidByName({
|
||||
required String processName,
|
||||
});
|
||||
|
||||
Future<RsiLauncherAsarData> crateApiAsarApiGetRsiLauncherAsarData({
|
||||
required String asarPath,
|
||||
});
|
||||
@@ -280,6 +288,66 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<List<ProcessInfo>> crateApiWin32ApiGetProcessListByName({
|
||||
required String processName,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(processName);
|
||||
return wire.wire__crate__api__win32_api__get_process_list_by_name(
|
||||
port_,
|
||||
arg0,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_list_process_info,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiWin32ApiGetProcessListByNameConstMeta,
|
||||
argValues: [processName],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiWin32ApiGetProcessListByNameConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "get_process_list_by_name",
|
||||
argNames: ["processName"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<int> crateApiWin32ApiGetProcessPidByName({
|
||||
required String processName,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(processName);
|
||||
return wire.wire__crate__api__win32_api__get_process_pid_by_name(
|
||||
port_,
|
||||
arg0,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_i_32,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiWin32ApiGetProcessPidByNameConstMeta,
|
||||
argValues: [processName],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiWin32ApiGetProcessPidByNameConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "get_process_pid_by_name",
|
||||
argNames: ["processName"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<RsiLauncherAsarData> crateApiAsarApiGetRsiLauncherAsarData({
|
||||
required String asarPath,
|
||||
@@ -722,6 +790,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw as Uint8List;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<ProcessInfo> dco_decode_list_process_info(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return (raw as List<dynamic>).map(dco_decode_process_info).toList();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<(String, String)> dco_decode_list_record_string_string(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -770,6 +844,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw == null ? null : dco_decode_list_prim_u_8_strict(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
ProcessInfo dco_decode_process_info(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 3)
|
||||
throw Exception('unexpected arr length: expect 3 but see ${arr.length}');
|
||||
return ProcessInfo(
|
||||
pid: dco_decode_u_32(arr[0]),
|
||||
name: dco_decode_String(arr[1]),
|
||||
path: dco_decode_String(arr[2]),
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
(String, String) dco_decode_record_string_string(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -949,6 +1036,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return deserializer.buffer.getUint8List(len_);
|
||||
}
|
||||
|
||||
@protected
|
||||
List<ProcessInfo> sse_decode_list_process_info(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
var len_ = sse_decode_i_32(deserializer);
|
||||
var ans_ = <ProcessInfo>[];
|
||||
for (var idx_ = 0; idx_ < len_; ++idx_) {
|
||||
ans_.add(sse_decode_process_info(deserializer));
|
||||
}
|
||||
return ans_;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<(String, String)> sse_decode_list_record_string_string(
|
||||
SseDeserializer deserializer,
|
||||
@@ -1034,6 +1133,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
ProcessInfo sse_decode_process_info(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var var_pid = sse_decode_u_32(deserializer);
|
||||
var var_name = sse_decode_String(deserializer);
|
||||
var var_path = sse_decode_String(deserializer);
|
||||
return ProcessInfo(pid: var_pid, name: var_name, path: var_path);
|
||||
}
|
||||
|
||||
@protected
|
||||
(String, String) sse_decode_record_string_string(
|
||||
SseDeserializer deserializer,
|
||||
@@ -1295,6 +1403,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
serializer.buffer.putUint8List(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_process_info(
|
||||
List<ProcessInfo> self,
|
||||
SseSerializer serializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_i_32(self.length, serializer);
|
||||
for (final item in self) {
|
||||
sse_encode_process_info(item, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_record_string_string(
|
||||
List<(String, String)> self,
|
||||
@@ -1378,6 +1498,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_process_info(ProcessInfo self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_u_32(self.pid, serializer);
|
||||
sse_encode_String(self.name, serializer);
|
||||
sse_encode_String(self.path, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_record_string_string(
|
||||
(String, String) self,
|
||||
|
||||
@@ -62,6 +62,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<ProcessInfo> dco_decode_list_process_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<(String, String)> dco_decode_list_record_string_string(dynamic raw);
|
||||
|
||||
@@ -86,6 +89,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw);
|
||||
|
||||
@protected
|
||||
ProcessInfo dco_decode_process_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
(String, String) dco_decode_record_string_string(dynamic raw);
|
||||
|
||||
@@ -159,6 +165,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<ProcessInfo> sse_decode_list_process_info(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<(String, String)> sse_decode_list_record_string_string(
|
||||
SseDeserializer deserializer,
|
||||
@@ -187,6 +196,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
ProcessInfo sse_decode_process_info(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
(String, String) sse_decode_record_string_string(
|
||||
SseDeserializer deserializer,
|
||||
@@ -315,6 +327,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
return ans;
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_process_info> cst_encode_list_process_info(
|
||||
List<ProcessInfo> raw,
|
||||
) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
final ans = wire.cst_new_list_process_info(raw.length);
|
||||
for (var i = 0; i < raw.length; ++i) {
|
||||
cst_api_fill_to_wire_process_info(raw[i], ans.ref.ptr[i]);
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_record_string_string>
|
||||
cst_encode_list_record_string_string(List<(String, String)> raw) {
|
||||
@@ -374,6 +398,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
cst_api_fill_to_wire_rsi_launcher_asar_data(apiObj, wireObj.ref);
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_process_info(
|
||||
ProcessInfo apiObj,
|
||||
wire_cst_process_info wireObj,
|
||||
) {
|
||||
wireObj.pid = cst_encode_u_32(apiObj.pid);
|
||||
wireObj.name = cst_encode_String(apiObj.name);
|
||||
wireObj.path = cst_encode_String(apiObj.path);
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_record_string_string(
|
||||
(String, String) apiObj,
|
||||
@@ -499,6 +533,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_process_info(
|
||||
List<ProcessInfo> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_record_string_string(
|
||||
List<(String, String)> self,
|
||||
@@ -532,6 +572,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_process_info(ProcessInfo self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_record_string_string(
|
||||
(String, String) self,
|
||||
@@ -719,6 +762,60 @@ class RustLibWire implements BaseWire {
|
||||
)
|
||||
>();
|
||||
|
||||
void wire__crate__api__win32_api__get_process_list_by_name(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> process_name,
|
||||
) {
|
||||
return _wire__crate__api__win32_api__get_process_list_by_name(
|
||||
port_,
|
||||
process_name,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__win32_api__get_process_list_by_namePtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_process_list_by_name',
|
||||
);
|
||||
late final _wire__crate__api__win32_api__get_process_list_by_name =
|
||||
_wire__crate__api__win32_api__get_process_list_by_namePtr
|
||||
.asFunction<
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)
|
||||
>();
|
||||
|
||||
void wire__crate__api__win32_api__get_process_pid_by_name(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> process_name,
|
||||
) {
|
||||
return _wire__crate__api__win32_api__get_process_pid_by_name(
|
||||
port_,
|
||||
process_name,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__win32_api__get_process_pid_by_namePtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_process_pid_by_name',
|
||||
);
|
||||
late final _wire__crate__api__win32_api__get_process_pid_by_name =
|
||||
_wire__crate__api__win32_api__get_process_pid_by_namePtr
|
||||
.asFunction<
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)
|
||||
>();
|
||||
|
||||
void wire__crate__api__asar_api__get_rsi_launcher_asar_data(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> asar_path,
|
||||
@@ -1144,6 +1241,19 @@ class RustLibWire implements BaseWire {
|
||||
late final _cst_new_list_prim_u_8_strict = _cst_new_list_prim_u_8_strictPtr
|
||||
.asFunction<ffi.Pointer<wire_cst_list_prim_u_8_strict> Function(int)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_process_info> cst_new_list_process_info(int len) {
|
||||
return _cst_new_list_process_info(len);
|
||||
}
|
||||
|
||||
late final _cst_new_list_process_infoPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Pointer<wire_cst_list_process_info> Function(ffi.Int32)
|
||||
>
|
||||
>('frbgen_starcitizen_doctor_cst_new_list_process_info');
|
||||
late final _cst_new_list_process_info = _cst_new_list_process_infoPtr
|
||||
.asFunction<ffi.Pointer<wire_cst_list_process_info> Function(int)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_record_string_string>
|
||||
cst_new_list_record_string_string(int len) {
|
||||
return _cst_new_list_record_string_string(len);
|
||||
@@ -1224,6 +1334,22 @@ final class wire_cst_list_String extends ffi.Struct {
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_process_info extends ffi.Struct {
|
||||
@ffi.Uint32()
|
||||
external int pid;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> name;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> path;
|
||||
}
|
||||
|
||||
final class wire_cst_list_process_info extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_process_info> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_rs_process_stream_data extends ffi.Struct {
|
||||
@ffi.Int32()
|
||||
external int data_type;
|
||||
|
||||
Reference in New Issue
Block a user