feat: 增加基础的游戏进程监听

This commit is contained in:
xkeyC
2025-11-20 00:27:20 +08:00
parent f6340337db
commit b65187d4f0
19 changed files with 1873 additions and 702 deletions

View 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;
}
}

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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;