diff --git a/lib/app.dart b/lib/app.dart index 76a0fbd..56bc3c9 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -22,7 +22,6 @@ import 'package:window_manager/window_manager.dart'; import 'api/analytics.dart'; import 'api/api.dart'; import 'common/conf/url_conf.dart'; -import 'common/helper/system_helper.dart'; import 'common/io/rs_http.dart'; import 'common/rust/frb_generated.dart'; import 'common/rust/api/win32_api.dart' as win32; @@ -148,16 +147,6 @@ class AppGlobalModel extends _$AppGlobalModel { exit(0); } - // init powershell - if (Platform.isWindows) { - try { - await SystemHelper.initPowershellPath(); - dPrint("---- Powershell init -----"); - } catch (e) { - dPrint("powershell init failed : $e"); - } - } - // get windows info WindowsDeviceInfo? windowsDeviceInfo; try { diff --git a/lib/common/helper/system_helper.dart b/lib/common/helper/system_helper.dart index 9506b6b..6696366 100644 --- a/lib/common/helper/system_helper.dart +++ b/lib/common/helper/system_helper.dart @@ -5,76 +5,31 @@ import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32; class SystemHelper { - static String powershellPath = "powershell.exe"; - - static Future initPowershellPath() async { - try { - var result = await Process.run(powershellPath, ["echo", "pong"]); - if (!result.stdout.toString().startsWith("pong") && powershellPath == "powershell.exe") { - throw "powershell check failed"; - } - } catch (e) { - Map envVars = Platform.environment; - final systemRoot = envVars["SYSTEMROOT"]; - if (systemRoot != null) { - final autoSearchPath = "$systemRoot\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"; - dPrint("auto search powershell path === $autoSearchPath"); - powershellPath = autoSearchPath; - } - } - } - static Future checkNvmePatchStatus() async { try { - var result = await Process.run(SystemHelper.powershellPath, [ - "Get-ItemProperty", - "-Path", - "\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"", - "-Name", - "\"ForcedPhysicalSectorSizeInBytes\"", - ]); - dPrint("checkNvmePatchStatus result ==== ${result.stdout}"); - if (result.stderr == "" && result.stdout.toString().contains("{* 4095}")) { - return true; - } else { - return false; - } + return await win32.checkNvmePatchStatus(); } catch (e) { + dPrint("checkNvmePatchStatus error: $e"); return false; } } static Future addNvmePatch() async { - var result = await Process.run(powershellPath, [ - 'New-ItemProperty', - "-Path", - "\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"", - "-Name", - "ForcedPhysicalSectorSizeInBytes", - "-PropertyType MultiString", - "-Force -Value", - "\"* 4095\"", - ]); - dPrint("nvme_PhysicalBytes result == ${result.stdout}"); - return result.stderr; + try { + await win32.addNvmePatch(); + return ""; + } catch (e) { + dPrint("addNvmePatch error: $e"); + return e.toString(); + } } static Future doRemoveNvmePath() async { try { - var result = await Process.run(powershellPath, [ - "Clear-ItemProperty", - "-Path", - "\"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\stornvme\\Parameters\\Device\"", - "-Name", - "\"ForcedPhysicalSectorSizeInBytes\"", - ]); - dPrint("doRemoveNvmePath result ==== ${result.stdout}"); - if (result.stderr == "") { - return true; - } else { - return false; - } + await win32.removeNvmePatch(); + return true; } catch (e) { + dPrint("doRemoveNvmePath error: $e"); return false; } } @@ -98,15 +53,17 @@ class SystemHelper { "$programDataPath\\Microsoft\\Windows\\Start Menu\\Programs\\Roberts Space Industries\\RSI Launcher.lnk"; final rsiLinkFile = File(rsiFilePath); if (await rsiLinkFile.exists()) { - final r = await Process.run(SystemHelper.powershellPath, [ - "(New-Object -ComObject WScript.Shell).CreateShortcut(\"$rsiFilePath\").targetpath", - ]); - if (r.stdout.toString().contains("RSI Launcher.exe")) { - final start = r.stdout.toString().split("RSI Launcher.exe"); - if (skipEXE) { - return start[0]; + try { + final targetPath = await win32.resolveShortcut(lnkPath: rsiFilePath); + if (targetPath.contains("RSI Launcher.exe")) { + final start = targetPath.split("RSI Launcher.exe"); + if (skipEXE) { + return start[0]; + } + return "${start[0]}RSI Launcher.exe"; } - return "${start[0]}RSI Launcher.exe"; + } catch (e) { + dPrint("resolveShortcut error: $e"); } } return ""; @@ -147,45 +104,78 @@ class SystemHelper { } static Future getSystemMemorySizeGB() async { - final r = await Process.run(powershellPath, [ - "(Get-CimInstance Win32_PhysicalMemory | Measure-Object -Property capacity -Sum).sum /1gb", - ]); - return int.tryParse(r.stdout.toString().trim()) ?? 0; + try { + final memoryGb = await win32.getSystemMemorySizeGb(); + return memoryGb.toInt(); + } catch (e) { + dPrint("getSystemMemorySizeGB error: $e"); + return 0; + } } static Future getSystemCimInstance(String win32InstanceName, {pathName = "Name"}) async { - final r = await Process.run(powershellPath, ["(Get-CimInstance $win32InstanceName).$pathName"]); - return r.stdout.toString().trim(); + // This method is deprecated, use getSystemInfo() instead + try { + final sysInfo = await win32.getSystemInfo(); + if (win32InstanceName.contains("OperatingSystem")) { + return sysInfo.osName; + } else if (win32InstanceName.contains("Processor")) { + return sysInfo.cpuName; + } else if (win32InstanceName.contains("VideoController")) { + return sysInfo.gpuInfo; + } else if (win32InstanceName.contains("DiskDrive")) { + return sysInfo.diskInfo; + } + } catch (e) { + dPrint("getSystemCimInstance error: $e"); + } + return ""; } static Future getSystemName() async { - final r = await Process.run(powershellPath, ["(Get-ComputerInfo | Select-Object -expand OsName)"]); - return r.stdout.toString().trim(); + try { + final sysInfo = await win32.getSystemInfo(); + return sysInfo.osName; + } catch (e) { + dPrint("getSystemName error: $e"); + return ""; + } } static Future getCpuName() async { - final r = await Process.run(powershellPath, ["(Get-WmiObject -Class Win32_Processor).Name"]); - return r.stdout.toString().trim(); + try { + final sysInfo = await win32.getSystemInfo(); + return sysInfo.cpuName; + } catch (e) { + dPrint("getCpuName error: $e"); + return ""; + } } static Future getGpuInfo() async { - const cmd = r""" - $adapterMemory = (Get-ItemProperty -Path "HKLM:\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0*" -Name "HardwareInformation.AdapterString", "HardwareInformation.qwMemorySize" -Exclude PSPath -ErrorAction SilentlyContinue) -foreach ($adapter in $adapterMemory) { - [PSCustomObject] @{ - Model=$adapter."HardwareInformation.AdapterString" - "VRAM (GB)"=[math]::round($adapter."HardwareInformation.qwMemorySize"/1GB) - } -} - """; - final r = await Process.run(powershellPath, [cmd]); - return r.stdout.toString().trim(); + try { + // Try registry first for more accurate VRAM info + final regInfo = await win32.getGpuInfoFromRegistry(); + if (regInfo.isNotEmpty) { + return regInfo; + } + // Fallback to WMI + final sysInfo = await win32.getSystemInfo(); + return sysInfo.gpuInfo; + } catch (e) { + dPrint("getGpuInfo error: $e"); + return ""; + } } static Future getDiskInfo() async { - return (await Process.run(powershellPath, [ - "Get-PhysicalDisk | format-table BusType,FriendlyName,Size", - ])).stdout.toString().trim(); + try { + final sysInfo = await win32.getSystemInfo(); + return sysInfo.diskInfo; + } catch (e) { + dPrint("getDiskInfo error: $e"); + return ""; + } } static Future getDirLen(String path, {List? skipPath}) async { @@ -212,11 +202,12 @@ foreach ($adapter in $adapterMemory) { } static Future getNumberOfLogicalProcessors() async { - final cpuNumberResult = await Process.run(powershellPath, [ - "(Get-WmiObject -Class Win32_Processor).NumberOfLogicalProcessors", - ]); - if (cpuNumberResult.exitCode != 0) return 0; - return int.tryParse(cpuNumberResult.stdout.toString().trim()) ?? 0; + try { + return await win32.getNumberOfLogicalProcessors(); + } catch (e) { + dPrint("getNumberOfLogicalProcessors error: $e"); + return 0; + } } static Future getCpuAffinity() async { @@ -244,10 +235,11 @@ foreach ($adapter in $adapterMemory) { static Future openDir(dynamic path, {bool isFile = false}) async { dPrint("SystemHelper.openDir path === $path"); if (Platform.isWindows) { - await Process.run(SystemHelper.powershellPath, [ - "explorer.exe", - isFile ? "/select,$path" : "\"/select,\"$path\"\"", - ]); + try { + await win32.openDirWithExplorer(path: path.toString(), isFile: isFile); + } catch (e) { + dPrint("openDir error: $e"); + } } } diff --git a/lib/common/rust/api/webview_api.dart b/lib/common/rust/api/webview_api.dart index ca03cf4..d889e82 100644 --- a/lib/common/rust/api/webview_api.dart +++ b/lib/common/rust/api/webview_api.dart @@ -8,9 +8,8 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; import 'package:freezed_annotation/freezed_annotation.dart' hide protected; part 'webview_api.freezed.dart'; -// These functions are ignored because they are not marked as `pub`: `handle_command`, `load_app_icon`, `run_webview_loop`, `send_command` -// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `UserEvent`, `WebViewCommand`, `WebViewInstance` -// 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`, `clone`, `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt` +// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `WebViewCommand` +// 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`, `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `fmt` /// Create a new WebView window and return its ID String webviewCreate({required WebViewConfiguration config}) => diff --git a/lib/common/rust/api/win32_api.dart b/lib/common/rust/api/win32_api.dart index 78d9de7..cda3105 100644 --- a/lib/common/rust/api/win32_api.dart +++ b/lib/common/rust/api/win32_api.dart @@ -7,7 +7,7 @@ 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` +// 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`, `clone`, `fmt`, `fmt` Future sendNotify({ String? summary, @@ -21,6 +21,35 @@ Future sendNotify({ appId: appId, ); +/// Get system memory size in GB +Future getSystemMemorySizeGb() => + RustLib.instance.api.crateApiWin32ApiGetSystemMemorySizeGb(); + +/// Get number of logical processors +Future getNumberOfLogicalProcessors() => + RustLib.instance.api.crateApiWin32ApiGetNumberOfLogicalProcessors(); + +/// Get all system information at once +Future getSystemInfo() => + RustLib.instance.api.crateApiWin32ApiGetSystemInfo(); + +/// Get GPU info from registry (more accurate VRAM) +Future getGpuInfoFromRegistry() => + RustLib.instance.api.crateApiWin32ApiGetGpuInfoFromRegistry(); + +/// Resolve shortcut (.lnk) file to get target path +Future resolveShortcut({required String lnkPath}) => + RustLib.instance.api.crateApiWin32ApiResolveShortcut(lnkPath: lnkPath); + +/// Open file explorer and select file/folder +Future openDirWithExplorer({ + required String path, + required bool isFile, +}) => RustLib.instance.api.crateApiWin32ApiOpenDirWithExplorer( + path: path, + isFile: isFile, +); + Future setForegroundWindow({required String windowName}) => RustLib .instance .api @@ -36,6 +65,55 @@ Future> getProcessListByName({required String processName}) => processName: processName, ); +/// Kill processes by name +Future killProcessByName({required String processName}) => RustLib + .instance + .api + .crateApiWin32ApiKillProcessByName(processName: processName); + +/// Get disk physical sector size for performance +Future getDiskPhysicalSectorSize({required String driveLetter}) => RustLib + .instance + .api + .crateApiWin32ApiGetDiskPhysicalSectorSize(driveLetter: driveLetter); + +/// Create a desktop shortcut +Future createDesktopShortcut({ + required String targetPath, + required String shortcutName, +}) => RustLib.instance.api.crateApiWin32ApiCreateDesktopShortcut( + targetPath: targetPath, + shortcutName: shortcutName, +); + +/// Run a program with admin privileges (UAC) +Future runAsAdmin({required String program, required String args}) => + RustLib.instance.api.crateApiWin32ApiRunAsAdmin( + program: program, + args: args, + ); + +/// Start a program (without waiting) +Future startProcess({ + required String program, + required List args, +}) => RustLib.instance.api.crateApiWin32ApiStartProcess( + program: program, + args: args, +); + +/// Check if NVME patch is applied +Future checkNvmePatchStatus() => + RustLib.instance.api.crateApiWin32ApiCheckNvmePatchStatus(); + +/// Add NVME patch to registry +Future addNvmePatch() => + RustLib.instance.api.crateApiWin32ApiAddNvmePatch(); + +/// Remove NVME patch from registry +Future removeNvmePatch() => + RustLib.instance.api.crateApiWin32ApiRemoveNvmePatch(); + class ProcessInfo { final int pid; final String name; @@ -59,3 +137,32 @@ class ProcessInfo { name == other.name && path == other.path; } + +/// System information struct +class SystemInfo { + final String osName; + final String cpuName; + final String gpuInfo; + final String diskInfo; + + const SystemInfo({ + required this.osName, + required this.cpuName, + required this.gpuInfo, + required this.diskInfo, + }); + + @override + int get hashCode => + osName.hashCode ^ cpuName.hashCode ^ gpuInfo.hashCode ^ diskInfo.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is SystemInfo && + runtimeType == other.runtimeType && + osName == other.osName && + cpuName == other.cpuName && + gpuInfo == other.gpuInfo && + diskInfo == other.diskInfo; +} diff --git a/lib/common/rust/frb_generated.dart b/lib/common/rust/frb_generated.dart index ba7174b..2a1d56c 100644 --- a/lib/common/rust/frb_generated.dart +++ b/lib/common/rust/frb_generated.dart @@ -71,7 +71,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.11.1'; @override - int get rustContentHash => -1082688871; + int get rustContentHash => 1161621087; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -82,8 +82,17 @@ class RustLib extends BaseEntrypoint { } abstract class RustLibApi extends BaseApi { + Future crateApiWin32ApiAddNvmePatch(); + + Future crateApiWin32ApiCheckNvmePatchStatus(); + Future crateApiOrtApiClearAllModels(); + Future crateApiWin32ApiCreateDesktopShortcut({ + required String targetPath, + required String shortcutName, + }); + Future> crateApiHttpApiDnsLookupIps({required String host}); Future> crateApiHttpApiDnsLookupTxt({required String host}); @@ -97,11 +106,19 @@ abstract class RustLibApi extends BaseApi { bool? withCustomDns, }); + Future crateApiWin32ApiGetDiskPhysicalSectorSize({ + required String driveLetter, + }); + Future crateApiHttpApiGetFasterUrl({ required List urls, String? pathSuffix, }); + Future crateApiWin32ApiGetGpuInfoFromRegistry(); + + Future crateApiWin32ApiGetNumberOfLogicalProcessors(); + Future> crateApiWin32ApiGetProcessListByName({ required String processName, }); @@ -114,6 +131,12 @@ abstract class RustLibApi extends BaseApi { required String asarPath, }); + Future crateApiWin32ApiGetSystemInfo(); + + Future crateApiWin32ApiGetSystemMemorySizeGb(); + + Future crateApiWin32ApiKillProcessByName({required String processName}); + Future crateApiOrtApiLoadTranslationModel({ required String modelPath, required String modelKey, @@ -121,6 +144,11 @@ abstract class RustLibApi extends BaseApi { required bool useXnnpack, }); + Future crateApiWin32ApiOpenDirWithExplorer({ + required String path, + required bool isFile, + }); + Future crateApiUnp4KApiP4KClose(); Future crateApiUnp4KApiP4KExtractToDisk({ @@ -138,11 +166,20 @@ abstract class RustLibApi extends BaseApi { Future crateApiUnp4KApiP4KOpen({required String p4KPath}); + Future crateApiWin32ApiRemoveNvmePatch(); + + Future crateApiWin32ApiResolveShortcut({required String lnkPath}); + Future crateApiAsarApiRsiLauncherAsarDataWriteMainJs({ required RsiLauncherAsarData that, required List content, }); + Future crateApiWin32ApiRunAsAdmin({ + required String program, + required String args, + }); + Future crateApiWin32ApiSendNotify({ String? summary, String? body, @@ -164,6 +201,11 @@ abstract class RustLibApi extends BaseApi { required String workingDirectory, }); + Future crateApiWin32ApiStartProcess({ + required String program, + required List args, + }); + Future crateApiOrtApiTranslateText({ required String modelKey, required String text, @@ -246,6 +288,50 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { required super.portManager, }); + @override + Future crateApiWin32ApiAddNvmePatch() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + return wire.wire__crate__api__win32_api__add_nvme_patch(port_); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_unit, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiAddNvmePatchConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiAddNvmePatchConstMeta => + const TaskConstMeta(debugName: "add_nvme_patch", argNames: []); + + @override + Future crateApiWin32ApiCheckNvmePatchStatus() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + return wire.wire__crate__api__win32_api__check_nvme_patch_status( + port_, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_bool, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiCheckNvmePatchStatusConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiCheckNvmePatchStatusConstMeta => + const TaskConstMeta(debugName: "check_nvme_patch_status", argNames: []); + @override Future crateApiOrtApiClearAllModels() { return handler.executeNormal( @@ -267,6 +353,39 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiOrtApiClearAllModelsConstMeta => const TaskConstMeta(debugName: "clear_all_models", argNames: []); + @override + Future crateApiWin32ApiCreateDesktopShortcut({ + required String targetPath, + required String shortcutName, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_String(targetPath); + var arg1 = cst_encode_String(shortcutName); + return wire.wire__crate__api__win32_api__create_desktop_shortcut( + port_, + arg0, + arg1, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_unit, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiCreateDesktopShortcutConstMeta, + argValues: [targetPath, shortcutName], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiCreateDesktopShortcutConstMeta => + const TaskConstMeta( + debugName: "create_desktop_shortcut", + argNames: ["targetPath", "shortcutName"], + ); + @override Future> crateApiHttpApiDnsLookupIps({required String host}) { return handler.executeNormal( @@ -369,6 +488,37 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ], ); + @override + Future crateApiWin32ApiGetDiskPhysicalSectorSize({ + required String driveLetter, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_String(driveLetter); + return wire + .wire__crate__api__win32_api__get_disk_physical_sector_size( + port_, + arg0, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_u_32, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiGetDiskPhysicalSectorSizeConstMeta, + argValues: [driveLetter], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiGetDiskPhysicalSectorSizeConstMeta => + const TaskConstMeta( + debugName: "get_disk_physical_sector_size", + argNames: ["driveLetter"], + ); + @override Future crateApiHttpApiGetFasterUrl({ required List urls, @@ -402,6 +552,59 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["urls", "pathSuffix"], ); + @override + Future crateApiWin32ApiGetGpuInfoFromRegistry() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + return wire.wire__crate__api__win32_api__get_gpu_info_from_registry( + port_, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_String, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiGetGpuInfoFromRegistryConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiGetGpuInfoFromRegistryConstMeta => + const TaskConstMeta( + debugName: "get_gpu_info_from_registry", + argNames: [], + ); + + @override + Future crateApiWin32ApiGetNumberOfLogicalProcessors() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + return wire + .wire__crate__api__win32_api__get_number_of_logical_processors( + port_, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_u_32, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiGetNumberOfLogicalProcessorsConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiGetNumberOfLogicalProcessorsConstMeta => + const TaskConstMeta( + debugName: "get_number_of_logical_processors", + argNames: [], + ); + @override Future> crateApiWin32ApiGetProcessListByName({ required String processName, @@ -492,6 +695,78 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["asarPath"], ); + @override + Future crateApiWin32ApiGetSystemInfo() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + return wire.wire__crate__api__win32_api__get_system_info(port_); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_system_info, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiGetSystemInfoConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiGetSystemInfoConstMeta => + const TaskConstMeta(debugName: "get_system_info", argNames: []); + + @override + Future crateApiWin32ApiGetSystemMemorySizeGb() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + return wire.wire__crate__api__win32_api__get_system_memory_size_gb( + port_, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_u_64, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiGetSystemMemorySizeGbConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiGetSystemMemorySizeGbConstMeta => + const TaskConstMeta(debugName: "get_system_memory_size_gb", argNames: []); + + @override + Future crateApiWin32ApiKillProcessByName({required String processName}) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_String(processName); + return wire.wire__crate__api__win32_api__kill_process_by_name( + port_, + arg0, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_u_32, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiKillProcessByNameConstMeta, + argValues: [processName], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiKillProcessByNameConstMeta => + const TaskConstMeta( + debugName: "kill_process_by_name", + argNames: ["processName"], + ); + @override Future crateApiOrtApiLoadTranslationModel({ required String modelPath, @@ -531,6 +806,39 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["modelPath", "modelKey", "quantizationSuffix", "useXnnpack"], ); + @override + Future crateApiWin32ApiOpenDirWithExplorer({ + required String path, + required bool isFile, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_String(path); + var arg1 = cst_encode_bool(isFile); + return wire.wire__crate__api__win32_api__open_dir_with_explorer( + port_, + arg0, + arg1, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_unit, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiOpenDirWithExplorerConstMeta, + argValues: [path, isFile], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiOpenDirWithExplorerConstMeta => + const TaskConstMeta( + debugName: "open_dir_with_explorer", + argNames: ["path", "isFile"], + ); + @override Future crateApiUnp4KApiP4KClose() { return handler.executeNormal( @@ -679,6 +987,52 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiUnp4KApiP4KOpenConstMeta => const TaskConstMeta(debugName: "p4k_open", argNames: ["p4KPath"]); + @override + Future crateApiWin32ApiRemoveNvmePatch() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + return wire.wire__crate__api__win32_api__remove_nvme_patch(port_); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_unit, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiRemoveNvmePatchConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiRemoveNvmePatchConstMeta => + const TaskConstMeta(debugName: "remove_nvme_patch", argNames: []); + + @override + Future crateApiWin32ApiResolveShortcut({required String lnkPath}) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_String(lnkPath); + return wire.wire__crate__api__win32_api__resolve_shortcut( + port_, + arg0, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_String, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiResolveShortcutConstMeta, + argValues: [lnkPath], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiResolveShortcutConstMeta => + const TaskConstMeta(debugName: "resolve_shortcut", argNames: ["lnkPath"]); + @override Future crateApiAsarApiRsiLauncherAsarDataWriteMainJs({ required RsiLauncherAsarData that, @@ -713,6 +1067,38 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["that", "content"], ); + @override + Future crateApiWin32ApiRunAsAdmin({ + required String program, + required String args, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_String(program); + var arg1 = cst_encode_String(args); + return wire.wire__crate__api__win32_api__run_as_admin( + port_, + arg0, + arg1, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_unit, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiRunAsAdminConstMeta, + argValues: [program, args], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiRunAsAdminConstMeta => const TaskConstMeta( + debugName: "run_as_admin", + argNames: ["program", "args"], + ); + @override Future crateApiWin32ApiSendNotify({ String? summary, @@ -854,6 +1240,39 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["executable", "arguments", "workingDirectory", "streamSink"], ); + @override + Future crateApiWin32ApiStartProcess({ + required String program, + required List args, + }) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_String(program); + var arg1 = cst_encode_list_String(args); + return wire.wire__crate__api__win32_api__start_process( + port_, + arg0, + arg1, + ); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_unit, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateApiWin32ApiStartProcessConstMeta, + argValues: [program, args], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiWin32ApiStartProcessConstMeta => + const TaskConstMeta( + debugName: "start_process", + argNames: ["program", "args"], + ); + @override Future crateApiOrtApiTranslateText({ required String modelKey, @@ -1667,6 +2086,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } + @protected + SystemInfo dco_decode_system_info(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 4) + throw Exception('unexpected arr length: expect 4 but see ${arr.length}'); + return SystemInfo( + osName: dco_decode_String(arr[0]), + cpuName: dco_decode_String(arr[1]), + gpuInfo: dco_decode_String(arr[2]), + diskInfo: dco_decode_String(arr[3]), + ); + } + @protected int dco_decode_u_16(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -2081,6 +2514,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } + @protected + SystemInfo sse_decode_system_info(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_osName = sse_decode_String(deserializer); + var var_cpuName = sse_decode_String(deserializer); + var var_gpuInfo = sse_decode_String(deserializer); + var var_diskInfo = sse_decode_String(deserializer); + return SystemInfo( + osName: var_osName, + cpuName: var_cpuName, + gpuInfo: var_gpuInfo, + diskInfo: var_diskInfo, + ); + } + @protected int sse_decode_u_16(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -2556,6 +3004,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_list_prim_u_8_strict(self.data, serializer); } + @protected + void sse_encode_system_info(SystemInfo self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.osName, serializer); + sse_encode_String(self.cpuName, serializer); + sse_encode_String(self.gpuInfo, serializer); + sse_encode_String(self.diskInfo, serializer); + } + @protected void sse_encode_u_16(int self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs diff --git a/lib/common/rust/frb_generated.io.dart b/lib/common/rust/frb_generated.io.dart index 9bdbb07..b87ec5e 100644 --- a/lib/common/rust/frb_generated.io.dart +++ b/lib/common/rust/frb_generated.io.dart @@ -126,6 +126,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected RustHttpResponse dco_decode_rust_http_response(dynamic raw); + @protected + SystemInfo dco_decode_system_info(dynamic raw); + @protected int dco_decode_u_16(dynamic raw); @@ -274,6 +277,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected RustHttpResponse sse_decode_rust_http_response(SseDeserializer deserializer); + @protected + SystemInfo sse_decode_system_info(SseDeserializer deserializer); + @protected int sse_decode_u_16(SseDeserializer deserializer); @@ -588,6 +594,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.data = cst_encode_opt_list_prim_u_8_strict(apiObj.data); } + @protected + void cst_api_fill_to_wire_system_info( + SystemInfo apiObj, + wire_cst_system_info wireObj, + ) { + wireObj.os_name = cst_encode_String(apiObj.osName); + wireObj.cpu_name = cst_encode_String(apiObj.cpuName); + wireObj.gpu_info = cst_encode_String(apiObj.gpuInfo); + wireObj.disk_info = cst_encode_String(apiObj.diskInfo); + } + @protected void cst_api_fill_to_wire_web_view_configuration( WebViewConfiguration apiObj, @@ -829,6 +846,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); + @protected + void sse_encode_system_info(SystemInfo self, SseSerializer serializer); + @protected void sse_encode_u_16(int self, SseSerializer serializer); @@ -900,6 +920,30 @@ class RustLibWire implements BaseWire { late final _store_dart_post_cobject = _store_dart_post_cobjectPtr .asFunction(); + void wire__crate__api__win32_api__add_nvme_patch(int port_) { + return _wire__crate__api__win32_api__add_nvme_patch(port_); + } + + late final _wire__crate__api__win32_api__add_nvme_patchPtr = + _lookup>( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__add_nvme_patch', + ); + late final _wire__crate__api__win32_api__add_nvme_patch = + _wire__crate__api__win32_api__add_nvme_patchPtr + .asFunction(); + + void wire__crate__api__win32_api__check_nvme_patch_status(int port_) { + return _wire__crate__api__win32_api__check_nvme_patch_status(port_); + } + + late final _wire__crate__api__win32_api__check_nvme_patch_statusPtr = + _lookup>( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__check_nvme_patch_status', + ); + late final _wire__crate__api__win32_api__check_nvme_patch_status = + _wire__crate__api__win32_api__check_nvme_patch_statusPtr + .asFunction(); + void wire__crate__api__ort_api__clear_all_models(int port_) { return _wire__crate__api__ort_api__clear_all_models(port_); } @@ -912,6 +956,40 @@ class RustLibWire implements BaseWire { _wire__crate__api__ort_api__clear_all_modelsPtr .asFunction(); + void wire__crate__api__win32_api__create_desktop_shortcut( + int port_, + ffi.Pointer target_path, + ffi.Pointer shortcut_name, + ) { + return _wire__crate__api__win32_api__create_desktop_shortcut( + port_, + target_path, + shortcut_name, + ); + } + + late final _wire__crate__api__win32_api__create_desktop_shortcutPtr = + _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, + ffi.Pointer, + ffi.Pointer, + ) + > + >( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__create_desktop_shortcut', + ); + late final _wire__crate__api__win32_api__create_desktop_shortcut = + _wire__crate__api__win32_api__create_desktop_shortcutPtr + .asFunction< + void Function( + int, + ffi.Pointer, + ffi.Pointer, + ) + >(); + void wire__crate__api__http_api__dns_lookup_ips( int port_, ffi.Pointer host, @@ -1004,6 +1082,33 @@ class RustLibWire implements BaseWire { ) >(); + void wire__crate__api__win32_api__get_disk_physical_sector_size( + int port_, + ffi.Pointer drive_letter, + ) { + return _wire__crate__api__win32_api__get_disk_physical_sector_size( + port_, + drive_letter, + ); + } + + late final _wire__crate__api__win32_api__get_disk_physical_sector_sizePtr = + _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, + ffi.Pointer, + ) + > + >( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_disk_physical_sector_size', + ); + late final _wire__crate__api__win32_api__get_disk_physical_sector_size = + _wire__crate__api__win32_api__get_disk_physical_sector_sizePtr + .asFunction< + void Function(int, ffi.Pointer) + >(); + void wire__crate__api__http_api__get_faster_url( int port_, ffi.Pointer urls, @@ -1036,6 +1141,34 @@ class RustLibWire implements BaseWire { ) >(); + void wire__crate__api__win32_api__get_gpu_info_from_registry(int port_) { + return _wire__crate__api__win32_api__get_gpu_info_from_registry(port_); + } + + late final _wire__crate__api__win32_api__get_gpu_info_from_registryPtr = + _lookup>( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_gpu_info_from_registry', + ); + late final _wire__crate__api__win32_api__get_gpu_info_from_registry = + _wire__crate__api__win32_api__get_gpu_info_from_registryPtr + .asFunction(); + + void wire__crate__api__win32_api__get_number_of_logical_processors( + int port_, + ) { + return _wire__crate__api__win32_api__get_number_of_logical_processors( + port_, + ); + } + + late final _wire__crate__api__win32_api__get_number_of_logical_processorsPtr = + _lookup>( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_number_of_logical_processors', + ); + late final _wire__crate__api__win32_api__get_number_of_logical_processors = + _wire__crate__api__win32_api__get_number_of_logical_processorsPtr + .asFunction(); + void wire__crate__api__win32_api__get_process_list_by_name( int port_, ffi.Pointer process_name, @@ -1117,6 +1250,57 @@ class RustLibWire implements BaseWire { void Function(int, ffi.Pointer) >(); + void wire__crate__api__win32_api__get_system_info(int port_) { + return _wire__crate__api__win32_api__get_system_info(port_); + } + + late final _wire__crate__api__win32_api__get_system_infoPtr = + _lookup>( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_system_info', + ); + late final _wire__crate__api__win32_api__get_system_info = + _wire__crate__api__win32_api__get_system_infoPtr + .asFunction(); + + void wire__crate__api__win32_api__get_system_memory_size_gb(int port_) { + return _wire__crate__api__win32_api__get_system_memory_size_gb(port_); + } + + late final _wire__crate__api__win32_api__get_system_memory_size_gbPtr = + _lookup>( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_system_memory_size_gb', + ); + late final _wire__crate__api__win32_api__get_system_memory_size_gb = + _wire__crate__api__win32_api__get_system_memory_size_gbPtr + .asFunction(); + + void wire__crate__api__win32_api__kill_process_by_name( + int port_, + ffi.Pointer process_name, + ) { + return _wire__crate__api__win32_api__kill_process_by_name( + port_, + process_name, + ); + } + + late final _wire__crate__api__win32_api__kill_process_by_namePtr = + _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, + ffi.Pointer, + ) + > + >( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__kill_process_by_name', + ); + late final _wire__crate__api__win32_api__kill_process_by_name = + _wire__crate__api__win32_api__kill_process_by_namePtr + .asFunction< + void Function(int, ffi.Pointer) + >(); + void wire__crate__api__ort_api__load_translation_model( int port_, ffi.Pointer model_path, @@ -1159,6 +1343,36 @@ class RustLibWire implements BaseWire { ) >(); + void wire__crate__api__win32_api__open_dir_with_explorer( + int port_, + ffi.Pointer path, + bool is_file, + ) { + return _wire__crate__api__win32_api__open_dir_with_explorer( + port_, + path, + is_file, + ); + } + + late final _wire__crate__api__win32_api__open_dir_with_explorerPtr = + _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, + ffi.Pointer, + ffi.Bool, + ) + > + >( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__open_dir_with_explorer', + ); + late final _wire__crate__api__win32_api__open_dir_with_explorer = + _wire__crate__api__win32_api__open_dir_with_explorerPtr + .asFunction< + void Function(int, ffi.Pointer, bool) + >(); + void wire__crate__api__unp4k_api__p4k_close(int port_) { return _wire__crate__api__unp4k_api__p4k_close(port_); } @@ -1278,6 +1492,42 @@ class RustLibWire implements BaseWire { void Function(int, ffi.Pointer) >(); + void wire__crate__api__win32_api__remove_nvme_patch(int port_) { + return _wire__crate__api__win32_api__remove_nvme_patch(port_); + } + + late final _wire__crate__api__win32_api__remove_nvme_patchPtr = + _lookup>( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__remove_nvme_patch', + ); + late final _wire__crate__api__win32_api__remove_nvme_patch = + _wire__crate__api__win32_api__remove_nvme_patchPtr + .asFunction(); + + void wire__crate__api__win32_api__resolve_shortcut( + int port_, + ffi.Pointer lnk_path, + ) { + return _wire__crate__api__win32_api__resolve_shortcut(port_, lnk_path); + } + + late final _wire__crate__api__win32_api__resolve_shortcutPtr = + _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, + ffi.Pointer, + ) + > + >( + 'frbgen_starcitizen_doctor_wire__crate__api__win32_api__resolve_shortcut', + ); + late final _wire__crate__api__win32_api__resolve_shortcut = + _wire__crate__api__win32_api__resolve_shortcutPtr + .asFunction< + void Function(int, ffi.Pointer) + >(); + void wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js( int port_, ffi.Pointer that, @@ -1312,6 +1562,34 @@ class RustLibWire implements BaseWire { ) >(); + void wire__crate__api__win32_api__run_as_admin( + int port_, + ffi.Pointer program, + ffi.Pointer args, + ) { + return _wire__crate__api__win32_api__run_as_admin(port_, program, args); + } + + late final _wire__crate__api__win32_api__run_as_adminPtr = + _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, + ffi.Pointer, + ffi.Pointer, + ) + > + >('frbgen_starcitizen_doctor_wire__crate__api__win32_api__run_as_admin'); + late final _wire__crate__api__win32_api__run_as_admin = + _wire__crate__api__win32_api__run_as_adminPtr + .asFunction< + void Function( + int, + ffi.Pointer, + ffi.Pointer, + ) + >(); + void wire__crate__api__win32_api__send_notify( int port_, ffi.Pointer summary, @@ -1443,6 +1721,34 @@ class RustLibWire implements BaseWire { ) >(); + void wire__crate__api__win32_api__start_process( + int port_, + ffi.Pointer program, + ffi.Pointer args, + ) { + return _wire__crate__api__win32_api__start_process(port_, program, args); + } + + late final _wire__crate__api__win32_api__start_processPtr = + _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, + ffi.Pointer, + ffi.Pointer, + ) + > + >('frbgen_starcitizen_doctor_wire__crate__api__win32_api__start_process'); + late final _wire__crate__api__win32_api__start_process = + _wire__crate__api__win32_api__start_processPtr + .asFunction< + void Function( + int, + ffi.Pointer, + ffi.Pointer, + ) + >(); + void wire__crate__api__ort_api__translate_text( int port_, ffi.Pointer model_key, @@ -2321,6 +2627,16 @@ final class wire_cst_rust_http_response extends ffi.Struct { external ffi.Pointer data; } +final class wire_cst_system_info extends ffi.Struct { + external ffi.Pointer os_name; + + external ffi.Pointer cpu_name; + + external ffi.Pointer gpu_info; + + external ffi.Pointer disk_info; +} + final class wire_cst_web_view_navigation_state extends ffi.Struct { external ffi.Pointer url; diff --git a/lib/common/rust/rust_webview_controller.dart b/lib/common/rust/rust_webview_controller.dart index b8ec5af..43fd6fb 100644 --- a/lib/common/rust/rust_webview_controller.dart +++ b/lib/common/rust/rust_webview_controller.dart @@ -4,7 +4,8 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter/services.dart'; -import 'package:starcitizen_doctor/common/rust/api/webview_api.dart' as rust_webview; +import 'package:starcitizen_doctor/common/rust/api/webview_api.dart' + as rust_webview; import 'package:starcitizen_doctor/common/utils/log.dart'; typedef OnWebMessageCallback = void Function(String message); @@ -76,7 +77,9 @@ class RustWebViewController { Future _loadScripts() async { try { _localizationScript = await rootBundle.loadString('assets/web_script.js'); - _requestInterceptorScript = await rootBundle.loadString('assets/request_interceptor.js'); + _requestInterceptorScript = await rootBundle.loadString( + 'assets/request_interceptor.js', + ); } catch (e) { dPrint("Failed to load scripts: $e"); } @@ -286,12 +289,16 @@ class RustWebViewController { } /// 添加导航完成回调(用于在页面加载完成后注入脚本) - void addOnNavigationCompletedCallback(OnNavigationCompletedCallback callback) { + void addOnNavigationCompletedCallback( + OnNavigationCompletedCallback callback, + ) { _navigationCompletedCallbacks.add(callback); } /// 移除导航完成回调 - void removeOnNavigationCompletedCallback(OnNavigationCompletedCallback callback) { + void removeOnNavigationCompletedCallback( + OnNavigationCompletedCallback callback, + ) { _navigationCompletedCallbacks.remove(callback); } @@ -320,7 +327,9 @@ class RustWebViewController { /// 更新翻译词典 void updateReplaceWords(List> words, bool enableCapture) { final jsonWords = json.encode(words); - executeScript("WebLocalizationUpdateReplaceWords($jsonWords, $enableCapture)"); + executeScript( + "WebLocalizationUpdateReplaceWords($jsonWords, $enableCapture)", + ); } /// 执行 RSI 登录脚本 diff --git a/lib/ui/home/game_doctor/game_doctor_ui_model.dart b/lib/ui/home/game_doctor/game_doctor_ui_model.dart index 45e6580..f4385f4 100644 --- a/lib/ui/home/game_doctor/game_doctor_ui_model.dart +++ b/lib/ui/home/game_doctor/game_doctor_ui_model.dart @@ -6,6 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:starcitizen_doctor/common/helper/log_helper.dart'; import 'package:starcitizen_doctor/common/helper/system_helper.dart'; +import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32; import 'package:starcitizen_doctor/common/utils/base_utils.dart'; import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/ui/home/home_ui_model.dart'; @@ -35,11 +36,11 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { } Future doFix( - // ignore: avoid_build_context_in_providers - BuildContext context, - MapEntry item) async { - final checkResult = - List>.from(state.checkResult ?? []); + // ignore: avoid_build_context_in_providers + BuildContext context, + MapEntry item, + ) async { + final checkResult = List>.from(state.checkResult ?? []); state = state.copyWith(isFixing: true, isFixingString: ""); switch (item.key) { case "unSupport_system": @@ -49,13 +50,11 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { try { await Directory(item.value).create(recursive: true); if (!context.mounted) break; - showToast( - context, S.current.doctor_action_result_create_folder_success); + showToast(context, S.current.doctor_action_result_create_folder_success); checkResult.remove(item); state = state.copyWith(checkResult: checkResult); } catch (e) { - showToast(context, - S.current.doctor_action_result_create_folder_fail(item.value, e)); + showToast(context, S.current.doctor_action_result_create_folder_fail(item.value, e)); } break; case "nvme_PhysicalBytes": @@ -71,8 +70,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { } break; case "eac_file_miss": - showToast(context, - S.current.doctor_info_result_verify_files_with_rsi_launcher); + showToast(context, S.current.doctor_info_result_verify_files_with_rsi_launcher); break; case "eac_not_install": final eacJsonPath = "${item.value}\\Settings.json"; @@ -80,19 +78,16 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { final Map eacJson = json.decode(utf8.decode(eacJsonData)); final eacID = eacJson["productid"]; try { - var result = await Process.run( - "${item.value}\\EasyAntiCheat_EOS_Setup.exe", ["install", eacID]); + var result = await Process.run("${item.value}\\EasyAntiCheat_EOS_Setup.exe", ["install", eacID]); dPrint("${item.value}\\EasyAntiCheat_EOS_Setup.exe install $eacID"); if (result.stderr == "") { if (!context.mounted) break; - showToast( - context, S.current.doctor_action_result_game_start_success); + showToast(context, S.current.doctor_action_result_game_start_success); checkResult.remove(item); state = state.copyWith(checkResult: checkResult); } else { if (!context.mounted) break; - showToast(context, - S.current.doctor_action_result_fix_fail(result.stderr)); + showToast(context, S.current.doctor_action_result_fix_fail(result.stderr)); } } catch (e) { if (!context.mounted) break; @@ -102,8 +97,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { case "cn_user_name": showToast(context, S.current.doctor_action_result_redirect_warning); await Future.delayed(const Duration(milliseconds: 300)); - launchUrlString( - "https://jingyan.baidu.com/article/59703552a318a08fc0074021.html"); + launchUrlString("https://jingyan.baidu.com/article/59703552a318a08fc0074021.html"); break; default: showToast(context, S.current.doctor_action_result_issue_not_supported); @@ -115,8 +109,7 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { // ignore: avoid_build_context_in_providers Future doCheck(BuildContext context) async { if (state.isChecking) return; - state = state.copyWith( - isChecking: true, lastScreenInfo: S.current.doctor_action_analyzing); + state = state.copyWith(isChecking: true, lastScreenInfo: S.current.doctor_action_analyzing); dPrint("-------- start docker check -----"); if (!context.mounted) return; await _statCheck(context); @@ -144,11 +137,8 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { final lastScreenInfo = S.current.doctor_action_result_analysis_no_issue; state = state.copyWith(checkResult: null, lastScreenInfo: lastScreenInfo); } else { - final lastScreenInfo = S.current - .doctor_action_result_analysis_issues_found( - checkResult.length.toString()); - state = state.copyWith( - checkResult: checkResult, lastScreenInfo: lastScreenInfo); + final lastScreenInfo = S.current.doctor_action_result_analysis_issues_found(checkResult.length.toString()); + state = state.copyWith(checkResult: checkResult, lastScreenInfo: lastScreenInfo); } if (scInstalledPath == "not_install" && (checkResult.isEmpty)) { @@ -158,8 +148,11 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { } // ignore: avoid_build_context_in_providers - Future _checkGameRunningLog(BuildContext context, String scInstalledPath, - List> checkResult) async { + Future _checkGameRunningLog( + BuildContext context, + String scInstalledPath, + List> checkResult, + ) async { if (scInstalledPath == "not_install") return; final lastScreenInfo = S.current.doctor_action_tip_checking_game_log; state = state.copyWith(lastScreenInfo: lastScreenInfo); @@ -168,28 +161,27 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { final info = SCLoggerHelper.getGameRunningLogInfo(logs); if (info != null) { if (info.key != "_") { - checkResult.add(MapEntry( - S.current.doctor_action_info_game_abnormal_exit(info.key), - info.value)); + checkResult.add(MapEntry(S.current.doctor_action_info_game_abnormal_exit(info.key), info.value)); } else { - checkResult.add(MapEntry( + checkResult.add( + MapEntry( S.current.doctor_action_info_game_abnormal_exit_unknown, - S.current.doctor_action_info_info_feedback(info.value))); + S.current.doctor_action_info_info_feedback(info.value), + ), + ); } } } // ignore: avoid_build_context_in_providers - Future _checkEAC(BuildContext context, String scInstalledPath, - List> checkResult) async { + Future _checkEAC(BuildContext context, String scInstalledPath, List> checkResult) async { if (scInstalledPath == "not_install") return; final lastScreenInfo = S.current.doctor_action_info_checking_eac; state = state.copyWith(lastScreenInfo: lastScreenInfo); final eacPath = "$scInstalledPath\\EasyAntiCheat"; final eacJsonPath = "$eacPath\\Settings.json"; - if (!await Directory(eacPath).exists() || - !await File(eacJsonPath).exists()) { + if (!await Directory(eacPath).exists() || !await File(eacJsonPath).exists()) { checkResult.add(const MapEntry("eac_file_miss", "")); return; } @@ -212,17 +204,18 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { final _cnExp = RegExp(r"[^\x00-\xff]"); // ignore: avoid_build_context_in_providers - Future _checkPreInstall(BuildContext context, String scInstalledPath, - List> checkResult) async { + Future _checkPreInstall( + BuildContext context, + String scInstalledPath, + List> checkResult, + ) async { final lastScreenInfo = S.current.doctor_action_info_checking_runtime; state = state.copyWith(lastScreenInfo: lastScreenInfo); if (!(Platform.operatingSystemVersion.contains("Windows 10") || Platform.operatingSystemVersion.contains("Windows 11"))) { - checkResult - .add(MapEntry("unSupport_system", Platform.operatingSystemVersion)); - final lastScreenInfo = S.current.doctor_action_result_info_unsupported_os( - Platform.operatingSystemVersion); + checkResult.add(MapEntry("unSupport_system", Platform.operatingSystemVersion)); + final lastScreenInfo = S.current.doctor_action_result_info_unsupported_os(Platform.operatingSystemVersion); state = state.copyWith(lastScreenInfo: lastScreenInfo); await showToast(context, lastScreenInfo); } @@ -236,12 +229,10 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { if (ramSize < 16) { checkResult.add(MapEntry("low_ram", "$ramSize")); } - state = state.copyWith( - lastScreenInfo: S.current.doctor_action_info_checking_install_info); + state = state.copyWith(lastScreenInfo: S.current.doctor_action_info_checking_install_info); // 检查安装分区 try { - final listData = await SCLoggerHelper.getGameInstallPath( - await SCLoggerHelper.getLauncherLogList() ?? []); + final listData = await SCLoggerHelper.getGameInstallPath(await SCLoggerHelper.getLauncherLogList() ?? []); final p = []; final checkedPath = []; for (var installPath in listData) { @@ -262,19 +253,16 @@ class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel { } } - // call check + // call check using Rust API for (var element in p) { - var result = await Process.run('powershell', [ - "(fsutil fsinfo sectorinfo $element: | Select-String 'PhysicalBytesPerSectorForPerformance').ToString().Split(':')[1].Trim()" - ]); - dPrint( - "fsutil info sector info: ->>> ${result.stdout.toString().trim()}"); - if (result.stderr == "") { - final rs = result.stdout.toString().trim(); - final physicalBytesPerSectorForPerformance = (int.tryParse(rs) ?? 0); + try { + final physicalBytesPerSectorForPerformance = await win32.getDiskPhysicalSectorSize(driveLetter: element); + dPrint("disk sector info for $element: -> $physicalBytesPerSectorForPerformance"); if (physicalBytesPerSectorForPerformance > 4096) { checkResult.add(MapEntry("nvme_PhysicalBytes", element)); } + } catch (e) { + dPrint("getDiskPhysicalSectorSize error: $e"); } } } catch (e) { diff --git a/lib/ui/home/game_doctor/game_doctor_ui_model.g.dart b/lib/ui/home/game_doctor/game_doctor_ui_model.g.dart index 3a5e722..89dafb4 100644 --- a/lib/ui/home/game_doctor/game_doctor_ui_model.g.dart +++ b/lib/ui/home/game_doctor/game_doctor_ui_model.g.dart @@ -42,7 +42,7 @@ final class HomeGameDoctorUIModelProvider } String _$homeGameDoctorUIModelHash() => - r'7035b501860e9d8c3fdfb91370311760120af115'; + r'8226e88797ecc68920f684a1dc072a340bca783a'; abstract class _$HomeGameDoctorUIModel extends $Notifier { HomeGameDoctorState build(); diff --git a/lib/ui/home/home_ui_model.dart b/lib/ui/home/home_ui_model.dart index 2b5938d..3d1eb05 100644 --- a/lib/ui/home/home_ui_model.dart +++ b/lib/ui/home/home_ui_model.dart @@ -12,7 +12,6 @@ import 'package:starcitizen_doctor/api/news_api.dart'; import 'package:starcitizen_doctor/common/conf/conf.dart'; import 'package:starcitizen_doctor/common/conf/url_conf.dart'; import 'package:starcitizen_doctor/common/helper/log_helper.dart'; -import 'package:starcitizen_doctor/common/helper/system_helper.dart'; import 'package:starcitizen_doctor/common/io/rs_http.dart'; import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32; import 'package:starcitizen_doctor/common/utils/async.dart'; @@ -323,7 +322,7 @@ class HomeUIModel extends _$HomeUIModel { if (ConstConf.isMSE) { if (state.isCurGameRunning) { - await Process.run(SystemHelper.powershellPath, ["ps \"StarCitizen\" | kill"]); + await win32.killProcessByName(processName: "StarCitizen"); return; } AnalyticsApi.touch("gameLaunch"); diff --git a/lib/ui/home/home_ui_model.g.dart b/lib/ui/home/home_ui_model.g.dart index 2b283cf..f0faba6 100644 --- a/lib/ui/home/home_ui_model.g.dart +++ b/lib/ui/home/home_ui_model.g.dart @@ -41,7 +41,7 @@ final class HomeUIModelProvider } } -String _$homeUIModelHash() => r'cc795e27213d02993459dd711de4a897c8491575'; +String _$homeUIModelHash() => r'c3affcfc99a0cd7e3587cc58f02dbdc24c4f8ae7'; abstract class _$HomeUIModel extends $Notifier { HomeUIModelState build(); diff --git a/lib/ui/settings/settings_ui_model.dart b/lib/ui/settings/settings_ui_model.dart index 95389ad..09970e3 100644 --- a/lib/ui/settings/settings_ui_model.dart +++ b/lib/ui/settings/settings_ui_model.dart @@ -9,6 +9,7 @@ import 'package:hive_ce/hive.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:starcitizen_doctor/common/conf/conf.dart'; import 'package:starcitizen_doctor/common/helper/system_helper.dart'; +import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32; import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/common/utils/provider.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; @@ -50,14 +51,15 @@ class SettingsUIModel extends _$SettingsUIModel { Future setGameLaunchECore(BuildContext context) async { final userBox = await Hive.openBox("app_conf"); - final defaultInput = - userBox.get("gameLaunch_eCore_count", defaultValue: "0"); + final defaultInput = userBox.get("gameLaunch_eCore_count", defaultValue: "0"); if (!context.mounted) return; - final input = await showInputDialogs(context, - title: S.current.setting_action_info_enter_cpu_core_to_ignore, - content: S.current.setting_action_info_cpu_core_tip, - initialValue: defaultInput, - inputFormatters: [FilteringTextInputFormatter.digitsOnly]); + final input = await showInputDialogs( + context, + title: S.current.setting_action_info_enter_cpu_core_to_ignore, + content: S.current.setting_action_info_cpu_core_tip, + initialValue: defaultInput, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + ); if (input == null) return; userBox.put("gameLaunch_eCore_count", input); _initState(); @@ -65,8 +67,7 @@ class SettingsUIModel extends _$SettingsUIModel { Future _updateGameLaunchECore() async { final userBox = await Hive.openBox("app_conf"); - final inputGameLaunchECore = - userBox.get("gameLaunch_eCore_count", defaultValue: "0"); + final inputGameLaunchECore = userBox.get("gameLaunch_eCore_count", defaultValue: "0"); state = state.copyWith(inputGameLaunchECore: inputGameLaunchECore); } @@ -102,9 +103,7 @@ class SettingsUIModel extends _$SettingsUIModel { if (r == null || r.files.firstOrNull?.path == null) return; final fileName = r.files.first.path!; dPrint(fileName); - final fileNameRegExp = RegExp( - r"^(.*\\StarCitizen\\.*\\)Bin64\\StarCitizen\.exe$", - caseSensitive: false); + final fileNameRegExp = RegExp(r"^(.*\\StarCitizen\\.*\\)Bin64\\StarCitizen\.exe$", caseSensitive: false); if (fileNameRegExp.hasMatch(fileName)) { RegExp pathRegex = RegExp(r"\\[^\\]+\\Bin64\\StarCitizen\.exe$"); String extractedPath = fileName.replaceFirst(pathRegex, ''); @@ -127,8 +126,7 @@ class SettingsUIModel extends _$SettingsUIModel { final confBox = await Hive.openBox("app_conf"); final customLauncherPath = confBox.get("custom_launcher_path"); final customGamePath = confBox.get("custom_game_path"); - state = state.copyWith( - customLauncherPath: customLauncherPath, customGamePath: customGamePath); + state = state.copyWith(customLauncherPath: customLauncherPath, customGamePath: customGamePath); } Future delName(String key) async { @@ -138,24 +136,21 @@ class SettingsUIModel extends _$SettingsUIModel { } Future _loadLocationCacheSize() async { - final len1 = await SystemHelper.getDirLen( - "${appGlobalState.applicationSupportDir}/Localizations"); - final len2 = await SystemHelper.getDirLen( - "${appGlobalState.applicationSupportDir}/launcher_enhance_data"); + final len1 = await SystemHelper.getDirLen("${appGlobalState.applicationSupportDir}/Localizations"); + final len2 = await SystemHelper.getDirLen("${appGlobalState.applicationSupportDir}/launcher_enhance_data"); final locationCacheSize = len1 + len2; state = state.copyWith(locationCacheSize: locationCacheSize); } Future cleanLocationCache(BuildContext context) async { final ok = await showConfirmDialogs( - context, - S.current.setting_action_info_confirm_clear_cache, - Text(S.current.setting_action_info_clear_cache_warning)); + context, + S.current.setting_action_info_confirm_clear_cache, + Text(S.current.setting_action_info_clear_cache_warning), + ); if (ok == true) { - final dir1 = - Directory("${appGlobalState.applicationSupportDir}/Localizations"); - final dir2 = Directory( - "${appGlobalState.applicationSupportDir}/launcher_enhance_data"); + final dir1 = Directory("${appGlobalState.applicationSupportDir}/Localizations"); + final dir2 = Directory("${appGlobalState.applicationSupportDir}/launcher_enhance_data"); if (!context.mounted) return; if (await dir1.exists()) { if (!context.mounted) return; @@ -172,36 +167,27 @@ class SettingsUIModel extends _$SettingsUIModel { Future addShortCut(BuildContext context) async { if (ConstConf.isMSE) { - showToast( - context, S.current.setting_action_info_microsoft_version_limitation); + showToast(context, S.current.setting_action_info_microsoft_version_limitation); await Future.delayed(const Duration(seconds: 1)); Process.run("explorer.exe", ["shell:AppsFolder"]); return; } dPrint(Platform.resolvedExecutable); - final shortCuntName = S.current.app_shortcut_name; - final script = """ - \$targetPath = "${Platform.resolvedExecutable}"; - \$shortcutPath = [System.IO.Path]::Combine([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::DesktopDirectory), "$shortCuntName"); - \$shell = New-Object -ComObject WScript.Shell - \$shortcut = \$shell.CreateShortcut(\$shortcutPath) - if (\$shortcut -eq \$null) { - Write-Host "Failed to create shortcut." - } else { - \$shortcut.TargetPath = \$targetPath - \$shortcut.Save() - Write-Host "Shortcut created successfully." + final shortcutName = S.current.app_shortcut_name; + try { + await win32.createDesktopShortcut(targetPath: Platform.resolvedExecutable, shortcutName: shortcutName); + if (!context.mounted) return; + showToast(context, S.current.setting_action_info_shortcut_created); + } catch (e) { + dPrint("createDesktopShortcut error: $e"); + if (!context.mounted) return; + showToast(context, "Failed to create shortcut: $e"); } -"""; - await Process.run(SystemHelper.powershellPath, [script]); - if (!context.mounted) return; - showToast(context, S.current.setting_action_info_shortcut_created); } Future _loadToolSiteMirrorState() async { final userBox = await Hive.openBox("app_conf"); - final isEnableToolSiteMirrors = - userBox.get("isEnableToolSiteMirrors", defaultValue: false); + final isEnableToolSiteMirrors = userBox.get("isEnableToolSiteMirrors", defaultValue: false); state = state.copyWith(isEnableToolSiteMirrors: isEnableToolSiteMirrors); } @@ -213,8 +199,7 @@ class SettingsUIModel extends _$SettingsUIModel { } Future showLogs() async { - SystemHelper.openDir(getDPrintFile()?.absolute.path.replaceAll("/", "\\"), - isFile: true); + SystemHelper.openDir(getDPrintFile()?.absolute.path.replaceAll("/", "\\"), isFile: true); } void onChangeUseInternalDNS(bool? b) { @@ -225,8 +210,7 @@ class SettingsUIModel extends _$SettingsUIModel { Future _loadUseInternalDNS() async { final userBox = await Hive.openBox("app_conf"); - final isUseInternalDNS = - userBox.get("isUseInternalDNS", defaultValue: false); + final isUseInternalDNS = userBox.get("isUseInternalDNS", defaultValue: false); state = state.copyWith(isUseInternalDNS: isUseInternalDNS); } @@ -238,8 +222,7 @@ class SettingsUIModel extends _$SettingsUIModel { Future _loadOnnxXnnPackState() async { final userBox = await Hive.openBox("app_conf"); - final isEnableOnnxXnnPack = - userBox.get("isEnableOnnxXnnPack", defaultValue: true); + final isEnableOnnxXnnPack = userBox.get("isEnableOnnxXnnPack", defaultValue: true); state = state.copyWith(isEnableOnnxXnnPack: isEnableOnnxXnnPack); } } diff --git a/lib/ui/settings/settings_ui_model.g.dart b/lib/ui/settings/settings_ui_model.g.dart index 3fd560d..a95376b 100644 --- a/lib/ui/settings/settings_ui_model.g.dart +++ b/lib/ui/settings/settings_ui_model.g.dart @@ -41,7 +41,7 @@ final class SettingsUIModelProvider } } -String _$settingsUIModelHash() => r'72947d5ed36290df865cb010b056dc632f5dccec'; +String _$settingsUIModelHash() => r'd34b1a2fac69d10f560d9a2e1a7431dd5a7954ca'; abstract class _$SettingsUIModel extends $Notifier { SettingsUIState build(); diff --git a/lib/ui/settings/upgrade_dialog.dart b/lib/ui/settings/upgrade_dialog.dart index 3c7f376..1a85d58 100644 --- a/lib/ui/settings/upgrade_dialog.dart +++ b/lib/ui/settings/upgrade_dialog.dart @@ -11,6 +11,7 @@ import 'package:starcitizen_doctor/app.dart'; import 'package:starcitizen_doctor/common/conf/conf.dart'; import 'package:starcitizen_doctor/common/conf/url_conf.dart'; import 'package:starcitizen_doctor/common/helper/system_helper.dart'; +import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32; import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; import 'package:html/parser.dart' as html_parser; @@ -44,39 +45,36 @@ class UpgradeDialogUI extends HookConsumerWidget { return Material( child: ContentDialog( - title: - Text(S.current.app_upgrade_title_new_version_found(targetVersion)), - constraints: - BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55), + title: Text(S.current.app_upgrade_title_new_version_found(targetVersion)), + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55), content: Column( mainAxisSize: MainAxisSize.min, children: [ Expanded( - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.only(left: 24, right: 24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - if (description.value == null) ...[ - Center( - child: Column( - children: [ - const ProgressRing(), - const SizedBox(height: 16), - Text(S.current - .app_upgrade_info_getting_new_version_details) - ], + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.only(left: 24, right: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + if (description.value == null) ...[ + Center( + child: Column( + children: [ + const ProgressRing(), + const SizedBox(height: 16), + Text(S.current.app_upgrade_info_getting_new_version_details), + ], + ), ), - ) - ] else - ...makeMarkdownView(description.value!, - attachmentsUrl: URLConf.giteaAttachmentsUrl), - ], + ] else + ...makeMarkdownView(description.value!, attachmentsUrl: URLConf.giteaAttachmentsUrl), + ], + ), ), ), - )), + ), if (isUsingDiversion.value) ...[ const SizedBox(height: 24), GestureDetector( @@ -84,13 +82,12 @@ class UpgradeDialogUI extends HookConsumerWidget { child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: Colors.white.withValues(alpha: .1), - borderRadius: BorderRadius.circular(7)), + color: Colors.white.withValues(alpha: .1), + borderRadius: BorderRadius.circular(7), + ), child: Text( S.current.app_upgrade_info_update_server_tip, - style: TextStyle( - fontSize: 14, - color: Colors.white.withValues(alpha: .7)), + style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .7)), ), ), ), @@ -99,14 +96,12 @@ class UpgradeDialogUI extends HookConsumerWidget { const SizedBox(height: 24), Row( children: [ - Text(progress.value == 100 - ? S.current.app_upgrade_info_installing - : S.current.app_upgrade_info_downloading( - progress.value.toStringAsFixed(2))), - Expanded( - child: ProgressBar( - value: progress.value == 100 ? null : progress.value, - )), + Text( + progress.value == 100 + ? S.current.app_upgrade_info_installing + : S.current.app_upgrade_info_downloading(progress.value.toStringAsFixed(2)), + ), + Expanded(child: ProgressBar(value: progress.value == 100 ? null : progress.value)), ], ), ], @@ -117,38 +112,40 @@ class UpgradeDialogUI extends HookConsumerWidget { : [ if (downloadUrl.value.isNotEmpty) FilledButton( - onPressed: () => _doUpgrade( - context, - appState, - isUpgrading, - appModel, - downloadUrl, - description, - isUsingDiversion, - progress), - child: Padding( - padding: const EdgeInsets.only( - top: 4, bottom: 4, left: 8, right: 8), - child: Text(S.current.app_upgrade_action_update_now), - )), + onPressed: () => _doUpgrade( + context, + appState, + isUpgrading, + appModel, + downloadUrl, + description, + isUsingDiversion, + progress, + ), + child: Padding( + padding: const EdgeInsets.only(top: 4, bottom: 4, left: 8, right: 8), + child: Text(S.current.app_upgrade_action_update_now), + ), + ), if (ConstConf.appVersionCode >= (minVersionCode ?? 0)) Button( - onPressed: () => _doCancel(context), - child: Padding( - padding: const EdgeInsets.only( - top: 4, bottom: 4, left: 8, right: 8), - child: Text(S.current.app_upgrade_action_next_time), - )), + onPressed: () => _doCancel(context), + child: Padding( + padding: const EdgeInsets.only(top: 4, bottom: 4, left: 8, right: 8), + child: Text(S.current.app_upgrade_action_next_time), + ), + ), ], ), ); } Future _getUpdateInfo( - BuildContext context, - String targetVersion, - ValueNotifier description, - ValueNotifier downloadUrl) async { + BuildContext context, + String targetVersion, + ValueNotifier description, + ValueNotifier downloadUrl, + ) async { try { final r = await Api.getAppReleaseDataByVersionName(targetVersion); description.value = r["body"]; @@ -193,19 +190,19 @@ class UpgradeDialogUI extends HookConsumerWidget { } Future _doUpgrade( - BuildContext context, - AppGlobalState appState, - ValueNotifier isUpgrading, - AppGlobalModel appModel, - ValueNotifier downloadUrl, - ValueNotifier description, - ValueNotifier isUsingDiversion, - ValueNotifier progress) async { + BuildContext context, + AppGlobalState appState, + ValueNotifier isUpgrading, + AppGlobalModel appModel, + ValueNotifier downloadUrl, + ValueNotifier description, + ValueNotifier isUsingDiversion, + ValueNotifier progress, + ) async { if (ConstConf.isMSE) { launchUrlString("ms-windows-store://pdp/?productid=9NF3SWFWNKL1"); await Future.delayed(const Duration(seconds: 3)); - if (ConstConf.appVersionCode < - (appState.networkVersionData?.minVersionCode ?? 0)) { + if (ConstConf.appVersionCode < (appState.networkVersionData?.minVersionCode ?? 0)) { exit(0); } if (!context.mounted) return; @@ -221,10 +218,10 @@ class UpgradeDialogUI extends HookConsumerWidget { final dio = Dio(); if (diversionDownloadUrl.isNotEmpty) { try { - final resp = await dio.head(diversionDownloadUrl, - options: Options( - sendTimeout: const Duration(seconds: 10), - receiveTimeout: const Duration(seconds: 10))); + final resp = await dio.head( + diversionDownloadUrl, + options: Options(sendTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 10)), + ); if (resp.statusCode == 200) { isUsingDiversion.value = true; url = diversionDownloadUrl; @@ -236,10 +233,13 @@ class UpgradeDialogUI extends HookConsumerWidget { dPrint("diversionDownloadUrl err:$e"); } } - await dio.download(url, fileName, - onReceiveProgress: (int count, int total) { - progress.value = (count / total) * 100; - }); + await dio.download( + url, + fileName, + onReceiveProgress: (int count, int total) { + progress.value = (count / total) * 100; + }, + ); } catch (_) { isUpgrading.value = false; progress.value = 0; @@ -249,11 +249,7 @@ class UpgradeDialogUI extends HookConsumerWidget { } try { - final r = await (Process.run( - SystemHelper.powershellPath, ["start", fileName, "/SILENT"])); - if (r.stderr.toString().isNotEmpty) { - throw r.stderr; - } + await win32.startProcess(program: fileName, args: ["/SILENT"]); exit(0); } catch (_) { isUpgrading.value = false; diff --git a/lib/ui/tools/dialogs/hosts_booster_dialog_ui.dart b/lib/ui/tools/dialogs/hosts_booster_dialog_ui.dart index 3177476..a2781bb 100644 --- a/lib/ui/tools/dialogs/hosts_booster_dialog_ui.dart +++ b/lib/ui/tools/dialogs/hosts_booster_dialog_ui.dart @@ -7,7 +7,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:starcitizen_doctor/api/analytics.dart'; import 'package:starcitizen_doctor/common/helper/system_helper.dart'; import 'package:starcitizen_doctor/common/io/rs_http.dart'; +import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32; import 'package:starcitizen_doctor/common/utils/async.dart'; +import 'package:starcitizen_doctor/common/utils/base_utils.dart'; import 'package:starcitizen_doctor/common/utils/log.dart'; class HostsBoosterDialogUI extends HookConsumerWidget { @@ -15,12 +17,8 @@ class HostsBoosterDialogUI extends HookConsumerWidget { static final _hostsMap = { "Recaptcha": ["www.recaptcha.net", "recaptcha.net"], - S.current.tools_hosts_info_rsi_official_website: [ - "robertsspaceindustries.com" - ], - S.current.tools_hosts_info_rsi_customer_service: [ - "support.robertsspaceindustries.com" - ], + S.current.tools_hosts_info_rsi_official_website: ["robertsspaceindustries.com"], + S.current.tools_hosts_info_rsi_customer_service: ["support.robertsspaceindustries.com"], }; @override @@ -31,9 +29,7 @@ class HostsBoosterDialogUI extends HookConsumerWidget { doHost(BuildContext context) async { if (workingMap.value.isEmpty) { - final hasTrue = - checkedMap.value.values.where((element) => element).firstOrNull != - null; + final hasTrue = checkedMap.value.values.where((element) => element).firstOrNull != null; if (!hasTrue) { for (var k in _hostsMap.keys) { checkedMap.value[k] = true; @@ -59,32 +55,29 @@ class HostsBoosterDialogUI extends HookConsumerWidget { }, []); return ContentDialog( - constraints: - BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55), + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55), title: Row( children: [ IconButton( - icon: const Icon( - FluentIcons.back, - size: 22, - ), - onPressed: - workingText.value.isEmpty ? Navigator.of(context).pop : null), + icon: const Icon(FluentIcons.back, size: 22), + onPressed: workingText.value.isEmpty ? Navigator.of(context).pop : null, + ), const SizedBox(width: 12), Text(S.current.tools_hosts_info_hosts_acceleration), const Spacer(), Button( - onPressed: () => _openHostsFile(context), - child: Padding( - padding: const EdgeInsets.all(3), - child: Row( - children: [ - const Icon(FluentIcons.open_file), - const SizedBox(width: 6), - Text(S.current.tools_hosts_info_open_hosts_file), - ], - ), - )) + onPressed: () => _openHostsFile(context), + child: Padding( + padding: const EdgeInsets.all(3), + child: Row( + children: [ + const Icon(FluentIcons.open_file), + const SizedBox(width: 6), + Text(S.current.tools_hosts_info_open_hosts_file), + ], + ), + ), + ), ], ), content: AnimatedSize( @@ -111,10 +104,8 @@ class HostsBoosterDialogUI extends HookConsumerWidget { padding: const EdgeInsets.all(6), physics: const NeverScrollableScrollPhysics(), itemBuilder: (BuildContext context, int index) { - final isEnable = - checkedMap.value[_hostsMap.keys.elementAt(index)] ?? false; - final workingState = - workingMap.value[_hostsMap.keys.elementAt(index)]; + final isEnable = checkedMap.value[_hostsMap.keys.elementAt(index)] ?? false; + final workingState = workingMap.value[_hostsMap.keys.elementAt(index)]; return Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.only(bottom: 12), @@ -124,23 +115,16 @@ class HostsBoosterDialogUI extends HookConsumerWidget { ), child: Row( children: [ - if (workingState == null) - Icon(FontAwesomeIcons.xmark, - size: 24, color: Colors.red), - if (workingState == 0) - const SizedBox( - width: 24, height: 24, child: ProgressRing()), - if (workingState == 1) - Icon(FontAwesomeIcons.check, - size: 24, color: Colors.green), + if (workingState == null) Icon(FontAwesomeIcons.xmark, size: 24, color: Colors.red), + if (workingState == 0) const SizedBox(width: 24, height: 24, child: ProgressRing()), + if (workingState == 1) Icon(FontAwesomeIcons.check, size: 24, color: Colors.green), const SizedBox(width: 24), const SizedBox(width: 12), Text(_hostsMap.keys.elementAt(index)), const Spacer(), ToggleSwitch( onChanged: (value) { - checkedMap.value[_hostsMap.keys.elementAt(index)] = - value; + checkedMap.value[_hostsMap.keys.elementAt(index)] = value; checkedMap.value = Map.from(checkedMap.value); }, checked: isEnable, @@ -156,8 +140,7 @@ class HostsBoosterDialogUI extends HookConsumerWidget { height: 86, child: Column( children: [ - const SizedBox( - height: 42, width: 42, child: ProgressRing()), + const SizedBox(height: 42, width: 42, child: ProgressRing()), const SizedBox(height: 12), Text(workingText.value), ], @@ -169,10 +152,8 @@ class HostsBoosterDialogUI extends HookConsumerWidget { child: FilledButton( onPressed: () => doHost(context), child: Padding( - padding: const EdgeInsets.only( - top: 3, bottom: 3, left: 12, right: 12), - child: Text( - S.current.tools_hosts_action_one_click_acceleration), + padding: const EdgeInsets.only(top: 3, bottom: 3, left: 12, right: 12), + child: Text(S.current.tools_hosts_action_one_click_acceleration), ), ), ), @@ -183,17 +164,19 @@ class HostsBoosterDialogUI extends HookConsumerWidget { } Future _openHostsFile(BuildContext context) async { - // 使用管理员权限调用记事本${S.current.tools_hosts_info_open_hosts_file} - Process.run(SystemHelper.powershellPath, [ - "-Command", - "Start-Process notepad.exe -Verb runAs -ArgumentList ${SystemHelper.getHostsFilePath()}" - // ignore: use_build_context_synchronously - ]).unwrap(context: context); + // 使用管理员权限调用记事本 + try { + await win32.runAsAdmin(program: "notepad.exe", args: SystemHelper.getHostsFilePath()); + } catch (e) { + if (!context.mounted) return; + showToast(context, "Failed to open hosts file: $e"); + } } Future> _doCheckDns( - ValueNotifier> workingMap, - ValueNotifier> checkedMap) async { + ValueNotifier> workingMap, + ValueNotifier> checkedMap, + ) async { Map result = {}; final trueLen = checkedMap.value.values.where((element) => element).length; if (trueLen == 0) { @@ -207,36 +190,37 @@ class HostsBoosterDialogUI extends HookConsumerWidget { } workingMap.value[siteName] = 0; workingMap.value = Map.from(workingMap.value); - RSHttp.dnsLookupIps(siteHost).then((ips) async { - int tryCount = ips.length; - try { - for (var ip in ips) { - final resp = - await RSHttp.head("https://$siteHost", withIpAddress: ip); - dPrint( - "[HostsBooster] host== $siteHost ip== $ip resp== ${resp.headers}"); - if (resp.headers.isNotEmpty) { - if (result[siteName] == null) { - result[siteName] = ip; - workingMap.value[siteName] = 1; - workingMap.value = Map.from(workingMap.value); - break; + RSHttp.dnsLookupIps(siteHost).then( + (ips) async { + int tryCount = ips.length; + try { + for (var ip in ips) { + final resp = await RSHttp.head("https://$siteHost", withIpAddress: ip); + dPrint("[HostsBooster] host== $siteHost ip== $ip resp== ${resp.headers}"); + if (resp.headers.isNotEmpty) { + if (result[siteName] == null) { + result[siteName] = ip; + workingMap.value[siteName] = 1; + workingMap.value = Map.from(workingMap.value); + break; + } } } + } catch (e) { + tryCount--; + if (tryCount == 0) { + workingMap.value[siteName] = null; + workingMap.value = Map.from(workingMap.value); + result[siteName] = ""; + } } - } catch (e) { - tryCount--; - if (tryCount == 0) { - workingMap.value[siteName] = null; - workingMap.value = Map.from(workingMap.value); - result[siteName] = ""; - } - } - }, onError: (e) { - workingMap.value[siteName] = null; - workingMap.value = Map.from(workingMap.value); - result[siteName] = ""; - }); + }, + onError: (e) { + workingMap.value[siteName] = null; + workingMap.value = Map.from(workingMap.value); + result[siteName] = ""; + }, + ); } while (true) { await Future.delayed(const Duration(milliseconds: 100)); @@ -265,16 +249,17 @@ class HostsBoosterDialogUI extends HookConsumerWidget { final domains = _hostsMap[kv.key] ?? []; for (var domain in domains) { if (kv.value != "") { - newHostsFileLines - .add("${kv.value} $domain #StarCitizenToolBox"); + newHostsFileLines.add("${kv.value} $domain #StarCitizenToolBox"); } } } await hostsFile.writeAsString(newHostsFileLines.join("\n"), flush: true); } - Future _readHostsState(ValueNotifier> workingMap, - ValueNotifier> checkedMap) async { + Future _readHostsState( + ValueNotifier> workingMap, + ValueNotifier> checkedMap, + ) async { workingMap.value.clear(); final hostsFile = File(SystemHelper.getHostsFilePath()); final hostsFileString = await hostsFile.readAsString(); diff --git a/rust/Cargo.lock b/rust/Cargo.lock index cdade69..25e45b2 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -4117,6 +4117,7 @@ dependencies = [ "walkdir", "win32job", "windows 0.62.2", + "wmi", "wry", ] @@ -5589,7 +5590,7 @@ dependencies = [ "webview2-com-sys", "windows 0.61.3", "windows-core 0.61.2", - "windows-implement", + "windows-implement 0.60.2", "windows-interface", ] @@ -5662,6 +5663,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" +dependencies = [ + "windows-collections 0.1.1", + "windows-core 0.60.1", + "windows-future 0.1.1", + "windows-link 0.1.3", + "windows-numerics 0.1.1", +] + [[package]] name = "windows" version = "0.61.3" @@ -5687,6 +5701,15 @@ dependencies = [ "windows-numerics 0.3.1", ] +[[package]] +name = "windows-collections" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5467f79cc1ba3f52ebb2ed41dbb459b8e7db636cc3429458d9a852e15bc24dec" +dependencies = [ + "windows-core 0.60.1", +] + [[package]] name = "windows-collections" version = "0.2.0" @@ -5705,13 +5728,26 @@ dependencies = [ "windows-core 0.62.2", ] +[[package]] +name = "windows-core" +version = "0.60.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" +dependencies = [ + "windows-implement 0.59.0", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.3.1", +] + [[package]] name = "windows-core" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement", + "windows-implement 0.60.2", "windows-interface", "windows-link 0.1.3", "windows-result 0.3.4", @@ -5724,13 +5760,23 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement", + "windows-implement 0.60.2", "windows-interface", "windows-link 0.2.1", "windows-result 0.4.1", "windows-strings 0.5.1", ] +[[package]] +name = "windows-future" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a787db4595e7eb80239b74ce8babfb1363d8e343ab072f2ffe901400c03349f0" +dependencies = [ + "windows-core 0.60.1", + "windows-link 0.1.3", +] + [[package]] name = "windows-future" version = "0.2.1" @@ -5753,6 +5799,17 @@ dependencies = [ "windows-threading 0.2.1", ] +[[package]] +name = "windows-implement" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "windows-implement" version = "0.60.2" @@ -5787,6 +5844,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005dea54e2f6499f2cee279b8f703b3cf3b5734a2d8d21867c8f44003182eeed" +dependencies = [ + "windows-core 0.60.1", + "windows-link 0.1.3", +] + [[package]] name = "windows-numerics" version = "0.2.0" @@ -5836,6 +5903,15 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -6203,6 +6279,21 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "wmi" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f902b4592b911109e7352bcfec7b754b07ec71e514d7dfa280eaef924c1cb08" +dependencies = [ + "chrono", + "futures", + "log", + "serde", + "thiserror 2.0.17", + "windows 0.60.0", + "windows-core 0.60.1", +] + [[package]] name = "writeable" version = "0.6.2" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index fa42165..d0a23ab 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -47,9 +47,20 @@ windows = { version = "0.62.2", features = [ "Win32_UI_WindowsAndMessaging", "Win32_System_Diagnostics_ToolHelp", "Win32_System_Threading", - "Win32_Foundation" + "Win32_Foundation", + "Win32_System_SystemInformation", + "Win32_System_Registry", + "Win32_Storage_FileSystem", + "Win32_UI_Shell", + "Win32_System_Com", + "Win32_System_Ole", + "Win32_System_Variant", + "Win32_Security", + "Win32_System_IO", + "Win32_System_Ioctl" ] } win32job = "2.0.3" +wmi = "0.15" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } diff --git a/rust/src/api/win32_api.rs b/rust/src/api/win32_api.rs index 338be7b..744ba81 100644 --- a/rust/src/api/win32_api.rs +++ b/rust/src/api/win32_api.rs @@ -26,6 +26,359 @@ pub fn send_notify( Ok(()) } +/// Get system memory size in GB +#[cfg(target_os = "windows")] +pub fn get_system_memory_size_gb() -> anyhow::Result { + use windows::Win32::System::SystemInformation::{GlobalMemoryStatusEx, MEMORYSTATUSEX}; + use std::mem; + + unsafe { + let mut mem_status: MEMORYSTATUSEX = mem::zeroed(); + mem_status.dwLength = mem::size_of::() as u32; + + GlobalMemoryStatusEx(&mut mem_status)?; + + // Convert bytes to GB + Ok(mem_status.ullTotalPhys / (1024 * 1024 * 1024)) + } +} + +#[cfg(not(target_os = "windows"))] +pub fn get_system_memory_size_gb() -> anyhow::Result { + Ok(0) +} + +/// Get number of logical processors +#[cfg(target_os = "windows")] +pub fn get_number_of_logical_processors() -> anyhow::Result { + use windows::Win32::System::SystemInformation::{GetSystemInfo, SYSTEM_INFO}; + use std::mem; + + unsafe { + let mut sys_info: SYSTEM_INFO = mem::zeroed(); + GetSystemInfo(&mut sys_info); + Ok(sys_info.dwNumberOfProcessors) + } +} + +#[cfg(not(target_os = "windows"))] +pub fn get_number_of_logical_processors() -> anyhow::Result { + Ok(0) +} + +/// System information struct +#[derive(Debug, Clone)] +pub struct SystemInfo { + pub os_name: String, + pub cpu_name: String, + pub gpu_info: String, + pub disk_info: String, +} + +/// Get all system information at once +#[cfg(target_os = "windows")] +pub fn get_system_info() -> anyhow::Result { + use wmi::{COMLibrary, WMIConnection}; + use serde::Deserialize; + + #[derive(Deserialize, Debug)] + #[serde(rename = "Caption")] + struct OsInfo { + #[serde(rename = "Caption")] + caption: Option, + } + + #[derive(Deserialize, Debug)] + struct CpuInfo { + #[serde(rename = "Name")] + name: Option, + } + + #[derive(Deserialize, Debug)] + struct GpuInfo { + #[serde(rename = "Name")] + name: Option, + #[serde(rename = "AdapterRAM")] + adapter_ram: Option, + } + + #[derive(Deserialize, Debug)] + struct DiskInfo { + #[serde(rename = "MediaType")] + media_type: Option, + #[serde(rename = "Model")] + model: Option, + #[serde(rename = "Size")] + size: Option, + } + + let com_con = COMLibrary::new()?; + let wmi_con = WMIConnection::new(com_con)?; + + // Get OS name using raw query + let os_name = match wmi_con.raw_query::("SELECT Caption FROM Win32_OperatingSystem") { + Ok(results) => results.first() + .and_then(|os| os.caption.clone()) + .unwrap_or_default(), + Err(_) => String::new(), + }; + + // Get CPU name using raw query + let cpu_name = match wmi_con.raw_query::("SELECT Name FROM Win32_Processor") { + Ok(results) => results.first() + .and_then(|cpu| cpu.name.clone()) + .unwrap_or_default(), + Err(_) => String::new(), + }; + + // Get GPU info using raw query + let gpu_info = match wmi_con.raw_query::("SELECT Name, AdapterRAM FROM Win32_VideoController") { + Ok(results) => results.iter() + .filter_map(|gpu| { + gpu.name.as_ref().map(|name| { + let vram_gb = gpu.adapter_ram.unwrap_or(0) / (1024 * 1024 * 1024); + format!("{} ({} GB)", name, vram_gb) + }) + }) + .collect::>() + .join("\n"), + Err(_) => String::new(), + }; + + // Get Disk info using raw query + let disk_info = match wmi_con.raw_query::("SELECT MediaType, Model, Size FROM Win32_DiskDrive") { + Ok(results) => results.iter() + .filter(|disk| disk.model.is_some()) + .map(|disk| { + let size_gb = disk.size.unwrap_or(0) / (1024 * 1024 * 1024); + format!("{}\t{}\t{} GB", + disk.media_type.as_deref().unwrap_or(""), + disk.model.as_deref().unwrap_or(""), + size_gb) + }) + .collect::>() + .join("\n"), + Err(_) => String::new(), + }; + + Ok(SystemInfo { + os_name, + cpu_name, + gpu_info, + disk_info, + }) +} + +#[cfg(not(target_os = "windows"))] +pub fn get_system_info() -> anyhow::Result { + Ok(SystemInfo { + os_name: String::new(), + cpu_name: String::new(), + gpu_info: String::new(), + disk_info: String::new(), + }) +} + +/// Get GPU info from registry (more accurate VRAM) +#[cfg(target_os = "windows")] +pub fn get_gpu_info_from_registry() -> anyhow::Result { + use windows::Win32::System::Registry::{ + RegOpenKeyExW, RegQueryValueExW, RegEnumKeyExW, RegCloseKey, + HKEY_LOCAL_MACHINE, KEY_READ, REG_VALUE_TYPE, + }; + use windows::core::{HSTRING, PCWSTR}; + use std::mem; + + let mut result = Vec::new(); + let base_path = HSTRING::from(r"SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}"); + + unsafe { + let mut hkey = std::mem::zeroed(); + if RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + PCWSTR(base_path.as_ptr()), + Some(0), + KEY_READ, + &mut hkey, + ).is_err() { + return Ok(String::new()); + } + + let mut index = 0u32; + let mut key_name = [0u16; 256]; + + loop { + let mut key_name_len = key_name.len() as u32; + if RegEnumKeyExW( + hkey, + index, + Some(windows::core::PWSTR::from_raw(key_name.as_mut_ptr())), + &mut key_name_len, + None, + None, + None, + None, + ).is_err() { + break; + } + + let subkey_name = String::from_utf16_lossy(&key_name[..key_name_len as usize]); + + // Only process numbered subkeys (0000, 0001, etc.) + if subkey_name.chars().all(|c| c.is_ascii_digit()) { + let full_path = HSTRING::from(format!( + r"SYSTEM\ControlSet001\Control\Class\{{4d36e968-e325-11ce-bfc1-08002be10318}}\{}", + subkey_name + )); + + let mut subkey = mem::zeroed(); + if RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + PCWSTR(full_path.as_ptr()), + Some(0), + KEY_READ, + &mut subkey, + ).is_ok() { + // Read adapter string + let adapter_name = HSTRING::from("HardwareInformation.AdapterString"); + let mut adapter_buffer = [0u16; 512]; + let mut adapter_size = (adapter_buffer.len() * 2) as u32; + let mut adapter_type = REG_VALUE_TYPE::default(); + + let adapter_string = if RegQueryValueExW( + subkey, + PCWSTR(adapter_name.as_ptr()), + None, + Some(&mut adapter_type), + Some(adapter_buffer.as_mut_ptr() as *mut u8), + Some(&mut adapter_size), + ).is_ok() { + let len = (adapter_size as usize / 2).saturating_sub(1); + String::from_utf16_lossy(&adapter_buffer[..len]) + } else { + String::new() + }; + + // Read memory size + let mem_name = HSTRING::from("HardwareInformation.qwMemorySize"); + let mut mem_value: u64 = 0; + let mut mem_size = std::mem::size_of::() as u32; + let mut mem_type = REG_VALUE_TYPE::default(); + + let vram_gb = if RegQueryValueExW( + subkey, + PCWSTR(mem_name.as_ptr()), + None, + Some(&mut mem_type), + Some(&mut mem_value as *mut u64 as *mut u8), + Some(&mut mem_size), + ).is_ok() { + mem_value / (1024 * 1024 * 1024) + } else { + 0 + }; + + if !adapter_string.is_empty() { + result.push(format!("Model: {}\nVRAM (GB): {}", adapter_string, vram_gb)); + } + + let _ = RegCloseKey(subkey); + } + } + + index += 1; + } + + let _ = RegCloseKey(hkey); + } + + Ok(result.join("\n\n")) +} + +#[cfg(not(target_os = "windows"))] +pub fn get_gpu_info_from_registry() -> anyhow::Result { + Ok(String::new()) +} + +/// Resolve shortcut (.lnk) file to get target path +#[cfg(target_os = "windows")] +pub fn resolve_shortcut(lnk_path: &str) -> anyhow::Result { + use windows::core::{HSTRING, Interface}; + use windows::Win32::System::Com::{ + CoCreateInstance, CoInitializeEx, CoUninitialize, + CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED, STGM_READ, + }; + use windows::Win32::UI::Shell::{IShellLinkW, ShellLink}; + use windows::Win32::System::Com::IPersistFile; + + unsafe { + // Initialize COM + let _ = CoInitializeEx(None, COINIT_APARTMENTTHREADED); + + let result = (|| -> anyhow::Result { + // Create ShellLink instance + let shell_link: IShellLinkW = CoCreateInstance( + &ShellLink, + None, + CLSCTX_INPROC_SERVER, + )?; + + // Get IPersistFile interface + let persist_file: IPersistFile = shell_link.cast()?; + + // Load the shortcut file + let lnk_path_w = HSTRING::from(lnk_path); + persist_file.Load(windows::core::PCWSTR(lnk_path_w.as_ptr()), STGM_READ)?; + + // Get target path + let mut path_buffer = [0u16; 260]; + shell_link.GetPath( + &mut path_buffer, + std::ptr::null_mut(), + 0, + )?; + + let path = String::from_utf16_lossy( + &path_buffer[..path_buffer.iter().position(|&c| c == 0).unwrap_or(path_buffer.len())] + ); + + Ok(path) + })(); + + CoUninitialize(); + result + } +} + +#[cfg(not(target_os = "windows"))] +pub fn resolve_shortcut(_: &str) -> anyhow::Result { + Ok(String::new()) +} + +/// Open file explorer and select file/folder +#[cfg(target_os = "windows")] +pub fn open_dir_with_explorer(path: &str, is_file: bool) -> anyhow::Result<()> { + use std::process::Command; + + if is_file { + Command::new("explorer.exe") + .args(["/select,", path]) + .spawn()?; + } else { + Command::new("explorer.exe") + .args(["/select,", path]) + .spawn()?; + } + + Ok(()) +} + +#[cfg(not(target_os = "windows"))] +pub fn open_dir_with_explorer(path: &str, is_file: bool) -> anyhow::Result<()> { + println!("open_dir_with_explorer (unix): {} is_file={}", path, is_file); + Ok(()) +} + #[cfg(target_os = "windows")] pub fn set_foreground_window(window_name: &str) -> anyhow::Result { @@ -174,4 +527,398 @@ fn get_process_path(pid: u32) -> Option { pub fn get_process_list_by_name(process_name: &str) -> anyhow::Result> { println!("get_process_list_by_name (unix): {}", process_name); Ok(Vec::new()) +} + +/// Kill processes by name +#[cfg(target_os = "windows")] +pub fn kill_process_by_name(process_name: &str) -> anyhow::Result { + use windows::Win32::Foundation::CloseHandle; + use windows::Win32::System::Threading::{OpenProcess, TerminateProcess, PROCESS_TERMINATE}; + + let processes = get_process_list_by_name(process_name)?; + let mut killed_count = 0u32; + + for process in processes { + unsafe { + if let Ok(h_process) = OpenProcess(PROCESS_TERMINATE, false, process.pid) { + if !h_process.is_invalid() { + if TerminateProcess(h_process, 0).is_ok() { + killed_count += 1; + } + let _ = CloseHandle(h_process); + } + } + } + } + + Ok(killed_count) +} + +#[cfg(not(target_os = "windows"))] +pub fn kill_process_by_name(process_name: &str) -> anyhow::Result { + println!("kill_process_by_name (unix): {}", process_name); + Ok(0) +} + +/// Get disk physical sector size for performance +#[cfg(target_os = "windows")] +pub fn get_disk_physical_sector_size(drive_letter: &str) -> anyhow::Result { + use windows::Win32::Storage::FileSystem::{ + CreateFileW, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING, + }; + use windows::Win32::System::IO::DeviceIoControl; + use windows::Win32::System::Ioctl::IOCTL_STORAGE_QUERY_PROPERTY; + use windows::Win32::Foundation::CloseHandle; + use windows::core::HSTRING; + use std::mem; + + // STORAGE_PROPERTY_QUERY structure + #[repr(C)] + struct StoragePropertyQuery { + property_id: u32, + query_type: u32, + additional_parameters: [u8; 1], + } + + // STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR structure + #[repr(C)] + struct StorageAccessAlignmentDescriptor { + version: u32, + size: u32, + bytes_per_cache_line: u32, + bytes_offset_for_cache_alignment: u32, + bytes_per_logical_sector: u32, + bytes_per_physical_sector: u32, + bytes_offset_for_sector_alignment: u32, + } + + let drive_path = format!(r"\\.\{}:", drive_letter.chars().next().unwrap_or('C')); + let drive_path_w = HSTRING::from(&drive_path); + + unsafe { + let handle = CreateFileW( + &drive_path_w, + 0, // No access needed, just query + FILE_SHARE_READ | FILE_SHARE_WRITE, + None, + OPEN_EXISTING, + windows::Win32::Storage::FileSystem::FILE_FLAGS_AND_ATTRIBUTES(0), + None, + )?; + + if handle.is_invalid() { + return Err(anyhow::anyhow!("Failed to open drive")); + } + + // StorageAccessAlignmentProperty = 6 + let query = StoragePropertyQuery { + property_id: 6, + query_type: 0, // PropertyStandardQuery + additional_parameters: [0], + }; + + let mut descriptor: StorageAccessAlignmentDescriptor = mem::zeroed(); + let mut bytes_returned: u32 = 0; + + let result = DeviceIoControl( + handle, + IOCTL_STORAGE_QUERY_PROPERTY, + Some(&query as *const _ as *const std::ffi::c_void), + mem::size_of::() as u32, + Some(&mut descriptor as *mut _ as *mut std::ffi::c_void), + mem::size_of::() as u32, + Some(&mut bytes_returned), + None, + ); + + let _ = CloseHandle(handle); + + if result.is_ok() { + Ok(descriptor.bytes_per_physical_sector) + } else { + Err(anyhow::anyhow!("DeviceIoControl failed")) + } + } +} + +#[cfg(not(target_os = "windows"))] +pub fn get_disk_physical_sector_size(drive_letter: &str) -> anyhow::Result { + println!("get_disk_physical_sector_size (unix): {}", drive_letter); + Ok(0) +} + +/// Create a desktop shortcut +#[cfg(target_os = "windows")] +pub fn create_desktop_shortcut(target_path: &str, shortcut_name: &str) -> anyhow::Result<()> { + use windows::core::{HSTRING, Interface, BSTR}; + use windows::Win32::System::Com::{ + CoCreateInstance, CoInitializeEx, CoUninitialize, + CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED, + }; + use windows::Win32::UI::Shell::{IShellLinkW, ShellLink, SHGetKnownFolderPath, FOLDERID_Desktop}; + use windows::Win32::System::Com::IPersistFile; + + unsafe { + let _ = CoInitializeEx(None, COINIT_APARTMENTTHREADED); + + let result = (|| -> anyhow::Result<()> { + // Get desktop path + let desktop_path = SHGetKnownFolderPath(&FOLDERID_Desktop, windows::Win32::UI::Shell::KNOWN_FOLDER_FLAG(0), None)?; + let desktop_str = desktop_path.to_string()?; + + // Create ShellLink instance + let shell_link: IShellLinkW = CoCreateInstance( + &ShellLink, + None, + CLSCTX_INPROC_SERVER, + )?; + + // Set target path + let target_w = HSTRING::from(target_path); + shell_link.SetPath(&target_w)?; + + // Get IPersistFile interface + let persist_file: IPersistFile = shell_link.cast()?; + + // Create shortcut file path + let shortcut_path = format!("{}\\{}", desktop_str, shortcut_name); + let shortcut_w = BSTR::from(&shortcut_path); + + // Save shortcut + persist_file.Save(windows::core::PCWSTR(shortcut_w.as_ptr()), true)?; + + Ok(()) + })(); + + CoUninitialize(); + result + } +} + +#[cfg(not(target_os = "windows"))] +pub fn create_desktop_shortcut(target_path: &str, shortcut_name: &str) -> anyhow::Result<()> { + println!("create_desktop_shortcut (unix): {} -> {}", target_path, shortcut_name); + Ok(()) +} + +/// Run a program with admin privileges (UAC) +#[cfg(target_os = "windows")] +pub fn run_as_admin(program: &str, args: &str) -> anyhow::Result<()> { + use windows::core::HSTRING; + use windows::Win32::UI::Shell::ShellExecuteW; + use windows::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL; + + let operation = HSTRING::from("runas"); + let file = HSTRING::from(program); + let parameters = HSTRING::from(args); + + unsafe { + let result = ShellExecuteW( + None, + &operation, + &file, + ¶meters, + None, + SW_SHOWNORMAL, + ); + + // ShellExecuteW returns a value > 32 on success + if result.0 as usize > 32 { + Ok(()) + } else { + Err(anyhow::anyhow!("ShellExecuteW failed with code: {}", result.0 as usize)) + } + } +} + +#[cfg(not(target_os = "windows"))] +pub fn run_as_admin(program: &str, args: &str) -> anyhow::Result<()> { + println!("run_as_admin (unix): {} {}", program, args); + Ok(()) +} + +/// Start a program (without waiting) +#[cfg(target_os = "windows")] +pub fn start_process(program: &str, args: Vec) -> anyhow::Result<()> { + use std::process::Command; + + Command::new(program) + .args(&args) + .spawn()?; + + Ok(()) +} + +#[cfg(not(target_os = "windows"))] +pub fn start_process(program: &str, args: Vec) -> anyhow::Result<()> { + println!("start_process (unix): {} {:?}", program, args); + Ok(()) +} + +// ============== NVME Patch Functions ============== +#[cfg(target_os = "windows")] +const NVME_REGISTRY_PATH: &str = r"SYSTEM\CurrentControlSet\Services\stornvme\Parameters\Device"; +#[cfg(target_os = "windows")] +const NVME_VALUE_NAME: &str = "ForcedPhysicalSectorSizeInBytes"; + +/// Check if NVME patch is applied +#[cfg(target_os = "windows")] +pub fn check_nvme_patch_status() -> anyhow::Result { + use windows::Win32::System::Registry::{ + RegOpenKeyExW, RegQueryValueExW, RegCloseKey, + HKEY_LOCAL_MACHINE, KEY_READ, REG_VALUE_TYPE, + }; + use windows::core::{HSTRING, PCWSTR}; + + unsafe { + let path = HSTRING::from(NVME_REGISTRY_PATH); + let mut hkey = std::mem::zeroed(); + + // Try to open the registry key + if RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + PCWSTR(path.as_ptr()), + Some(0), + KEY_READ, + &mut hkey, + ).is_err() { + return Ok(false); + } + + // Query the value + let value_name = HSTRING::from(NVME_VALUE_NAME); + let mut buffer = [0u8; 1024]; + let mut size = buffer.len() as u32; + let mut value_type = REG_VALUE_TYPE::default(); + + let result = if RegQueryValueExW( + hkey, + PCWSTR(value_name.as_ptr()), + None, + Some(&mut value_type), + Some(buffer.as_mut_ptr()), + Some(&mut size), + ).is_ok() { + // Check if the value contains "* 4095" + // REG_MULTI_SZ is stored as null-terminated wide strings + let data = String::from_utf16_lossy( + std::slice::from_raw_parts(buffer.as_ptr() as *const u16, size as usize / 2) + ); + data.contains("* 4095") + } else { + false + }; + + let _ = RegCloseKey(hkey); + Ok(result) + } +} + +#[cfg(not(target_os = "windows"))] +pub fn check_nvme_patch_status() -> anyhow::Result { + Ok(false) +} + +/// Add NVME patch to registry +#[cfg(target_os = "windows")] +pub fn add_nvme_patch() -> anyhow::Result<()> { + use windows::Win32::System::Registry::{ + RegOpenKeyExW, RegSetValueExW, RegCloseKey, + HKEY_LOCAL_MACHINE, KEY_WRITE, REG_MULTI_SZ, + }; + use windows::core::{HSTRING, PCWSTR}; + + unsafe { + let path = HSTRING::from(NVME_REGISTRY_PATH); + let mut hkey = std::mem::zeroed(); + + // Open the registry key with write access + let open_result = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + PCWSTR(path.as_ptr()), + Some(0), + KEY_WRITE, + &mut hkey, + ); + if open_result.is_err() { + return Err(anyhow::anyhow!("Failed to open registry key: {:?}", open_result)); + } + + // Prepare the value: "* 4095" as REG_MULTI_SZ (double null terminated) + let value_str = "* 4095\0\0"; + let value_wide: Vec = value_str.encode_utf16().collect(); + let value_name = HSTRING::from(NVME_VALUE_NAME); + + let result = RegSetValueExW( + hkey, + PCWSTR(value_name.as_ptr()), + Some(0), + REG_MULTI_SZ, + Some(std::slice::from_raw_parts( + value_wide.as_ptr() as *const u8, + value_wide.len() * 2, + )), + ); + + let _ = RegCloseKey(hkey); + + if result.is_err() { + return Err(anyhow::anyhow!("Failed to set registry value: {:?}", result)); + } + Ok(()) + } +} + +#[cfg(not(target_os = "windows"))] +pub fn add_nvme_patch() -> anyhow::Result<()> { + Err(anyhow::anyhow!("NVME patch is only supported on Windows")) +} + +/// Remove NVME patch from registry +#[cfg(target_os = "windows")] +pub fn remove_nvme_patch() -> anyhow::Result<()> { + use windows::Win32::System::Registry::{ + RegOpenKeyExW, RegDeleteValueW, RegCloseKey, + HKEY_LOCAL_MACHINE, KEY_WRITE, + }; + use windows::Win32::Foundation::ERROR_FILE_NOT_FOUND; + use windows::core::{HSTRING, PCWSTR}; + + unsafe { + let path = HSTRING::from(NVME_REGISTRY_PATH); + let mut hkey = std::mem::zeroed(); + + // Open the registry key with write access + let open_result = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + PCWSTR(path.as_ptr()), + Some(0), + KEY_WRITE, + &mut hkey, + ); + if open_result.is_err() { + return Err(anyhow::anyhow!("Failed to open registry key: {:?}", open_result)); + } + + let value_name = HSTRING::from(NVME_VALUE_NAME); + + let result = RegDeleteValueW( + hkey, + PCWSTR(value_name.as_ptr()), + ); + + let _ = RegCloseKey(hkey); + + // It's OK if the value doesn't exist + if result.is_err() && result != ERROR_FILE_NOT_FOUND { + return Err(anyhow::anyhow!("Failed to delete registry value: {:?}", result)); + } + + Ok(()) + } +} + +#[cfg(not(target_os = "windows"))] +pub fn remove_nvme_patch() -> anyhow::Result<()> { + Err(anyhow::anyhow!("NVME patch is only supported on Windows")) } \ No newline at end of file diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index e91ad96..e4d586e 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -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 = -1082688871; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1161621087; // Section: executor @@ -45,6 +45,48 @@ flutter_rust_bridge::frb_generated_default_handler!(); // Section: wire_funcs +fn wire__crate__api__win32_api__add_nvme_patch_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "add_nvme_patch", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::api::win32_api::add_nvme_patch()?; + Ok(output_ok) + })(), + ) + } + }, + ) +} +fn wire__crate__api__win32_api__check_nvme_patch_status_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "check_nvme_patch_status", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::api::win32_api::check_nvme_patch_status()?; + Ok(output_ok) + })(), + ) + } + }, + ) +} fn wire__crate__api__ort_api__clear_all_models_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ) { @@ -66,6 +108,34 @@ fn wire__crate__api__ort_api__clear_all_models_impl( }, ) } +fn wire__crate__api__win32_api__create_desktop_shortcut_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + target_path: impl CstDecode, + shortcut_name: impl CstDecode, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "create_desktop_shortcut", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_target_path = target_path.cst_decode(); + let api_shortcut_name = shortcut_name.cst_decode(); + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::api::win32_api::create_desktop_shortcut( + &api_target_path, + &api_shortcut_name, + )?; + Ok(output_ok) + })(), + ) + } + }, + ) +} fn wire__crate__api__http_api__dns_lookup_ips_impl( port_: flutter_rust_bridge::for_generated::MessagePort, host: impl CstDecode, @@ -156,6 +226,31 @@ fn wire__crate__api__http_api__fetch_impl( }, ) } +fn wire__crate__api__win32_api__get_disk_physical_sector_size_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + drive_letter: impl CstDecode, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "get_disk_physical_sector_size", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_drive_letter = drive_letter.cst_decode(); + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::api::win32_api::get_disk_physical_sector_size( + &api_drive_letter, + )?; + Ok(output_ok) + })(), + ) + } + }, + ) +} fn wire__crate__api__http_api__get_faster_url_impl( port_: flutter_rust_bridge::for_generated::MessagePort, urls: impl CstDecode>, @@ -183,6 +278,48 @@ fn wire__crate__api__http_api__get_faster_url_impl( }, ) } +fn wire__crate__api__win32_api__get_gpu_info_from_registry_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "get_gpu_info_from_registry", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::api::win32_api::get_gpu_info_from_registry()?; + Ok(output_ok) + })(), + ) + } + }, + ) +} +fn wire__crate__api__win32_api__get_number_of_logical_processors_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "get_number_of_logical_processors", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::api::win32_api::get_number_of_logical_processors()?; + Ok(output_ok) + })(), + ) + } + }, + ) +} fn wire__crate__api__win32_api__get_process_list_by_name_impl( port_: flutter_rust_bridge::for_generated::MessagePort, process_name: impl CstDecode, @@ -257,6 +394,72 @@ fn wire__crate__api__asar_api__get_rsi_launcher_asar_data_impl( }, ) } +fn wire__crate__api__win32_api__get_system_info_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "get_system_info", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::api::win32_api::get_system_info()?; + Ok(output_ok) + })(), + ) + } + }, + ) +} +fn wire__crate__api__win32_api__get_system_memory_size_gb_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "get_system_memory_size_gb", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::api::win32_api::get_system_memory_size_gb()?; + Ok(output_ok) + })(), + ) + } + }, + ) +} +fn wire__crate__api__win32_api__kill_process_by_name_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + process_name: impl CstDecode, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "kill_process_by_name", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_process_name = process_name.cst_decode(); + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = + crate::api::win32_api::kill_process_by_name(&api_process_name)?; + Ok(output_ok) + })(), + ) + } + }, + ) +} fn wire__crate__api__ort_api__load_translation_model_impl( port_: flutter_rust_bridge::for_generated::MessagePort, model_path: impl CstDecode, @@ -291,6 +494,32 @@ fn wire__crate__api__ort_api__load_translation_model_impl( }, ) } +fn wire__crate__api__win32_api__open_dir_with_explorer_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + path: impl CstDecode, + is_file: impl CstDecode, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "open_dir_with_explorer", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_path = path.cst_decode(); + let api_is_file = is_file.cst_decode(); + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = + crate::api::win32_api::open_dir_with_explorer(&api_path, api_is_file)?; + Ok(output_ok) + })(), + ) + } + }, + ) +} fn wire__crate__api__unp4k_api__p4k_close_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ) { @@ -436,6 +665,50 @@ fn wire__crate__api__unp4k_api__p4k_open_impl( }, ) } +fn wire__crate__api__win32_api__remove_nvme_patch_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "remove_nvme_patch", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::api::win32_api::remove_nvme_patch()?; + Ok(output_ok) + })(), + ) + } + }, + ) +} +fn wire__crate__api__win32_api__resolve_shortcut_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + lnk_path: impl CstDecode, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "resolve_shortcut", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_lnk_path = lnk_path.cst_decode(); + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = crate::api::win32_api::resolve_shortcut(&api_lnk_path)?; + Ok(output_ok) + })(), + ) + } + }, + ) +} fn wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js_impl( port_: flutter_rust_bridge::for_generated::MessagePort, that: impl CstDecode, @@ -466,6 +739,32 @@ fn wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js_impl( }, ) } +fn wire__crate__api__win32_api__run_as_admin_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + program: impl CstDecode, + args: impl CstDecode, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "run_as_admin", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_program = program.cst_decode(); + let api_args = args.cst_decode(); + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = + crate::api::win32_api::run_as_admin(&api_program, &api_args)?; + Ok(output_ok) + })(), + ) + } + }, + ) +} fn wire__crate__api__win32_api__send_notify_impl( port_: flutter_rust_bridge::for_generated::MessagePort, summary: impl CstDecode>, @@ -590,6 +889,32 @@ fn wire__crate__api__rs_process__start_impl( }, ) } +fn wire__crate__api__win32_api__start_process_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + program: impl CstDecode, + args: impl CstDecode>, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "start_process", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_program = program.cst_decode(); + let api_args = args.cst_decode(); + move |context| { + transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( + (move || { + let output_ok = + crate::api::win32_api::start_process(&api_program, api_args)?; + Ok(output_ok) + })(), + ) + } + }, + ) +} fn wire__crate__api__ort_api__translate_text_impl( port_: flutter_rust_bridge::for_generated::MessagePort, model_key: impl CstDecode, @@ -1451,6 +1776,22 @@ impl SseDecode for crate::http_package::RustHttpResponse { } } +impl SseDecode for crate::api::win32_api::SystemInfo { + // 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_osName = ::sse_decode(deserializer); + let mut var_cpuName = ::sse_decode(deserializer); + let mut var_gpuInfo = ::sse_decode(deserializer); + let mut var_diskInfo = ::sse_decode(deserializer); + return crate::api::win32_api::SystemInfo { + os_name: var_osName, + cpu_name: var_cpuName, + gpu_info: var_gpuInfo, + disk_info: var_diskInfo, + }; + } +} + impl SseDecode for u16 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1789,6 +2130,29 @@ impl flutter_rust_bridge::IntoIntoDart } } // Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::api::win32_api::SystemInfo { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.os_name.into_into_dart().into_dart(), + self.cpu_name.into_into_dart().into_dart(), + self.gpu_info.into_into_dart().into_dart(), + self.disk_info.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::api::win32_api::SystemInfo +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::api::win32_api::SystemInfo +{ + fn into_into_dart(self) -> crate::api::win32_api::SystemInfo { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::api::webview_api::WebViewConfiguration { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { [ @@ -2159,6 +2523,16 @@ impl SseEncode for crate::http_package::RustHttpResponse { } } +impl SseEncode for crate::api::win32_api::SystemInfo { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.os_name, serializer); + ::sse_encode(self.cpu_name, serializer); + ::sse_encode(self.gpu_info, serializer); + ::sse_encode(self.disk_info, serializer); + } +} + impl SseEncode for u16 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -2481,6 +2855,17 @@ mod io { } } } + impl CstDecode for wire_cst_system_info { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::api::win32_api::SystemInfo { + crate::api::win32_api::SystemInfo { + os_name: self.os_name.cst_decode(), + cpu_name: self.cpu_name.cst_decode(), + gpu_info: self.gpu_info.cst_decode(), + disk_info: self.disk_info.cst_decode(), + } + } + } impl CstDecode for wire_cst_web_view_configuration { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::api::webview_api::WebViewConfiguration { @@ -2637,6 +3022,21 @@ mod io { Self::new_with_null_ptr() } } + impl NewWithNullPtr for wire_cst_system_info { + fn new_with_null_ptr() -> Self { + Self { + os_name: core::ptr::null_mut(), + cpu_name: core::ptr::null_mut(), + gpu_info: core::ptr::null_mut(), + disk_info: core::ptr::null_mut(), + } + } + } + impl Default for wire_cst_system_info { + fn default() -> Self { + Self::new_with_null_ptr() + } + } impl NewWithNullPtr for wire_cst_web_view_configuration { fn new_with_null_ptr() -> Self { Self { @@ -2685,6 +3085,20 @@ mod io { } } + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__add_nvme_patch( + port_: i64, + ) { + wire__crate__api__win32_api__add_nvme_patch_impl(port_) + } + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__check_nvme_patch_status( + port_: i64, + ) { + wire__crate__api__win32_api__check_nvme_patch_status_impl(port_) + } + #[unsafe(no_mangle)] pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__ort_api__clear_all_models( port_: i64, @@ -2692,6 +3106,15 @@ mod io { wire__crate__api__ort_api__clear_all_models_impl(port_) } + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__create_desktop_shortcut( + port_: i64, + target_path: *mut wire_cst_list_prim_u_8_strict, + shortcut_name: *mut wire_cst_list_prim_u_8_strict, + ) { + wire__crate__api__win32_api__create_desktop_shortcut_impl(port_, target_path, shortcut_name) + } + #[unsafe(no_mangle)] pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__http_api__dns_lookup_ips( port_: i64, @@ -2729,6 +3152,14 @@ mod io { ) } + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_disk_physical_sector_size( + port_: i64, + drive_letter: *mut wire_cst_list_prim_u_8_strict, + ) { + wire__crate__api__win32_api__get_disk_physical_sector_size_impl(port_, drive_letter) + } + #[unsafe(no_mangle)] pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__http_api__get_faster_url( port_: i64, @@ -2738,6 +3169,20 @@ mod io { wire__crate__api__http_api__get_faster_url_impl(port_, urls, path_suffix) } + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_gpu_info_from_registry( + port_: i64, + ) { + wire__crate__api__win32_api__get_gpu_info_from_registry_impl(port_) + } + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_number_of_logical_processors( + port_: i64, + ) { + wire__crate__api__win32_api__get_number_of_logical_processors_impl(port_) + } + #[unsafe(no_mangle)] pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_process_list_by_name( port_: i64, @@ -2762,6 +3207,28 @@ mod io { wire__crate__api__asar_api__get_rsi_launcher_asar_data_impl(port_, asar_path) } + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_system_info( + port_: i64, + ) { + wire__crate__api__win32_api__get_system_info_impl(port_) + } + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_system_memory_size_gb( + port_: i64, + ) { + wire__crate__api__win32_api__get_system_memory_size_gb_impl(port_) + } + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__kill_process_by_name( + port_: i64, + process_name: *mut wire_cst_list_prim_u_8_strict, + ) { + wire__crate__api__win32_api__kill_process_by_name_impl(port_, process_name) + } + #[unsafe(no_mangle)] pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__ort_api__load_translation_model( port_: i64, @@ -2779,6 +3246,15 @@ mod io { ) } + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__open_dir_with_explorer( + port_: i64, + path: *mut wire_cst_list_prim_u_8_strict, + is_file: bool, + ) { + wire__crate__api__win32_api__open_dir_with_explorer_impl(port_, path, is_file) + } + #[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_) @@ -2823,6 +3299,21 @@ mod io { wire__crate__api__unp4k_api__p4k_open_impl(port_, p4k_path) } + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__remove_nvme_patch( + port_: i64, + ) { + wire__crate__api__win32_api__remove_nvme_patch_impl(port_) + } + + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__resolve_shortcut( + port_: i64, + lnk_path: *mut wire_cst_list_prim_u_8_strict, + ) { + wire__crate__api__win32_api__resolve_shortcut_impl(port_, lnk_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, @@ -2832,6 +3323,15 @@ mod io { wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js_impl(port_, that, content) } + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__run_as_admin( + port_: i64, + program: *mut wire_cst_list_prim_u_8_strict, + args: *mut wire_cst_list_prim_u_8_strict, + ) { + wire__crate__api__win32_api__run_as_admin_impl(port_, program, args) + } + #[unsafe(no_mangle)] pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__send_notify( port_: i64, @@ -2876,6 +3376,15 @@ mod io { ) } + #[unsafe(no_mangle)] + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__start_process( + port_: i64, + program: *mut wire_cst_list_prim_u_8_strict, + args: *mut wire_cst_list_String, + ) { + wire__crate__api__win32_api__start_process_impl(port_, program, args) + } + #[unsafe(no_mangle)] pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__ort_api__translate_text( port_: i64, @@ -3245,6 +3754,14 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_system_info { + os_name: *mut wire_cst_list_prim_u_8_strict, + cpu_name: *mut wire_cst_list_prim_u_8_strict, + gpu_info: *mut wire_cst_list_prim_u_8_strict, + disk_info: *mut wire_cst_list_prim_u_8_strict, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_web_view_configuration { title: *mut wire_cst_list_prim_u_8_strict, width: u32,