mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-01-13 11:40:27 +00:00
feat: linux support
This commit is contained in:
parent
66ead87d47
commit
bba2dbd360
@ -8,7 +8,15 @@ import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class SCLoggerHelper {
|
||||
static Future<String?> getLogFilePath() async {
|
||||
if (!Platform.isWindows) return null;
|
||||
if (!Platform.isWindows) {
|
||||
final wineUserPath = await getWineUserPath();
|
||||
if (wineUserPath == null) return null;
|
||||
// /home/xkeyc/Games/star-citizen/drive_c/users/xkeyc/AppData/Roaming/rsilauncher/
|
||||
final rsiLauncherPath = "$wineUserPath/AppData/Roaming/rsilauncher";
|
||||
dPrint("rsiLauncherPath Wine:$rsiLauncherPath");
|
||||
final jsonLogPath = "$rsiLauncherPath/logs/log.log";
|
||||
return jsonLogPath;
|
||||
};
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
final appDataPath = envVars["appdata"];
|
||||
if (appDataPath == null) {
|
||||
@ -21,6 +29,14 @@ class SCLoggerHelper {
|
||||
}
|
||||
|
||||
static Future<String?> getShaderCachePath() async {
|
||||
if (!Platform.isWindows) {
|
||||
final wineUserPath = await getWineUserPath();
|
||||
if (wineUserPath == null) return null;
|
||||
// /home/xkeyc/Games/star-citizen/drive_c/users/xkeyc/AppData/Local/star citizen/
|
||||
final scCachePath = "$wineUserPath/AppData/Local/star citizen";
|
||||
dPrint("getShaderCachePath Wine === $scCachePath");
|
||||
return scCachePath;
|
||||
}
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
final appDataPath = envVars["LOCALAPPDATA"];
|
||||
if (appDataPath == null) {
|
||||
@ -31,6 +47,23 @@ class SCLoggerHelper {
|
||||
return scCachePath;
|
||||
}
|
||||
|
||||
static Future<String?> getWineUserPath() async {
|
||||
// get game path in hiveBox
|
||||
final confBox = await Hive.openBox("app_conf");
|
||||
final path = confBox.get("custom_game_path");
|
||||
if (path?.isEmpty ?? true) return null;
|
||||
// path eg: /home/xkeyc/Games/star-citizen/drive_c/Program Files/Roberts Space Industries/StarCitizen/LIVE/
|
||||
// resolve wine c_drive path
|
||||
final wineCDrivePath = path.toString().split('/drive_c/').first;
|
||||
// scan wine user path == current_unix_user
|
||||
final wineUserPath = "$wineCDrivePath/drive_c/users/${Platform.environment['USER']}";
|
||||
// check exists
|
||||
final wineUserDir = Directory(wineUserPath);
|
||||
if (!await wineUserDir.exists()) return null;
|
||||
dPrint("getWineUserPath === $wineUserPath");
|
||||
return wineUserPath;
|
||||
}
|
||||
|
||||
static Future<List?> getLauncherLogList() async {
|
||||
if (!Platform.isWindows) return [];
|
||||
try {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32;
|
||||
|
||||
@ -41,7 +42,7 @@ class SystemHelper {
|
||||
if (path != null && path != "") {
|
||||
if (await File(path).exists()) {
|
||||
if (skipEXE) {
|
||||
return "${path.toString().replaceAll("\\RSI Launcher.exe", "")}\\";
|
||||
return "${path.toString().replaceAll("\\RSI Launcher.exe".platformPath, "")}\\".platformPath;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
@ -112,7 +113,7 @@ class MultiWindowManager {
|
||||
|
||||
await Window.initialize();
|
||||
|
||||
if (windowAppState.windowsVersion >= 10) {
|
||||
if (Platform.isWindows && windowAppState.windowsVersion >= 10) {
|
||||
await Window.setEffect(effect: WindowEffect.acrylic);
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/data/app_unp4k_p4k_item_data.dart';
|
||||
import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart';
|
||||
@ -80,7 +81,7 @@ class Unp4kCModel extends _$Unp4kCModel {
|
||||
|
||||
void _init() async {
|
||||
final gamePath = getGamePath();
|
||||
final gameP4kPath = "$gamePath\\Data.p4k";
|
||||
final gameP4kPath = "$gamePath\\Data.p4k".platformPath;
|
||||
|
||||
try {
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading);
|
||||
|
||||
@ -30,13 +30,9 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
return ContentDialog(
|
||||
title: makeTitle(context, model, state),
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width * .7, minHeight: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.height * .9),
|
||||
maxWidth: MediaQuery.of(context).size.width * .7,
|
||||
minHeight: MediaQuery.of(context).size.height * .9,
|
||||
),
|
||||
content: Padding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12, top: 12),
|
||||
child: SingleChildScrollView(
|
||||
@ -44,132 +40,128 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
children: [
|
||||
AnimatedSize(
|
||||
duration: const Duration(milliseconds: 130),
|
||||
child: state.patchStatus?.key == true &&
|
||||
state.patchStatus?.value == S.current.home_action_info_game_built_in
|
||||
child:
|
||||
state.patchStatus?.key == true &&
|
||||
state.patchStatus?.value == S.current.home_action_info_game_built_in
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: InfoBar(
|
||||
title: Text(S.current.home_action_info_warning),
|
||||
content: Text(S.current.localization_info_machine_translation_warning),
|
||||
severity: InfoBarSeverity.info,
|
||||
style: InfoBarThemeData(decoration: (severity) {
|
||||
return const BoxDecoration(color: Color.fromRGBO(155, 7, 7, 1.0));
|
||||
}, iconColor: (severity) {
|
||||
return Colors.white;
|
||||
}),
|
||||
),
|
||||
)
|
||||
: SizedBox(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width,
|
||||
),
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: InfoBar(
|
||||
title: Text(S.current.home_action_info_warning),
|
||||
content: Text(S.current.localization_info_machine_translation_warning),
|
||||
severity: InfoBarSeverity.info,
|
||||
style: InfoBarThemeData(
|
||||
decoration: (severity) {
|
||||
return const BoxDecoration(color: Color.fromRGBO(155, 7, 7, 1.0));
|
||||
},
|
||||
iconColor: (severity) {
|
||||
return Colors.white;
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox(width: MediaQuery.of(context).size.width),
|
||||
),
|
||||
makeToolsListContainer(context, model, state),
|
||||
makeListContainer(
|
||||
S.current.localization_info_translation,
|
||||
[
|
||||
if (state.patchStatus == null)
|
||||
makeLoading(context)
|
||||
else
|
||||
...[
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
Center(
|
||||
child: Text(S.current.localization_info_enabled(
|
||||
LocalizationUIModel.languageSupport[state.selectedLanguage] ?? "")),
|
||||
),
|
||||
const Spacer(),
|
||||
ToggleSwitch(
|
||||
checked: state.patchStatus?.key == true,
|
||||
onChanged: model.updateLangCfg,
|
||||
)
|
||||
],
|
||||
makeListContainer(S.current.localization_info_translation, [
|
||||
if (state.patchStatus == null)
|
||||
makeLoading(context)
|
||||
else ...[
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
S.current.localization_info_enabled(
|
||||
LocalizationUIModel.languageSupport[state.selectedLanguage] ?? "",
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
),
|
||||
const Spacer(),
|
||||
ToggleSwitch(checked: state.patchStatus?.key == true, onChanged: model.updateLangCfg),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Text(S.current.localization_info_installed_version(
|
||||
"${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? S
|
||||
.current.home_localization_msg_version_advanced : ""}")),
|
||||
SizedBox(width: 24),
|
||||
if (state.installedCommunityInputMethodSupportVersion != null)
|
||||
Text(
|
||||
S.current.input_method_community_input_method_support_version(
|
||||
state.installedCommunityInputMethodSupportVersion ?? "?"),
|
||||
)
|
||||
],
|
||||
Text(
|
||||
S.current.localization_info_installed_version(
|
||||
"${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? S.current.home_localization_msg_version_advanced : ""}",
|
||||
),
|
||||
),
|
||||
if (state.patchStatus?.value != S.current.home_action_info_game_built_in)
|
||||
Row(
|
||||
children: [
|
||||
Button(
|
||||
onPressed: model.goFeedback,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(FluentIcons.feedback),
|
||||
const SizedBox(width: 6),
|
||||
Text(S.current.localization_action_translation_feedback),
|
||||
],
|
||||
),
|
||||
)),
|
||||
const SizedBox(width: 16),
|
||||
Button(
|
||||
onPressed: model.doDelIniFile(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(FluentIcons.delete),
|
||||
const SizedBox(width: 6),
|
||||
Text(S.current.localization_action_uninstall_translation),
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
SizedBox(width: 24),
|
||||
if (state.installedCommunityInputMethodSupportVersion != null)
|
||||
Text(
|
||||
S.current.input_method_community_input_method_support_version(
|
||||
state.installedCommunityInputMethodSupportVersion ?? "?",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
color: Colors.white.withValues(alpha: .1),
|
||||
height: 1,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (state.apiLocalizationData == null)
|
||||
makeLoading(context)
|
||||
else
|
||||
if (state.apiLocalizationData!.isEmpty)
|
||||
Center(
|
||||
child: Text(
|
||||
S.current.localization_info_no_translation_available,
|
||||
style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .8)),
|
||||
),
|
||||
if (state.patchStatus?.value != S.current.home_action_info_game_built_in)
|
||||
Row(
|
||||
children: [
|
||||
Button(
|
||||
onPressed: model.goFeedback,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(FluentIcons.feedback),
|
||||
const SizedBox(width: 6),
|
||||
Text(S.current.localization_action_translation_feedback),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
AlignedGridView.count(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = state.apiLocalizationData!.entries.elementAt(index);
|
||||
return makeRemoteList(context, model, item, state, index);
|
||||
},
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: state.apiLocalizationData?.length ?? 0,
|
||||
)
|
||||
],
|
||||
],
|
||||
context),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Button(
|
||||
onPressed: model.doDelIniFile(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(FluentIcons.delete),
|
||||
const SizedBox(width: 6),
|
||||
Text(S.current.localization_action_uninstall_translation),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Container(color: Colors.white.withValues(alpha: .1), height: 1),
|
||||
const SizedBox(height: 12),
|
||||
if (state.apiLocalizationData == null)
|
||||
makeLoading(context)
|
||||
else if (state.apiLocalizationData!.isEmpty)
|
||||
Center(
|
||||
child: Text(
|
||||
S.current.localization_info_no_translation_available,
|
||||
style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .8)),
|
||||
),
|
||||
)
|
||||
else
|
||||
AlignedGridView.count(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = state.apiLocalizationData!.entries.elementAt(index);
|
||||
return makeRemoteList(context, model, item, state, index);
|
||||
},
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: state.apiLocalizationData?.length ?? 0,
|
||||
),
|
||||
],
|
||||
], context),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -177,8 +169,13 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeRemoteList(BuildContext context, LocalizationUIModel model, MapEntry<String, ScLocalizationData> item,
|
||||
LocalizationUIState state, int index) {
|
||||
Widget makeRemoteList(
|
||||
BuildContext context,
|
||||
LocalizationUIModel model,
|
||||
MapEntry<String, ScLocalizationData> item,
|
||||
LocalizationUIState state,
|
||||
int index,
|
||||
) {
|
||||
final isWorking = state.workingVersion.isNotEmpty;
|
||||
final isMineWorking = state.workingVersion == item.key;
|
||||
final isInstalled = state.patchStatus?.value == item.key;
|
||||
@ -207,10 +204,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${item.value.info}",
|
||||
style: const TextStyle(fontSize: 19),
|
||||
),
|
||||
Text("${item.value.info}", style: const TextStyle(fontSize: 19)),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
S.current.localization_info_version_number(item.value.versionName ?? ""),
|
||||
@ -230,40 +224,30 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
if (isMineWorking)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 12),
|
||||
child: ProgressRing(),
|
||||
)
|
||||
else
|
||||
...[
|
||||
Icon(
|
||||
isInstalled
|
||||
? FluentIcons.check_mark
|
||||
: isItemEnabled
|
||||
? FluentIcons.download
|
||||
: FluentIcons.disable_updates,
|
||||
color: Colors.white.withValues(alpha: .8),
|
||||
size: 18,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
isInstalled
|
||||
? S.current.localization_info_installed
|
||||
: (isItemEnabled
|
||||
? S.current.localization_action_install
|
||||
: S.current.localization_info_unavailable),
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .8),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
if ((!isInstalled) && isItemEnabled)
|
||||
Icon(
|
||||
FluentIcons.chevron_right,
|
||||
size: 14,
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
)
|
||||
]
|
||||
const Padding(padding: EdgeInsets.only(right: 12), child: ProgressRing())
|
||||
else ...[
|
||||
Icon(
|
||||
isInstalled
|
||||
? FluentIcons.check_mark
|
||||
: isItemEnabled
|
||||
? FluentIcons.download
|
||||
: FluentIcons.disable_updates,
|
||||
color: Colors.white.withValues(alpha: .8),
|
||||
size: 18,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
isInstalled
|
||||
? S.current.localization_info_installed
|
||||
: (isItemEnabled
|
||||
? S.current.localization_action_install
|
||||
: S.current.localization_info_unavailable),
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: .8)),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
if ((!isInstalled) && isItemEnabled)
|
||||
Icon(FluentIcons.chevron_right, size: 14, color: Colors.white.withValues(alpha: .6)),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (item.value.note != null) ...[
|
||||
@ -272,10 +256,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
"${item.value.note}",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .4),
|
||||
fontSize: 13,
|
||||
),
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: .4), fontSize: 13),
|
||||
),
|
||||
],
|
||||
],
|
||||
@ -286,16 +267,20 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeListContainer(String title, List<Widget> children, BuildContext context,
|
||||
{List<Widget> actions = const [], bool gridViewMode = false, int gridViewCrossAxisCount = 2}) {
|
||||
Widget makeListContainer(
|
||||
String title,
|
||||
List<Widget> children,
|
||||
BuildContext context, {
|
||||
List<Widget> actions = const [],
|
||||
bool gridViewMode = false,
|
||||
int gridViewCrossAxisCount = 2,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: AnimatedSize(
|
||||
duration: const Duration(milliseconds: 130),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: FluentTheme
|
||||
.of(context)
|
||||
.cardColor, borderRadius: BorderRadius.circular(7)),
|
||||
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor, borderRadius: BorderRadius.circular(7)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 12, bottom: 12, left: 24, right: 24),
|
||||
child: Column(
|
||||
@ -303,21 +288,13 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 22),
|
||||
),
|
||||
Text(title, style: const TextStyle(fontSize: 22)),
|
||||
const Spacer(),
|
||||
if (actions.isNotEmpty) ...actions,
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
Container(
|
||||
color: Colors.white.withValues(alpha: .1),
|
||||
height: 1,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Container(color: Colors.white.withValues(alpha: .1), height: 1),
|
||||
const SizedBox(height: 12),
|
||||
if (gridViewMode)
|
||||
AlignedGridView.count(
|
||||
@ -332,7 +309,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
itemCount: children.length,
|
||||
)
|
||||
else
|
||||
...children
|
||||
...children,
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -344,55 +321,46 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
Widget makeTitle(BuildContext context, LocalizationUIModel model, LocalizationUIState state) {
|
||||
return Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
FluentIcons.back,
|
||||
size: 22,
|
||||
),
|
||||
onPressed: model.onBack(context)),
|
||||
IconButton(icon: const Icon(FluentIcons.back, size: 22), onPressed: model.onBack(context)),
|
||||
const SizedBox(width: 12),
|
||||
Text(S.current.home_action_localization_management),
|
||||
const SizedBox(width: 24),
|
||||
Text(
|
||||
"${model.getScInstallPath()}",
|
||||
style: const TextStyle(fontSize: 13),
|
||||
Expanded(
|
||||
child: Text(
|
||||
"${model.getScInstallPath()}",
|
||||
style: const TextStyle(fontSize: 13),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
SizedBox(width: 24),
|
||||
SizedBox(
|
||||
height: 36,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
S.current.localization_info_language,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
Text(S.current.localization_info_language, style: const TextStyle(fontSize: 16)),
|
||||
const SizedBox(width: 12),
|
||||
ComboBox<String>(
|
||||
value: state.selectedLanguage,
|
||||
items: [
|
||||
for (final lang in LocalizationUIModel.languageSupport.entries)
|
||||
ComboBoxItem(
|
||||
value: lang.key,
|
||||
child: Text(lang.value),
|
||||
)
|
||||
ComboBoxItem(value: lang.key, child: Text(lang.value)),
|
||||
],
|
||||
onChanged: state.workingVersion.isNotEmpty
|
||||
? null
|
||||
: (v) {
|
||||
if (v == null) return;
|
||||
model.selectLang(v);
|
||||
},
|
||||
)
|
||||
if (v == null) return;
|
||||
model.selectLang(v);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Button(
|
||||
onPressed: model.doRefresh(),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(6),
|
||||
child: Icon(FluentIcons.refresh),
|
||||
)),
|
||||
onPressed: model.doRefresh(),
|
||||
child: const Padding(padding: EdgeInsets.all(6), child: Icon(FluentIcons.refresh)),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -400,85 +368,77 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
Widget makeToolsListContainer(BuildContext context, LocalizationUIModel model, LocalizationUIState state) {
|
||||
final toolsMenu = {
|
||||
"launcher_mod": (
|
||||
const Icon(FluentIcons.c_plus_plus, size: 24),
|
||||
(S.current.home_localization_action_rsi_launcher_localization),
|
||||
),
|
||||
"advanced": (
|
||||
const Icon(FluentIcons.queue_advanced, size: 24),
|
||||
(S.current.home_localization_action_advanced),
|
||||
const Icon(FluentIcons.c_plus_plus, size: 24),
|
||||
(S.current.home_localization_action_rsi_launcher_localization),
|
||||
),
|
||||
"advanced": (const Icon(FluentIcons.queue_advanced, size: 24), (S.current.home_localization_action_advanced)),
|
||||
"custom_files": (
|
||||
const Icon(FluentIcons.custom_activity, size: 24),
|
||||
(S.current.home_localization_action_install_customize),
|
||||
const Icon(FluentIcons.custom_activity, size: 24),
|
||||
(S.current.home_localization_action_install_customize),
|
||||
),
|
||||
};
|
||||
|
||||
final enableTap = state.workingVersion.isEmpty;
|
||||
|
||||
return makeListContainer(
|
||||
S.current.home_localization_title_localization_tools,
|
||||
[
|
||||
for (final item in toolsMenu.entries)
|
||||
Tilt(
|
||||
disable: !enableTap,
|
||||
shadowConfig: const ShadowConfig(maxIntensity: .3),
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
child: GestureDetector(
|
||||
onTap: enableTap
|
||||
? () async {
|
||||
switch (item.key) {
|
||||
case "launcher_mod":
|
||||
ToolsUIModel.rsiEnhance(context);
|
||||
break;
|
||||
case "advanced":
|
||||
context.push("/index/advanced_localization");
|
||||
break;
|
||||
case "custom_files":
|
||||
final sb = await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => const LocalizationFromFileDialogUI(),
|
||||
);
|
||||
if (sb is (StringBuffer, bool)) {
|
||||
if (!context.mounted) return;
|
||||
await model.installFormString(
|
||||
sb.$1,
|
||||
S.current.localization_info_custom_files,
|
||||
isEnableCommunityInputMethod: sb.$2,
|
||||
context: context,
|
||||
);
|
||||
S.current.home_localization_title_localization_tools,
|
||||
[
|
||||
for (final item in toolsMenu.entries)
|
||||
Tilt(
|
||||
disable: !enableTap,
|
||||
shadowConfig: const ShadowConfig(maxIntensity: .3),
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
child: GestureDetector(
|
||||
onTap: enableTap
|
||||
? () async {
|
||||
switch (item.key) {
|
||||
case "launcher_mod":
|
||||
ToolsUIModel.rsiEnhance(context);
|
||||
break;
|
||||
case "advanced":
|
||||
context.push("/index/advanced_localization");
|
||||
break;
|
||||
case "custom_files":
|
||||
final sb = await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => const LocalizationFromFileDialogUI(),
|
||||
);
|
||||
if (sb is (StringBuffer, bool)) {
|
||||
if (!context.mounted) return;
|
||||
await model.installFormString(
|
||||
sb.$1,
|
||||
S.current.localization_info_custom_files,
|
||||
isEnableCommunityInputMethod: sb.$2,
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme
|
||||
.of(context)
|
||||
.cardColor,
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
item.value.$1,
|
||||
const SizedBox(width: 12),
|
||||
Text(item.value.$2),
|
||||
const SizedBox(width: 12),
|
||||
const Spacer(),
|
||||
Icon(
|
||||
FluentIcons.chevron_right,
|
||||
size: 14,
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
)
|
||||
],
|
||||
),
|
||||
}
|
||||
: null,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
item.value.$1,
|
||||
const SizedBox(width: 12),
|
||||
Text(item.value.$2),
|
||||
const SizedBox(width: 12),
|
||||
const Spacer(),
|
||||
Icon(FluentIcons.chevron_right, size: 14, color: Colors.white.withValues(alpha: .6)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
context,
|
||||
gridViewMode: true,
|
||||
gridViewCrossAxisCount: 3);
|
||||
),
|
||||
],
|
||||
context,
|
||||
gridViewMode: true,
|
||||
gridViewCrossAxisCount: 3,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,13 +48,12 @@ abstract class LocalizationUIState with _$LocalizationUIState {
|
||||
class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
static const languageSupport = {"chinese_(simplified)": NoL10n.langZHS, "chinese_(traditional)": NoL10n.langZHT};
|
||||
|
||||
Directory get _downloadDir => Directory("${appGlobalState.applicationSupportDir}\\Localizations");
|
||||
Directory get _downloadDir => Directory("${appGlobalState.applicationSupportDir}\\Localizations".platformPath);
|
||||
|
||||
Directory getDownloadDir() => _downloadDir;
|
||||
|
||||
Directory get _scDataDir => Directory("${ref.read(homeUIModelProvider).scInstalledPath}\\data");
|
||||
|
||||
File get _cfgFile => File("${_scDataDir.absolute.path}\\system.cfg");
|
||||
Directory get _scDataDir => Directory("${ref.read(homeUIModelProvider).scInstalledPath}\\data".platformPath);
|
||||
File get _cfgFile => File("${_scDataDir.absolute.path}\\system.cfg".platformPath);
|
||||
|
||||
StreamSubscription? _customizeDirListenSub;
|
||||
|
||||
@ -155,7 +154,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
}
|
||||
|
||||
void checkUserCfg(BuildContext context) async {
|
||||
final userCfgFile = File("$_scInstallPath\\USER.cfg");
|
||||
final userCfgFile = File("$_scInstallPath\\USER.cfg".platformPath);
|
||||
if (await userCfgFile.exists()) {
|
||||
final cfgString = await userCfgFile.readAsString();
|
||||
if (cfgString.contains("g_language") && !cfgString.contains("g_language=${state.selectedLanguage}")) {
|
||||
@ -241,7 +240,9 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
|
||||
VoidCallback? doDelIniFile() {
|
||||
return () async {
|
||||
final iniFile = File("${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini");
|
||||
final iniFile = File(
|
||||
"${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini".platformPath,
|
||||
);
|
||||
if (await iniFile.exists()) await iniFile.delete();
|
||||
await updateLangCfg(false);
|
||||
await _updateStatus();
|
||||
@ -249,7 +250,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
}
|
||||
|
||||
String getCustomizeFileName(String path) {
|
||||
return path.split("\\").last;
|
||||
return path.split("\\".platformPath).last;
|
||||
}
|
||||
|
||||
Future<void> installFormString(
|
||||
@ -261,7 +262,9 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
BuildContext? context,
|
||||
}) async {
|
||||
dPrint("LocalizationUIModel -> installFormString $versionName");
|
||||
final iniFile = File("${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini");
|
||||
final iniFile = File(
|
||||
"${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini".platformPath,
|
||||
);
|
||||
if (versionName.isNotEmpty) {
|
||||
if (!globalIni.toString().endsWith("\n")) {
|
||||
globalIni.write("\n");
|
||||
@ -323,7 +326,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
}
|
||||
|
||||
Future<Map<String, String>?> getCommunityInputMethodSupportData() async {
|
||||
final iniPath = "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini";
|
||||
final iniPath = "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini".platformPath;
|
||||
final iniFile = File(iniPath);
|
||||
if (!await iniFile.exists()) {
|
||||
return {};
|
||||
@ -351,7 +354,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
}
|
||||
|
||||
Future<(String, String)> getIniContentWithoutCommunityInputMethodSupportData() async {
|
||||
final iniPath = "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini";
|
||||
final iniPath = "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini".platformPath;
|
||||
final iniFile = File(iniPath);
|
||||
if (!await iniFile.exists()) {
|
||||
return ("", "");
|
||||
@ -383,7 +386,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
}) async {
|
||||
AnalyticsApi.touch("install_localization");
|
||||
|
||||
final savePath = File("${_downloadDir.absolute.path}\\${value.versionName}.sclang");
|
||||
final savePath = File("${_downloadDir.absolute.path}\\${value.versionName}.sclang".platformPath);
|
||||
try {
|
||||
state = state.copyWith(workingVersion: value.versionName!);
|
||||
if (!await savePath.exists()) {
|
||||
@ -488,7 +491,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
}
|
||||
|
||||
Future<void> _updateStatus() async {
|
||||
final iniPath = "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini";
|
||||
final iniPath = "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini".platformPath;
|
||||
final patchStatus = MapEntry(
|
||||
await _getLangCfgEnableLang(lang: state.selectedLanguage!),
|
||||
await _getInstalledIniVersion(iniPath),
|
||||
@ -535,7 +538,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
if (gamePath.isEmpty) {
|
||||
gamePath = _scInstallPath;
|
||||
}
|
||||
final cfgFile = File("${_scDataDir.absolute.path}\\system.cfg");
|
||||
final cfgFile = File("${_scDataDir.absolute.path}\\system.cfg".platformPath);
|
||||
if (!await cfgFile.exists()) return false;
|
||||
final str = (await cfgFile.readAsString()).replaceAll(" ", "");
|
||||
return str.contains("sys_languages=$lang") &&
|
||||
@ -573,13 +576,13 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
|
||||
for (var scInstallPath in homeState.scInstallPaths) {
|
||||
// 读取游戏安装文件夹
|
||||
final scDataDir = Directory("$scInstallPath\\data\\Localization");
|
||||
final scDataDir = Directory("$scInstallPath\\data\\Localization".platformPath);
|
||||
// 扫描目录确认已安装的语言
|
||||
final dirList = await scDataDir.list().toList();
|
||||
for (var element in dirList) {
|
||||
for (var lang in languageSupport.keys) {
|
||||
if (element.path.contains(lang) && await _getLangCfgEnableLang(lang: lang, gamePath: scInstallPath)) {
|
||||
final installedVersion = await _getInstalledIniVersion("${element.path}\\global.ini");
|
||||
final installedVersion = await _getInstalledIniVersion("${element.path}\\global.ini".platformPath);
|
||||
if (installedVersion == S.current.home_action_info_game_built_in ||
|
||||
installedVersion == S.current.localization_info_custom_files) {
|
||||
continue;
|
||||
|
||||
@ -104,11 +104,11 @@ class SettingsUIModel extends _$SettingsUIModel {
|
||||
final fileName = r.files.first.path!.platformPath;
|
||||
dPrint(fileName);
|
||||
final fileNameRegExp = RegExp(
|
||||
r'^(.*\\StarCitizen\\.*\\)Bin64\\StarCitizen\.exe$'.platformPath,
|
||||
r'^(.*[/\\]starcitizen[/\\].*[/\\])bin64[/\\]starcitizen\.exe$',
|
||||
caseSensitive: false,
|
||||
);
|
||||
if (fileNameRegExp.hasMatch(fileName)) {
|
||||
RegExp pathRegex = RegExp(r'\\[^\\]+\\Bin64\\StarCitizen\.exe$'.platformPath, caseSensitive: false);
|
||||
RegExp pathRegex = RegExp(r'[/\\][^/\\]+[/\\]bin64[/\\]starcitizen\.exe$', caseSensitive: false);
|
||||
String extractedPath = fileName.replaceFirst(pathRegex, '');
|
||||
await _saveCustomPath("custom_game_path", extractedPath);
|
||||
if (!context.mounted) return;
|
||||
|
||||
@ -70,8 +70,11 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
|
||||
workingText.value = S.current.tools_rsi_launcher_enhance_working_msg1;
|
||||
if ((await SystemHelper.getPID("RSI Launcher")).isNotEmpty) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_rsi_launcher_running_warning,
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35));
|
||||
showToast(
|
||||
context,
|
||||
S.current.tools_action_info_rsi_launcher_running_warning,
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35),
|
||||
);
|
||||
workingText.value = "";
|
||||
return;
|
||||
}
|
||||
@ -92,16 +95,16 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
|
||||
|
||||
return ContentDialog(
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .48),
|
||||
title: Row(children: [
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
FluentIcons.back,
|
||||
size: 22,
|
||||
),
|
||||
onPressed: workingText.value.isEmpty ? Navigator.of(context).pop : null),
|
||||
const SizedBox(width: 12),
|
||||
Text(S.current.tools_rsi_launcher_enhance_title),
|
||||
]),
|
||||
title: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(FluentIcons.back, size: 22),
|
||||
onPressed: workingText.value.isEmpty ? Navigator.of(context).pop : null,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(S.current.tools_rsi_launcher_enhance_title),
|
||||
],
|
||||
),
|
||||
content: AnimatedSize(
|
||||
duration: const Duration(milliseconds: 130),
|
||||
child: Column(
|
||||
@ -112,17 +115,16 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
|
||||
InfoBar(
|
||||
title: const SizedBox(),
|
||||
content: Text(S.current.home_localization_action_rsi_launcher_no_game_path_msg),
|
||||
style: InfoBarThemeData(decoration: (severity) {
|
||||
return BoxDecoration(
|
||||
color: Colors.orange,
|
||||
);
|
||||
}, iconColor: (severity) {
|
||||
return Colors.white;
|
||||
}),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
style: InfoBarThemeData(
|
||||
decoration: (severity) {
|
||||
return BoxDecoration(color: Colors.orange);
|
||||
},
|
||||
iconColor: (severity) {
|
||||
return Colors.white;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
if (workingText.value.isNotEmpty) ...[
|
||||
Center(
|
||||
@ -144,19 +146,17 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
|
||||
Expanded(
|
||||
child: Text(
|
||||
S.current.tools_rsi_launcher_enhance_msg_version(assarState.value?.version ?? ""),
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
),
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: .6)),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
S.current.tools_rsi_launcher_enhance_msg_patch_status((assarState.value?.isPatchInstalled ?? false)
|
||||
? S.current.localization_info_installed
|
||||
: S.current.tools_action_info_not_installed),
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
S.current.tools_rsi_launcher_enhance_msg_patch_status(
|
||||
(assarState.value?.isPatchInstalled ?? false)
|
||||
? S.current.localization_info_installed
|
||||
: S.current.tools_action_info_not_installed,
|
||||
),
|
||||
)
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: .6)),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (assarState.value?.serverData.isEmpty ?? true) ...[
|
||||
@ -165,6 +165,84 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
|
||||
const SizedBox(height: 24),
|
||||
if (assarState.value?.enabledLocalization != null)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.current.tools_rsi_launcher_enhance_title_localization),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
S.current.tools_rsi_launcher_enhance_subtitle_localization,
|
||||
style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .6)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ComboBox(
|
||||
items: [
|
||||
for (final key in supportLocalizationMap.keys)
|
||||
ComboBoxItem(value: key, child: Text(supportLocalizationMap[key]!)),
|
||||
],
|
||||
value: assarState.value?.enabledLocalization,
|
||||
onChanged: (v) {
|
||||
assarState.value = assarState.value!.copyWith(enabledLocalization: v);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
if (assarState.value?.enableDownloaderBoost != null) ...[
|
||||
IconButton(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(top: 3, bottom: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(expandEnhance.value ? FluentIcons.chevron_up : FluentIcons.chevron_down),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
expandEnhance.value
|
||||
? S.current.tools_rsi_launcher_enhance_action_fold
|
||||
: S.current.tools_rsi_launcher_enhance_action_expand,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (!expandEnhance.value) {
|
||||
final userOK = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.tools_rsi_launcher_enhance_note_title,
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [Text(S.current.tools_rsi_launcher_enhance_note_msg)],
|
||||
),
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55),
|
||||
);
|
||||
if (!userOK) return;
|
||||
}
|
||||
expandEnhance.value = !expandEnhance.value;
|
||||
},
|
||||
),
|
||||
if (expandEnhance.value)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
decoration: BoxDecoration(
|
||||
@ -174,111 +252,38 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.current.tools_rsi_launcher_enhance_title_localization),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
S.current.tools_rsi_launcher_enhance_subtitle_localization,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
ComboBox(
|
||||
items: [
|
||||
for (final key in supportLocalizationMap.keys)
|
||||
ComboBoxItem(value: key, child: Text(supportLocalizationMap[key]!))
|
||||
],
|
||||
value: assarState.value?.enabledLocalization,
|
||||
onChanged: (v) {
|
||||
assarState.value = assarState.value!.copyWith(enabledLocalization: v);
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
const SizedBox(height: 3),
|
||||
if (assarState.value?.enableDownloaderBoost != null) ...[
|
||||
IconButton(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(top: 3, bottom: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(expandEnhance.value ? FluentIcons.chevron_up : FluentIcons.chevron_down),
|
||||
const SizedBox(width: 12),
|
||||
Text(expandEnhance.value
|
||||
? S.current.tools_rsi_launcher_enhance_action_fold
|
||||
: S.current.tools_rsi_launcher_enhance_action_expand),
|
||||
],
|
||||
))),
|
||||
],
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (!expandEnhance.value) {
|
||||
final userOK = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.tools_rsi_launcher_enhance_note_title,
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.current.tools_rsi_launcher_enhance_note_msg),
|
||||
Text(S.current.tools_rsi_launcher_enhance_title_download_booster),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
S.current.tools_rsi_launcher_enhance_subtitle_download_booster,
|
||||
style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .6)),
|
||||
),
|
||||
],
|
||||
),
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55));
|
||||
if (!userOK) return;
|
||||
}
|
||||
expandEnhance.value = !expandEnhance.value;
|
||||
},
|
||||
),
|
||||
if (expandEnhance.value)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.current.tools_rsi_launcher_enhance_title_download_booster),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
S.current.tools_rsi_launcher_enhance_subtitle_download_booster,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
ToggleSwitch(
|
||||
onChanged: (value) {
|
||||
assarState.value = assarState.value?.copyWith(enableDownloaderBoost: value);
|
||||
},
|
||||
checked: assarState.value?.enableDownloaderBoost ?? false,
|
||||
)
|
||||
])),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
Center(
|
||||
child: FilledButton(
|
||||
onPressed: doInstall,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6),
|
||||
child: Text(S.current.tools_rsi_launcher_enhance_action_install),
|
||||
))),
|
||||
child: FilledButton(
|
||||
onPressed: doInstall,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6),
|
||||
child: Text(S.current.tools_rsi_launcher_enhance_action_install),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
@ -300,7 +305,7 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
|
||||
return null;
|
||||
}
|
||||
dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherPath ==== $lPath");
|
||||
final dataPath = "${lPath}resources\\app.asar";
|
||||
final dataPath = "${lPath}resources\\app.asar".platformPath;
|
||||
dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherDataPath ==== $dataPath");
|
||||
try {
|
||||
final data = await asar_api.getRsiLauncherAsarData(asarPath: dataPath);
|
||||
@ -333,7 +338,10 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
Future<String> _loadEnhanceData(
|
||||
BuildContext context, WidgetRef ref, ValueNotifier<RSILauncherStateData?> assarState) async {
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
ValueNotifier<RSILauncherStateData?> assarState,
|
||||
) async {
|
||||
final globalModel = ref.read(appGlobalModelProvider);
|
||||
final enhancePath = "${globalModel.applicationSupportDir}/launcher_enhance_data";
|
||||
|
||||
@ -418,11 +426,13 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
|
||||
for (final line in serverScriptLines) {
|
||||
final lineTrim = line.trim();
|
||||
if (lineTrim.startsWith(SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START)) {
|
||||
scriptBuffer
|
||||
.writeln("$SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START\"${assarState.value!.enabledLocalization}\";");
|
||||
scriptBuffer.writeln(
|
||||
"$SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START\"${assarState.value!.enabledLocalization}\";",
|
||||
);
|
||||
} else if (lineTrim.startsWith(SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START)) {
|
||||
scriptBuffer
|
||||
.writeln("$SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START${assarState.value!.enableDownloaderBoost};");
|
||||
scriptBuffer.writeln(
|
||||
"$SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START${assarState.value!.enableDownloaderBoost};",
|
||||
);
|
||||
} else {
|
||||
scriptBuffer.writeln(line);
|
||||
}
|
||||
|
||||
@ -72,13 +72,14 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
}
|
||||
try {
|
||||
items = [
|
||||
ToolsItemData(
|
||||
"systemnfo",
|
||||
S.current.tools_action_view_system_info,
|
||||
S.current.tools_action_info_view_critical_system_info,
|
||||
const Icon(FluentIcons.system, size: 24),
|
||||
onTap: () => _showSystemInfo(context),
|
||||
),
|
||||
if (Platform.isWindows)
|
||||
ToolsItemData(
|
||||
"systemnfo",
|
||||
S.current.tools_action_view_system_info,
|
||||
S.current.tools_action_info_view_critical_system_info,
|
||||
const Icon(FluentIcons.system, size: 24),
|
||||
onTap: () => _showSystemInfo(context),
|
||||
),
|
||||
];
|
||||
|
||||
// 年度报告入口 logic
|
||||
@ -108,13 +109,14 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
if (!context.mounted) return;
|
||||
items.add(await _addP4kCard(context));
|
||||
items.addAll([
|
||||
ToolsItemData(
|
||||
"hosts_booster",
|
||||
S.current.tools_action_hosts_acceleration_experimental,
|
||||
S.current.tools_action_info_hosts_acceleration_experimental_tip,
|
||||
const Icon(FluentIcons.virtual_network, size: 24),
|
||||
onTap: () => _doHostsBooster(context),
|
||||
),
|
||||
if (Platform.isWindows)
|
||||
ToolsItemData(
|
||||
"hosts_booster",
|
||||
S.current.tools_action_hosts_acceleration_experimental,
|
||||
S.current.tools_action_info_hosts_acceleration_experimental_tip,
|
||||
const Icon(FluentIcons.virtual_network, size: 24),
|
||||
onTap: () => _doHostsBooster(context),
|
||||
),
|
||||
ToolsItemData(
|
||||
"log_analyze",
|
||||
S.current.log_analyzer_title,
|
||||
@ -129,20 +131,22 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
const Icon(FluentIcons.c_plus_plus, size: 24),
|
||||
onTap: () => rsiEnhance(context),
|
||||
),
|
||||
ToolsItemData(
|
||||
"reinstall_eac",
|
||||
S.current.tools_action_reinstall_easyanticheat,
|
||||
S.current.tools_action_info_reinstall_eac,
|
||||
const Icon(FluentIcons.game, size: 24),
|
||||
onTap: () => _reinstallEAC(context),
|
||||
),
|
||||
ToolsItemData(
|
||||
"rsilauncher_admin_mode",
|
||||
S.current.tools_action_rsi_launcher_admin_mode,
|
||||
S.current.tools_action_info_run_rsi_as_admin,
|
||||
const Icon(FluentIcons.admin, size: 24),
|
||||
onTap: () => _adminRSILauncher(context),
|
||||
),
|
||||
if (Platform.isWindows)
|
||||
ToolsItemData(
|
||||
"reinstall_eac",
|
||||
S.current.tools_action_reinstall_easyanticheat,
|
||||
S.current.tools_action_info_reinstall_eac,
|
||||
const Icon(FluentIcons.game, size: 24),
|
||||
onTap: () => _reinstallEAC(context),
|
||||
),
|
||||
if (Platform.isWindows)
|
||||
ToolsItemData(
|
||||
"rsilauncher_admin_mode",
|
||||
S.current.tools_action_rsi_launcher_admin_mode,
|
||||
S.current.tools_action_info_run_rsi_as_admin,
|
||||
const Icon(FluentIcons.admin, size: 24),
|
||||
onTap: () => _adminRSILauncher(context),
|
||||
),
|
||||
ToolsItemData(
|
||||
"unp4kc",
|
||||
S.current.tools_action_unp4k,
|
||||
@ -169,8 +173,10 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
if (!context.mounted) return;
|
||||
items.add(await _addPhotographyCard(context));
|
||||
state = state.copyWith(items: items);
|
||||
if (!context.mounted) return;
|
||||
items.addAll(await _addNvmePatchCard(context));
|
||||
if (Platform.isWindows) {
|
||||
if (!context.mounted) return;
|
||||
items.addAll(await _addNvmePatchCard(context));
|
||||
}
|
||||
state = state.copyWith(items: items, isItemLoading: false);
|
||||
} catch (e) {
|
||||
if (!context.mounted) return;
|
||||
@ -250,7 +256,10 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
Future<ToolsItemData> _addShaderCard(BuildContext context) async {
|
||||
final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath();
|
||||
final shaderSize =
|
||||
((await SystemHelper.getDirLen(gameShaderCachePath ?? "", skipPath: ["$gameShaderCachePath\\Crashes"])) /
|
||||
((await SystemHelper.getDirLen(
|
||||
gameShaderCachePath ?? "",
|
||||
skipPath: ["$gameShaderCachePath\\Crashes".platformPath],
|
||||
)) /
|
||||
1024 /
|
||||
1024)
|
||||
.toStringAsFixed(4);
|
||||
@ -300,7 +309,8 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
|
||||
// 使用最新版本
|
||||
final latestVersion = versions.first;
|
||||
final settingsPath = "$gameShaderCachePath\\starcitizen_$latestVersion\\GraphicsSettings\\GraphicsSettings.json";
|
||||
final settingsPath =
|
||||
"$gameShaderCachePath\\starcitizen_$latestVersion\\GraphicsSettings\\GraphicsSettings.json".platformPath;
|
||||
|
||||
final file = File(settingsPath);
|
||||
if (!await file.exists()) return (-1, latestVersion);
|
||||
@ -627,6 +637,7 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
final result = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (dialogContext) => ContentDialog(
|
||||
constraints: BoxConstraints(maxWidth: 380),
|
||||
title: Text(S.current.tools_shader_clean_dialog_title),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@ -4,10 +4,10 @@ project(runner LANGUAGES CXX)
|
||||
|
||||
# The name of the executable created for the application. Change this to change
|
||||
# the on-disk name of your application.
|
||||
set(BINARY_NAME "app")
|
||||
set(BINARY_NAME "starcitizen_doctor")
|
||||
# The unique GTK application identifier for this application. See:
|
||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||
set(APPLICATION_ID "com.example.app")
|
||||
set(APPLICATION_ID "com.starcitizen_doctor")
|
||||
|
||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||
# versions of CMake.
|
||||
@ -53,6 +53,7 @@ add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||
# System-level dependencies.
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||
pkg_check_modules(GDK_PIXBUF REQUIRED IMPORTED_TARGET gdk-pixbuf-2.0)
|
||||
|
||||
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
|
||||
|
||||
@ -73,6 +74,7 @@ apply_standard_settings(${BINARY_NAME})
|
||||
# Add dependency libraries. Add any application-specific dependencies here.
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GDK_PIXBUF)
|
||||
|
||||
# Run the Flutter tool portions of the build. This must not be removed.
|
||||
add_dependencies(${BINARY_NAME} flutter_assemble)
|
||||
@ -143,3 +145,13 @@ if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endif()
|
||||
|
||||
# Install application icons
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/icons/hicolor"
|
||||
DESTINATION "share/icons"
|
||||
COMPONENT Runtime)
|
||||
|
||||
# Install desktop file
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/com.starcitizen_doctor.desktop"
|
||||
DESTINATION "share/applications"
|
||||
COMPONENT Runtime)
|
||||
|
||||
9
linux/com.starcitizen_doctor.desktop
Normal file
9
linux/com.starcitizen_doctor.desktop
Normal file
@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=SC汉化盒子
|
||||
Comment=Star Citizen Tool Box for Chinese players
|
||||
Exec=starcitizen_doctor
|
||||
Icon=starcitizen_doctor
|
||||
Categories=Utility;Game;
|
||||
Terminal=false
|
||||
StartupNotify=true
|
||||
BIN
linux/icons/hicolor/128x128/apps/starcitizen_doctor.png
Normal file
BIN
linux/icons/hicolor/128x128/apps/starcitizen_doctor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
BIN
linux/icons/hicolor/16x16/apps/starcitizen_doctor.png
Normal file
BIN
linux/icons/hicolor/16x16/apps/starcitizen_doctor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 879 B |
BIN
linux/icons/hicolor/256x256/apps/starcitizen_doctor.png
Normal file
BIN
linux/icons/hicolor/256x256/apps/starcitizen_doctor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
linux/icons/hicolor/32x32/apps/starcitizen_doctor.png
Normal file
BIN
linux/icons/hicolor/32x32/apps/starcitizen_doctor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
BIN
linux/icons/hicolor/48x48/apps/starcitizen_doctor.png
Normal file
BIN
linux/icons/hicolor/48x48/apps/starcitizen_doctor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
BIN
linux/icons/hicolor/64x64/apps/starcitizen_doctor.png
Normal file
BIN
linux/icons/hicolor/64x64/apps/starcitizen_doctor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
@ -4,6 +4,7 @@
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
#include <gdk/gdkx.h>
|
||||
#endif
|
||||
#include <glib.h>
|
||||
|
||||
#include "flutter/generated_plugin_registrant.h"
|
||||
|
||||
@ -42,11 +43,11 @@ static void my_application_activate(GApplication* application) {
|
||||
if (use_header_bar) {
|
||||
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
||||
gtk_widget_show(GTK_WIDGET(header_bar));
|
||||
gtk_header_bar_set_title(header_bar, "app");
|
||||
gtk_header_bar_set_title(header_bar, "SC汉化盒子");
|
||||
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
||||
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
||||
} else {
|
||||
gtk_window_set_title(window, "app");
|
||||
gtk_window_set_title(window, "SC汉化盒子");
|
||||
}
|
||||
|
||||
gtk_window_set_default_size(window, 1280, 720);
|
||||
|
||||
@ -71,8 +71,8 @@ pub async fn get_rsi_launcher_asar_data(asar_path: &str) -> anyhow::Result<RsiLa
|
||||
let mut main_js_content: Vec<u8> = Vec::new();
|
||||
asar.files().iter().for_each(|v| {
|
||||
let (path, file) = v;
|
||||
let path_string = path.clone().into_os_string().into_string().unwrap();
|
||||
if path_string.starts_with("app\\static\\js\\main.") && path_string.ends_with(".js") {
|
||||
let path_string = path.clone().into_os_string().into_string().unwrap().replace("\\", "/");
|
||||
if path_string.starts_with("app/static/js/main.") && path_string.ends_with(".js") {
|
||||
main_js_path = path_string;
|
||||
main_js_content = file.data().to_vec();
|
||||
}
|
||||
|
||||
@ -12,12 +12,16 @@ use tao::dpi::{LogicalPosition, LogicalSize};
|
||||
use tao::event::{Event, WindowEvent};
|
||||
use tao::event_loop::{ControlFlow, EventLoop, EventLoopBuilder};
|
||||
use tao::platform::run_return::EventLoopExtRunReturn;
|
||||
|
||||
use tao::window::{Icon, Window, WindowBuilder};
|
||||
use wry::{NewWindowResponse, PageLoadEvent, WebView, WebViewBuilder};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
use tao::platform::windows::EventLoopBuilderExtWindows;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use tao::platform::unix::EventLoopBuilderExtUnix;
|
||||
|
||||
use crate::api::webview_api::{
|
||||
WebViewCommand, WebViewConfiguration, WebViewEvent, WebViewNavigationState,
|
||||
};
|
||||
@ -263,14 +267,10 @@ fn run_webview_loop(
|
||||
is_closed: Arc<AtomicBool>,
|
||||
) {
|
||||
// Create event loop with any_thread support for non-main thread execution
|
||||
#[cfg(target_os = "windows")]
|
||||
let mut event_loop: EventLoop<UserEvent> = EventLoopBuilder::with_user_event()
|
||||
.with_any_thread(true)
|
||||
.build();
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let mut event_loop: EventLoop<UserEvent> = EventLoopBuilder::with_user_event().build();
|
||||
|
||||
let proxy = event_loop.create_proxy();
|
||||
|
||||
// Load window icon from embedded ICO file
|
||||
|
||||
Loading…
Reference in New Issue
Block a user