app/lib/ui/tools/tools_ui_model.dart

389 lines
14 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:starcitizen_doctor/api/analytics.dart';
import 'package:starcitizen_doctor/base/ui_model.dart';
import 'package:starcitizen_doctor/common/conf.dart';
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
import 'package:starcitizen_doctor/ui/tools/downloader/downloader_dialog_ui_model.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'downloader/downloader_dialog_ui.dart';
class ToolsUIModel extends BaseUIModel {
bool _working = false;
String scInstalledPath = "";
String rsiLauncherInstalledPath = "";
List<String> scInstallPaths = [];
List<String> rsiLauncherInstallPaths = [];
set working(bool b) {
_working = b;
notifyListeners();
}
bool get working => _working;
var items = <_ToolsItemData>[];
bool isItemLoading = false;
@override
Future loadData({bool skipPathScan = false}) async {
if (isItemLoading) return;
items.clear();
notifyListeners();
if (!skipPathScan) {
await reScanPath();
}
try {
items = [
_ToolsItemData(
"systeminfo",
"查看系统信息",
"查看系统关键信息,用于快速问诊 \n\n耗时操作,请耐心等待。",
const Icon(FluentIcons.system, size: 28),
onTap: _showSystemInfo,
),
_ToolsItemData(
"p4k_downloader",
"P4K 分流下载",
"使用星际公民中文百科提供的分流下载服务。 \n\n资源有限,请勿滥用。请确保您的硬盘拥有至少大于 200G 的可用空间。",
const Icon(FontAwesomeIcons.download, size: 28),
onTap: _downloadP4k,
),
_ToolsItemData(
"reinstall_eac",
"重装 EasyAntiCheat 反作弊",
"若您遇到 EAC 错误,且自动修复无效,请尝试使用此功能重装 EAC。",
const Icon(FluentIcons.game, size: 28),
onTap: _reinstallEAC,
),
_ToolsItemData(
"rsilauncher_admin_mode",
"RSI Launcher 管理员模式",
"在某些情况下 RSI启动器 无法正确获得管理员权限,您可尝试使用该功能以管理员模式运行启动器。",
const Icon(FluentIcons.admin, size: 28),
onTap: _adminRSILauncher,
)
];
isItemLoading = true;
notifyListeners();
items.addAll(await _addLogCard());
notifyListeners();
items.addAll(await _addNvmePatchCard());
notifyListeners();
items.add(await _addShaderCard());
isItemLoading = false;
notifyListeners();
} catch (e) {
showToast(context!, "初始化失败,请截图报告给开发者。$e");
}
notifyListeners();
}
Future<List<_ToolsItemData>> _addLogCard() async {
double logPathLen = 0;
try {
logPathLen =
(await File(await SCLoggerHelper.getLogFilePath() ?? "").length()) /
1024 /
1024;
} catch (_) {}
return [
_ToolsItemData(
"rsilauncher_log_select",
"RSI Launcher Log 查看",
"打开 RSI启动器 Log文件 所在文件夹",
const Icon(FontAwesomeIcons.bookBible, size: 28),
onTap: _selectLog,
),
_ToolsItemData(
"rsilauncher_log_fix",
"RSI Launcher Log 修复",
"在某些情况下 RSI启动器 的 log 文件会损坏,导致无法完成问题扫描,使用此工具清理损坏的 log 文件。\n\n当前日志文件大小:${(logPathLen.toStringAsFixed(4))} MB",
const Icon(FontAwesomeIcons.bookBible, size: 28),
onTap: _rsiLogFix,
),
];
}
Future<List<_ToolsItemData>> _addNvmePatchCard() async {
final nvmePatchStatus = await SystemHelper.checkNvmePatchStatus();
return [
if (nvmePatchStatus)
_ToolsItemData(
"remove_nvme_settings",
"移除 nvme 注册表补丁",
"若您使用 nvme 补丁出现问题,请运行此工具。(可能导致游戏 安装/更新 不可用。)\n\n当前补丁状态:${(nvmePatchStatus) ? "已安装" : "未安装"}",
const Icon(FluentIcons.hard_drive, size: 28),
onTap: nvmePatchStatus
? () async {
working = true;
await SystemHelper.doRemoveNvmePath();
working = false;
showToast(context!, "已移除,重启生效!");
loadData(skipPathScan: true);
}
: null,
),
if (!nvmePatchStatus)
_ToolsItemData(
"add_nvme_settings",
"写入 nvme 注册表补丁",
"手动写入NVM补丁该功能仅在您知道自己在作什么的情况下使用",
const Icon(FontAwesomeIcons.cashRegister, size: 28),
onTap: () async {
working = true;
final r = await SystemHelper.addNvmePatch();
if (r == "") {
showToast(context!,
"修复成功,请尝试重启后继续安装游戏! 若注册表修改操作导致其他软件出现兼容问题,请使用 工具 中的 NVME 注册表清理。");
notifyListeners();
} else {
showToast(context!, "修复失败,$r");
}
working = false;
loadData(skipPathScan: true);
},
)
];
}
Future<_ToolsItemData> _addShaderCard() async {
final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath();
return _ToolsItemData(
"clean_shaders",
"清理着色器缓存",
"若游戏画面出现异常或版本更新后可使用本工具清理过期的着色器当大于500M时建议清理 \n\n缓存大小:${((await SystemHelper.getDirLen(gameShaderCachePath ?? "", skipPath: [
"$gameShaderCachePath\\Crashes"
])) / 1024 / 1024).toStringAsFixed(4)} MB",
const Icon(FontAwesomeIcons.shapes, size: 28),
onTap: _cleanShaderCache,
);
}
/// ---------------------------- func -------------------------------------------------------
/// -----------------------------------------------------------------------------------------
/// -----------------------------------------------------------------------------------------
/// -----------------------------------------------------------------------------------------
Future<void> reScanPath() async {
scInstallPaths.clear();
rsiLauncherInstallPaths.clear();
scInstalledPath = "";
rsiLauncherInstalledPath = "";
try {
rsiLauncherInstalledPath = await SystemHelper.getRSILauncherPath();
rsiLauncherInstallPaths.add(rsiLauncherInstalledPath);
final listData = await SCLoggerHelper.getLauncherLogList();
if (listData == null) {
return;
}
scInstallPaths = await SCLoggerHelper.getGameInstallPath(listData,
checkExists: false, withVersion: ["LIVE", "PTU", "EPTU"]);
if (scInstallPaths.isNotEmpty) {
scInstalledPath = scInstallPaths.first;
}
} catch (e) {
dPrint(e);
showToast(context!, "解析 log 文件失败!\n请尝试使用 RSI Launcher log 修复 工具!");
}
notifyListeners();
if (rsiLauncherInstalledPath == "") {
showToast(context!, "未找到 RSI 启动器,请尝试重新安装,或在设置中手动添加。");
}
if (scInstalledPath == "") {
showToast(context!, "未找到星际公民游戏安装位置,请至少完成一次游戏启动操作 或在设置中手动添加。");
}
}
/// 重装EAC
Future<void> _reinstallEAC() async {
if (scInstalledPath.isEmpty) {
showToast(context!, "该功能需要一个有效的游戏安装目录");
return;
}
working = true;
try {
final eacPath = "$scInstalledPath\\EasyAntiCheat";
final eacJsonPath = "$eacPath\\Settings.json";
if (await File(eacJsonPath).exists()) {
Map<String, String> envVars = Platform.environment;
final eacJsonData = await File(eacJsonPath).readAsString();
final Map eacJson = json.decode(eacJsonData);
final eacID = eacJson["productid"];
if (eacID != null) {
final eacCacheDir =
Directory("${envVars["appdata"]}\\EasyAntiCheat\\$eacID");
if (await eacCacheDir.exists()) {
await eacCacheDir.delete(recursive: true);
}
}
}
final dir = Directory(eacPath);
if (await dir.exists()) {
await dir.delete(recursive: true);
}
final eacLauncher = File("$scInstalledPath\\StarCitizen_Launcher.exe");
if (await eacLauncher.exists()) {
await eacLauncher.delete(recursive: true);
}
showToast(context!,
"已为您移除 EAC 文件,接下来将为您打开 RSI 启动器,请您前往 SETTINGS -> VERIFY 重装 EAC。");
_adminRSILauncher();
} catch (e) {
showToast(context!, "出现错误:$e");
}
working = false;
loadData(skipPathScan: true);
}
Future<String> getSystemInfo() async {
return "系统:${await SystemHelper.getSystemName()}\n\n"
"处理器:${await SystemHelper.getCpuName()}\n\n"
"内存大小:${await SystemHelper.getSystemMemorySizeGB()}GB\n\n"
"显卡信息:\n${await SystemHelper.getGpuInfo()}\n\n"
"硬盘信息:\n${await SystemHelper.getDiskInfo()}\n\n";
}
/// 管理员模式运行 RSI 启动器
Future _adminRSILauncher() async {
if (rsiLauncherInstalledPath == "") {
showToast(context!, "未找到 RSI 启动器目录,请您尝试手动操作。");
}
handleError(
() => SystemHelper.checkAndLaunchRSILauncher(rsiLauncherInstalledPath));
}
Future<void> _rsiLogFix() async {
working = true;
final path = await SCLoggerHelper.getLogFilePath();
if (!await File(path!).exists()) {
showToast(
context!, "日志文件不存在,请尝试进行一次游戏启动或游戏安装,并退出启动器,若无法解决问题,请尝试将启动器更新至最新版本!");
return;
}
try {
SystemHelper.killRSILauncher();
await File(path).delete(recursive: true);
showToast(context!, "清理完毕,请完成一次安装 / 游戏启动 操作。");
SystemHelper.checkAndLaunchRSILauncher(rsiLauncherInstalledPath);
} catch (_) {
showToast(context!, "清理失败,请手动移除,文件位置:$path");
}
working = false;
}
Future<void> _selectLog() async {
final path = await SCLoggerHelper.getLogFilePath();
if (path == null) return;
openDir(path);
}
openDir(path) async {
await Process.run(
SystemHelper.powershellPath, ["explorer.exe", "/select,\"$path\""]);
}
Future _showSystemInfo() async {
working = true;
final systemInfo = await getSystemInfo();
showDialog<String>(
context: context!,
builder: (context) => ContentDialog(
title: const Text('系统信息'),
content: Text(systemInfo),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * .65,
),
actions: [
FilledButton(
child: const Padding(
padding: EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8),
child: Text('关闭'),
),
onPressed: () => Navigator.pop(context),
),
],
),
);
working = false;
}
Future<void> _cleanShaderCache() async {
working = true;
final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath();
final l =
await Directory(gameShaderCachePath!).list(recursive: false).toList();
for (var value in l) {
if (value is Directory) {
if (!value.absolute.path.contains("Crashes")) {
await value.delete(recursive: true);
}
}
}
loadData(skipPathScan: true);
working = false;
}
Future<void> _downloadP4k() async {
var downloadUrl = AppConf.networkVersionData?.p4kDownloadUrl;
if (downloadUrl == null || downloadUrl.isEmpty) {
showToast(context!, "该功能维护中,请稍后再试!");
return;
}
if ((await SystemHelper.getPID("\"RSI Launcher\"")).isNotEmpty) {
showToast(context!, "RSI启动器正在运行请先关闭启动器再使用此功能",
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context!).size.width * .35));
return;
}
await showToast(
context!,
"P4k 是星际公民的核心游戏文件,高达近 100GB盒子提供的离线下载是为了帮助一些p4k文件下载超级慢的用户。"
"\n\n接下来会弹窗询问您保存位置(可以选择星际公民文件夹也可以选择别处),下载完成后请确保 P4K 文件夹位于 LIVE 文件夹内,之后使用星际公民启动器校验更新即可。");
AnalyticsApi.touch("p4k_download");
launchUrlString(
"https://citizenwiki.cn/SC%E6%B1%89%E5%8C%96%E7%9B%92%E5%AD%90#%E5%88%86%E6%B5%81%E4%B8%8B%E8%BD%BD%E6%95%99%E7%A8%8B");
final r = await showDialog(
context: context!,
dismissWithEsc: false,
builder: (context) {
return BaseUIContainer(
uiCreate: () => DownloaderDialogUI(),
modelCreate: () => DownloaderDialogUIModel(
"Data.p4k", scInstalledPath, downloadUrl,
showChangeSavePathDialog: true, threadCount: 10));
});
if (r != null) {
if (r == "cancel") {
return showToast(context!, "下载已取消,下载进度已保留,如果您无需恢复下载,请手动删除下载临时文件。");
} else if (r is DioException) {
showToast(context!, "下载失败:$r");
} else {
showToast(context!, "下载完毕,文件已保存到:$r");
}
}
}
}
class _ToolsItemData {
String key;
_ToolsItemData(this.key, this.name, this.infoString, this.icon, {this.onTap});
String name;
String infoString;
Widget icon;
AsyncCallback? onTap;
}