2023-10-09 09:32:07 +08:00
|
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
2024-01-29 22:36:04 +08:00
|
|
|
|
import 'package:hive/hive.dart';
|
2023-11-05 16:22:43 +08:00
|
|
|
|
import 'package:starcitizen_doctor/api/analytics.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
import 'package:starcitizen_doctor/base/ui_model.dart';
|
2024-01-29 20:44:00 +08:00
|
|
|
|
import 'package:starcitizen_doctor/common/conf/app_conf.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
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';
|
2023-11-21 23:54:48 +08:00
|
|
|
|
import 'package:url_launcher/url_launcher_string.dart';
|
2023-12-11 22:48:49 +08:00
|
|
|
|
import 'package:xml/xml.dart';
|
2023-10-09 09:32:07 +08:00
|
|
|
|
|
|
|
|
|
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>[];
|
|
|
|
|
|
2023-10-13 22:18:02 +08:00
|
|
|
|
bool isItemLoading = false;
|
|
|
|
|
|
2023-10-09 09:32:07 +08:00
|
|
|
|
@override
|
|
|
|
|
Future loadData({bool skipPathScan = false}) async {
|
2023-10-29 17:10:39 +08:00
|
|
|
|
if (isItemLoading) return;
|
2023-10-09 09:32:07 +08:00
|
|
|
|
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 管理员模式",
|
2023-11-28 20:04:15 +08:00
|
|
|
|
"以管理员身份运行RSI启动器,可能会解决一些问题。\n\n若设置了能效核心屏蔽参数,也会在此应用。",
|
2023-10-09 09:32:07 +08:00
|
|
|
|
const Icon(FluentIcons.admin, size: 28),
|
|
|
|
|
onTap: _adminRSILauncher,
|
2023-10-13 22:18:02 +08:00
|
|
|
|
)
|
|
|
|
|
];
|
|
|
|
|
isItemLoading = true;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
items.addAll(await _addLogCard());
|
|
|
|
|
notifyListeners();
|
|
|
|
|
items.addAll(await _addNvmePatchCard());
|
|
|
|
|
notifyListeners();
|
|
|
|
|
items.add(await _addShaderCard());
|
2023-12-11 22:54:11 +08:00
|
|
|
|
notifyListeners();
|
|
|
|
|
items.add(await _addPhotographyCard());
|
2023-12-11 22:48:49 +08:00
|
|
|
|
// close loading
|
2023-10-13 22:18:02 +08:00
|
|
|
|
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)
|
2023-10-09 09:32:07 +08:00
|
|
|
|
_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,
|
|
|
|
|
),
|
2023-10-13 22:18:02 +08:00
|
|
|
|
if (!nvmePatchStatus)
|
2023-10-09 09:32:07 +08:00
|
|
|
|
_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);
|
|
|
|
|
},
|
2023-10-13 22:18:02 +08:00
|
|
|
|
)
|
|
|
|
|
];
|
2023-10-09 09:32:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-13 22:18:02 +08:00
|
|
|
|
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,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-11 22:48:49 +08:00
|
|
|
|
Future<_ToolsItemData> _addPhotographyCard() async {
|
|
|
|
|
// 获取配置文件状态
|
|
|
|
|
final isEnable = await _checkPhotographyStatus();
|
|
|
|
|
|
|
|
|
|
return _ToolsItemData(
|
|
|
|
|
"photography_mode",
|
|
|
|
|
isEnable ? "关闭摄影模式" : "开启摄影模式",
|
2023-12-12 19:46:39 +08:00
|
|
|
|
isEnable
|
|
|
|
|
? "还原镜头摇晃效果。\n\n@拉邦那 Lapernum 提供参数信息。"
|
|
|
|
|
: "一键关闭游戏内镜头晃动以便于摄影操作。\n\n @拉邦那 Lapernum 提供参数信息。",
|
2023-12-11 22:48:49 +08:00
|
|
|
|
const Icon(FontAwesomeIcons.camera, size: 28),
|
|
|
|
|
onTap: () => _onChangePhotographyMode(isEnable),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-13 22:18:02 +08:00
|
|
|
|
/// ---------------------------- func -------------------------------------------------------
|
|
|
|
|
/// -----------------------------------------------------------------------------------------
|
|
|
|
|
/// -----------------------------------------------------------------------------------------
|
|
|
|
|
/// -----------------------------------------------------------------------------------------
|
|
|
|
|
|
2023-10-09 09:32:07 +08:00
|
|
|
|
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,
|
2023-11-21 01:05:20 +08:00
|
|
|
|
checkExists: false, withVersion: ["LIVE", "PTU", "EPTU"]);
|
2023-10-09 09:32:07 +08:00
|
|
|
|
if (scInstallPaths.isNotEmpty) {
|
|
|
|
|
scInstalledPath = scInstallPaths.first;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
2023-11-21 23:36:26 +08:00
|
|
|
|
dPrint(e);
|
2023-10-09 09:32:07 +08:00
|
|
|
|
showToast(context!, "解析 log 文件失败!\n请尝试使用 RSI Launcher log 修复 工具!");
|
|
|
|
|
}
|
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
|
|
if (rsiLauncherInstalledPath == "") {
|
2023-11-21 23:36:26 +08:00
|
|
|
|
showToast(context!, "未找到 RSI 启动器,请尝试重新安装,或在设置中手动添加。");
|
2023-10-09 09:32:07 +08:00
|
|
|
|
}
|
|
|
|
|
if (scInstalledPath == "") {
|
2023-11-21 23:36:26 +08:00
|
|
|
|
showToast(context!, "未找到星际公民游戏安装位置,请至少完成一次游戏启动操作 或在设置中手动添加。");
|
2023-10-09 09:32:07 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 重装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"
|
2023-10-29 17:10:39 +08:00
|
|
|
|
"处理器:${await SystemHelper.getCpuName()}\n\n"
|
2023-10-09 09:32:07 +08:00
|
|
|
|
"内存大小:${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 {
|
2023-11-10 22:10:27 +08:00
|
|
|
|
await Process.run(
|
|
|
|
|
SystemHelper.powershellPath, ["explorer.exe", "/select,\"$path\""]);
|
2023-10-09 09:32:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2024-01-29 22:36:04 +08:00
|
|
|
|
String savePath = scInstalledPath;
|
|
|
|
|
String fileName = "Data.p4k";
|
|
|
|
|
bool isResumeDownload = false;
|
|
|
|
|
final box = await Hive.openBox("p4k_cache");
|
2023-11-05 15:56:48 +08:00
|
|
|
|
var downloadUrl = AppConf.networkVersionData?.p4kDownloadUrl;
|
2023-10-12 21:00:58 +08:00
|
|
|
|
if (downloadUrl == null || downloadUrl.isEmpty) {
|
|
|
|
|
showToast(context!, "该功能维护中,请稍后再试!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-13 23:03:44 +08:00
|
|
|
|
if ((await SystemHelper.getPID("\"RSI Launcher\"")).isNotEmpty) {
|
|
|
|
|
showToast(context!, "RSI启动器正在运行,请先关闭启动器再使用此功能!",
|
|
|
|
|
constraints: BoxConstraints(
|
|
|
|
|
maxWidth: MediaQuery.of(context!).size.width * .35));
|
2023-10-13 21:58:25 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2024-01-29 22:36:04 +08:00
|
|
|
|
final lastSavePath = (box.get("last_save_dir", defaultValue: {}) as Map);
|
|
|
|
|
dPrint("lastSavePath === $lastSavePath");
|
|
|
|
|
if (lastSavePath.isNotEmpty) {
|
|
|
|
|
final s = lastSavePath["save_path"] ?? "";
|
|
|
|
|
final f = lastSavePath["file_name"] ?? "";
|
|
|
|
|
if ((await File("$s/$f.downloading").exists()) &&
|
|
|
|
|
(await File("$s/$f.downloading.bson").exists())) {
|
|
|
|
|
final ok = await showConfirmDialogs(context!, "是否恢复下载?",
|
|
|
|
|
const Text("检测到未完成的下载,点击确认即可恢复下载,点击取消将会删除之前的临时文件,并开始一个新的下载。"));
|
|
|
|
|
if (ok) {
|
|
|
|
|
savePath = s;
|
|
|
|
|
fileName = f;
|
|
|
|
|
isResumeDownload = true;
|
|
|
|
|
} else {
|
|
|
|
|
// del last cache and del file
|
|
|
|
|
await box.delete("last_save_dir");
|
|
|
|
|
await File("$s/$f.downloading").delete();
|
|
|
|
|
await File("$s/$f.downloading.bson").delete();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// del last cache
|
|
|
|
|
await box.delete("last_save_dir");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
await showToast(
|
|
|
|
|
context!,
|
|
|
|
|
"P4k 是星际公民的核心游戏文件,高达近 100GB,盒子提供的离线下载是为了帮助一些p4k文件下载超级慢的用户。"
|
|
|
|
|
"\n\n接下来会弹窗询问您保存位置(可以选择星际公民文件夹也可以选择别处),下载完成后请确保 P4K 文件夹位于 LIVE 文件夹内,之后使用星际公民启动器校验更新即可。");
|
|
|
|
|
AnalyticsApi.touch("p4k_download");
|
|
|
|
|
}
|
2023-10-09 09:32:07 +08:00
|
|
|
|
final r = await showDialog(
|
|
|
|
|
context: context!,
|
|
|
|
|
dismissWithEsc: false,
|
|
|
|
|
builder: (context) {
|
|
|
|
|
return BaseUIContainer(
|
|
|
|
|
uiCreate: () => DownloaderDialogUI(),
|
|
|
|
|
modelCreate: () => DownloaderDialogUIModel(
|
2024-01-29 22:36:04 +08:00
|
|
|
|
fileName, savePath, downloadUrl,
|
|
|
|
|
showChangeSavePathDialog: !isResumeDownload,
|
|
|
|
|
threadCount: 10,
|
|
|
|
|
isP4kDownload: true));
|
2023-10-09 09:32:07 +08:00
|
|
|
|
});
|
2024-01-29 22:36:04 +08:00
|
|
|
|
|
2023-10-09 09:32:07 +08:00
|
|
|
|
if (r != null) {
|
|
|
|
|
if (r == "cancel") {
|
2024-01-29 22:36:04 +08:00
|
|
|
|
return showToast(context!, "下载进度已保留,您可以再次点击此功能恢复下载。");
|
2023-10-29 17:10:39 +08:00
|
|
|
|
} else {
|
2024-01-29 22:36:04 +08:00
|
|
|
|
final ok = await showConfirmDialogs(
|
|
|
|
|
context!, "下载完毕!", Text("文件已保存到:$r\n\n是否查看P4K操作教程?"));
|
|
|
|
|
if (ok == true) {
|
|
|
|
|
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");
|
|
|
|
|
}
|
2023-10-09 09:32:07 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-11 22:48:49 +08:00
|
|
|
|
|
|
|
|
|
Future<bool> _checkPhotographyStatus({bool? setMode}) async {
|
|
|
|
|
const keys = ["AudioShakeStrength", "CameraSpringMovement", "ShakeScale"];
|
|
|
|
|
final attributesFile = File(
|
|
|
|
|
"$scInstalledPath\\USER\\Client\\0\\Profiles\\default\\attributes.xml");
|
|
|
|
|
if (setMode == null) {
|
|
|
|
|
bool isEnable = false;
|
|
|
|
|
if (scInstalledPath.isNotEmpty) {
|
|
|
|
|
if (await attributesFile.exists()) {
|
|
|
|
|
final xmlFile =
|
|
|
|
|
XmlDocument.parse(await attributesFile.readAsString());
|
|
|
|
|
isEnable = true;
|
|
|
|
|
for (var k in keys) {
|
|
|
|
|
if (!isEnable) break;
|
|
|
|
|
final e = xmlFile.rootElement.children
|
|
|
|
|
.where((element) => element.getAttribute("name") == k)
|
|
|
|
|
.firstOrNull;
|
|
|
|
|
if (e != null && e.getAttribute("value") == "0") {
|
|
|
|
|
} else {
|
|
|
|
|
isEnable = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return isEnable;
|
|
|
|
|
} else {
|
|
|
|
|
if (!await attributesFile.exists()) {
|
|
|
|
|
showToast(context!, "配置文件不存在,请尝试运行一次游戏");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
final xmlFile = XmlDocument.parse(await attributesFile.readAsString());
|
|
|
|
|
// clear all
|
|
|
|
|
xmlFile.rootElement.children.removeWhere(
|
|
|
|
|
(element) => keys.contains(element.getAttribute("name")));
|
|
|
|
|
if (setMode) {
|
|
|
|
|
for (var element in keys) {
|
|
|
|
|
XmlElement newNode = XmlElement(XmlName('Attr'), [
|
|
|
|
|
XmlAttribute(XmlName('name'), element),
|
|
|
|
|
XmlAttribute(XmlName('value'), '0'),
|
|
|
|
|
]);
|
|
|
|
|
xmlFile.rootElement.children.add(newNode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dPrint(xmlFile);
|
|
|
|
|
await attributesFile.delete();
|
|
|
|
|
await attributesFile.writeAsString(xmlFile.toXmlString(pretty: true));
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_onChangePhotographyMode(bool isEnable) async {
|
|
|
|
|
await handleError(() => _checkPhotographyStatus(setMode: !isEnable));
|
|
|
|
|
reloadData();
|
|
|
|
|
}
|
2023-10-09 09:32:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _ToolsItemData {
|
|
|
|
|
String key;
|
|
|
|
|
|
|
|
|
|
_ToolsItemData(this.key, this.name, this.infoString, this.icon, {this.onTap});
|
|
|
|
|
|
|
|
|
|
String name;
|
|
|
|
|
String infoString;
|
|
|
|
|
Widget icon;
|
|
|
|
|
AsyncCallback? onTap;
|
|
|
|
|
}
|