app/lib/ui/home/home_ui_model.dart

411 lines
14 KiB
Dart
Raw Normal View History

2023-10-09 09:32:07 +08:00
import 'dart:convert';
import 'dart:io';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:hive/hive.dart';
import 'package:starcitizen_doctor/api/api.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/data/app_placard_data.dart';
import 'package:starcitizen_doctor/ui/home/dialogs/md_content_dialog_ui.dart';
import 'package:starcitizen_doctor/ui/home/dialogs/md_content_dialog_ui_model.dart';
import 'package:starcitizen_doctor/ui/home/localization/localization_ui_model.dart';
import 'package:starcitizen_doctor/ui/home/performance/performance_ui_model.dart';
import 'package:starcitizen_doctor/ui/home/webview/webview.dart';
import 'package:starcitizen_doctor/ui/home/webview/webview_localization_capture_ui_model.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'localization/localization_ui.dart';
import 'performance/performance_ui.dart';
import 'webview/webview_localization_capture_ui.dart';
class HomeUIModel extends BaseUIModel {
var scInstalledPath = "not_install";
List<String> scInstallPaths = [];
String _lastScreenInfo = "";
String get lastScreenInfo => _lastScreenInfo;
bool isChecking = false;
bool isFixing = false;
String isFixingString = "";
set lastScreenInfo(String info) {
_lastScreenInfo = info;
notifyListeners();
}
List<MapEntry<String, String>>? checkResult;
final cnExp = RegExp(r"[^\x00-\xff]");
AppPlacardData? appPlacardData;
@override
Future loadData() async {
if (AppConf.networkVersionData == null) return;
try {
final r = await Api.getAppPlacard();
final box = await Hive.openBox("app_conf");
final version = box.get("close_placard", defaultValue: "");
if (r.enable != true) return;
if (r.alwaysShow != true && version == r.version) return;
appPlacardData = r;
notifyListeners();
} catch (e) {
dPrint(e);
}
}
@override
void initModel() {
reScanPath();
super.initModel();
}
Future<void> reScanPath() async {
scInstallPaths.clear();
scInstalledPath = "not_install";
lastScreenInfo = "正在扫描 ...";
try {
final listData = await SCLoggerHelper.getLauncherLogList();
if (listData == null) {
lastScreenInfo = "获取log失败";
return;
}
scInstallPaths = await SCLoggerHelper.getGameInstallPath(listData,
withVersion: ["LIVE", "PTU", "EPTU"], checkExists: true);
if (scInstallPaths.isNotEmpty) {
scInstalledPath = scInstallPaths.first;
}
lastScreenInfo = "扫描完毕,共找到 ${scInstallPaths.length} 个有效安装目录";
} catch (e) {
lastScreenInfo = "解析 log 文件失败!";
showToast(context!,
"解析 log 文件失败! \n请关闭游戏退出RSI启动器后重试若仍有问题请使用工具箱中的 RSI Launcher log 修复。");
}
}
VoidCallback? doCheck() {
if (isChecking) return null;
return () async {
isChecking = true;
lastScreenInfo = "正在分析...";
await _statCheck();
isChecking = false;
notifyListeners();
};
}
Future _statCheck() async {
checkResult = [];
await _checkPreInstall();
await _checkEAC();
// TODO for debug
// checkResult?.add(const MapEntry("unSupport_system", "android"));
// checkResult?.add(const MapEntry("nvme_PhysicalBytes", "c"));
// checkResult?.add(const MapEntry("no_live_path", ""));
if (checkResult!.isEmpty) {
checkResult = null;
lastScreenInfo = "分析完毕,没有发现问题";
} else {
lastScreenInfo = "分析完毕,发现 ${checkResult!.length} 个问题";
}
if (scInstalledPath == "not_install" && (checkResult?.isEmpty ?? true)) {
showToast(context!, "扫描完毕,没有发现问题,若仍然安装失败,请尝试使用工具箱中的 RSI启动器管理员模式。");
}
}
Future _checkEAC() async {
if (scInstalledPath == "not_install") return;
lastScreenInfo = "正在检查EAC";
final eacPath = "$scInstalledPath\\EasyAntiCheat";
final eacJsonPath = "$eacPath\\Settings.json";
if (!await Directory(eacPath).exists() ||
!await File(eacJsonPath).exists()) {
checkResult?.add(const MapEntry("eac_file_miss", ""));
return;
}
final eacJsonData = await File(eacJsonPath).readAsBytes();
final Map eacJson = json.decode(utf8.decode(eacJsonData));
final eacID = eacJson["productid"];
final eacDeploymentId = eacJson["deploymentid"];
if (eacID == null || eacDeploymentId == null) {
checkResult?.add(const MapEntry("eac_file_miss", ""));
return;
}
final eacFilePath =
"${Platform.environment["appdata"]}\\EasyAntiCheat\\$eacID\\$eacDeploymentId\\easyanticheat_wow64_x64.eac";
if (!await File(eacFilePath).exists()) {
checkResult?.add(MapEntry("eac_not_install", eacPath));
return;
}
}
Future _checkPreInstall() async {
lastScreenInfo = "正在检查:运行环境";
if (!(Platform.operatingSystemVersion.contains("Windows 10") ||
Platform.operatingSystemVersion.contains("Windows 11"))) {
checkResult
?.add(MapEntry("unSupport_system", Platform.operatingSystemVersion));
lastScreenInfo = "不支持的操作系统:${Platform.operatingSystemVersion}";
await showToast(context!, lastScreenInfo);
}
if (cnExp.hasMatch(await SCLoggerHelper.getLogFilePath() ?? "")) {
checkResult?.add(const MapEntry("cn_user_name", ""));
}
// 检查 RAM
final ramSize = await SystemHelper.getSystemMemorySizeGB();
if (ramSize < 16) {
checkResult?.add(MapEntry("low_ram", "$ramSize"));
}
lastScreenInfo = "正在检查:安装信息";
// 检查安装分区
try {
final listData = await SCLoggerHelper.getGameInstallPath(
await SCLoggerHelper.getLauncherLogList() ?? []);
final p = [];
final checkedPath = [];
for (var installPath in listData) {
if (!checkedPath.contains(installPath)) {
if (cnExp.hasMatch(installPath)) {
checkResult?.add(MapEntry("cn_install_path", installPath));
}
if (scInstalledPath == "not_install") {
checkedPath.add(installPath);
if (!await Directory(installPath).exists()) {
checkResult?.add(MapEntry("no_live_path", installPath));
}
}
final tp = installPath.split(":")[0];
if (!p.contains(tp)) {
p.add(tp);
}
}
}
// call check
for (var element in p) {
var result = await Process.run('powershell', [
"(fsutil fsinfo sectorinfo $element: | Select-String 'PhysicalBytesPerSectorForPerformance').ToString().Split(':')[1].Trim()"
]);
dPrint(result.stdout);
if (result.stderr == "") {
final rs = result.stdout.toString();
final physicalBytesPerSectorForPerformance = (int.tryParse(rs) ?? 0);
if (physicalBytesPerSectorForPerformance > 4096) {
checkResult?.add(MapEntry("nvme_PhysicalBytes", element));
}
}
}
} catch (e) {
dPrint(e);
}
}
void resetCheck() {
checkResult = null;
reScanPath();
notifyListeners();
}
Future<void> doFix(MapEntry<String, String> item) async {
isFixing = true;
notifyListeners();
switch (item.key) {
case "unSupport_system":
showToast(context!, "若您的硬件达标,请尝试安装最新的 Windows 系统。");
return;
case "no_live_path":
try {
await Directory(item.value).create(recursive: true);
showToast(context!, "创建文件夹成功,请尝试继续下载游戏!");
checkResult?.remove(item);
notifyListeners();
} catch (e) {
showToast(context!, "创建文件夹失败,请尝试手动创建。\n目录:${item.value} \n错误:$e");
}
return;
case "nvme_PhysicalBytes":
final r = await SystemHelper.addNvmePatch();
if (r == "") {
showToast(context!,
"修复成功,请尝试重启后继续安装游戏! 若注册表修改操作导致其他软件出现兼容问题,请使用 工具 中的 NVME 注册表清理。");
checkResult?.remove(item);
notifyListeners();
} else {
showToast(context!, "修复失败,$r");
}
return;
case "eac_file_miss":
showToast(
context!, "未在 LIVE 文件夹找到 EasyAntiCheat 文件 或 文件不完整,请使用 RSI 启动器校验文件");
return;
case "eac_not_install":
final eacJsonPath = "${item.value}\\Settings.json";
final eacJsonData = await File(eacJsonPath).readAsBytes();
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]);
dPrint("${item.value}\\EasyAntiCheat_EOS_Setup.exe install $eacID");
if (result.stderr == "") {
showToast(context!, "修复成功,请尝试启动游戏。(若问题无法解决,请使用工具箱的 《重装 EAC》");
checkResult?.remove(item);
notifyListeners();
} else {
showToast(context!, "修复失败,${result.stderr}");
}
} catch (e) {
showToast(context!, "修复失败,$e");
}
return;
case "cn_user_name":
showToast(context!, "即将跳转,教程来自互联网,请谨慎操作...");
await Future.delayed(const Duration(milliseconds: 300));
launchUrlString(
"https://btfy.eu.org/?q=5L+u5pS5d2luZG93c+eUqOaIt+WQjeS7juS4reaWh+WIsOiLseaWhw==");
return;
default:
showToast(context!, "该问题暂不支持自动处理,请提供截图寻求帮助");
return;
}
}
openDir(rsiLauncherInstalledPath) async {
await Process.run("powershell.exe",
["explorer.exe", "/select,\"$rsiLauncherInstalledPath\""]);
}
onMenuTap(String key) {
switch (key) {
case "auto_check":
doCheck()?.call();
return;
case "localization":
if (scInstalledPath == "not_install") {
showToast(context!, "该功能需要一个有效的安装位置");
return;
}
showDialog(
context: context!,
dismissWithEsc: false,
builder: (BuildContext context) {
return BaseUIContainer(
uiCreate: () => LocalizationUI(),
modelCreate: () => LocalizationUIModel(scInstalledPath));
});
return;
case "performance":
if (scInstalledPath == "not_install") {
showToast(context!, "该功能需要一个有效的安装位置");
return;
}
BaseUIContainer(
uiCreate: () => PerformanceUI(),
modelCreate: () => PerformanceUIModel(scInstalledPath))
.push(context!);
return;
}
}
showPlacard() {
switch (appPlacardData?.linkType) {
case "external":
launchUrlString(appPlacardData?.link);
return;
case "doc":
showDialog(
context: context!,
builder: (context) {
return BaseUIContainer(
uiCreate: () => MDContentDialogUI(),
modelCreate: () => MDContentDialogUIModel(
appPlacardData?.title ?? "公告详情", appPlacardData?.link));
});
return;
}
}
closePlacard() async {
final box = await Hive.openBox("app_conf");
await box.put("close_placard", appPlacardData?.version);
appPlacardData = null;
notifyListeners();
}
goWebView(String title, String url, {bool useLocalization = false}) async {
if (useLocalization) {
const tipVersion = 1;
final box = await Hive.openBox("app_conf");
final skip =
await box.get("skip_web_localization_tip_version", defaultValue: 0);
if (skip != tipVersion) {
final ok = await showConfirmDialogs(
context!,
"星际公民官网汉化",
const Text(
"该汉化功能移植自星际公民汉化组的 Tampermonkey 浏览器插件https://greasyfork.org/zh-CN/scripts/459084文本内容由星际公民汉化组进行更新。"
"\n\n移植后的脚本源代码随 StarCitizenDoctor 项目一起分发https://jihulab.com/StarCitizenCN_Community/StarCitizenDoctor"
"\n\n\n本插功能件仅供大致浏览使用,不对任何有关本功能产生的问题负责!在涉及账号操作前请注意确认网站的原本内容!"
"\n\n\n使用此功能登录账号时请确保您的 StarCitizenDoctor 是从可信任的来源下载。",
style: TextStyle(fontSize: 16),
),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context!).size.width * .6));
if (!ok) return;
await box.put("skip_web_localization_tip_version", tipVersion);
}
}
if (!await WebviewWindow.isWebviewAvailable()) {
showToast(context!, "需要安装 WebView2 Runtime");
launchUrlString(
"https://developer.microsoft.com/en-us/microsoft-edge/webview2/");
return;
}
final webViewModel = WebViewModel(context!);
if (useLocalization) {
isFixingString = "正在初始化汉化资源...";
isFixing = true;
notifyListeners();
try {
await webViewModel.initLocalization();
} catch (e) {
showToast(context!, "初始化网页汉化资源失败!$e");
}
isFixingString = "";
isFixing = false;
}
await webViewModel.initWebView(title: title);
if (await File(
"${AppConf.applicationSupportDir}\\webview_data\\enable_webview_localization_capture")
.exists()) {
webViewModel.enableCapture = true;
BaseUIContainer(
uiCreate: () => WebviewLocalizationCaptureUI(),
modelCreate: () =>
WebviewLocalizationCaptureUIModel(webViewModel))
.push(context!)
.then((_) {
webViewModel.enableCapture = false;
});
}
await webViewModel.launch(url);
notifyListeners();
}
}