feat: linux support

This commit is contained in:
xkeyC 2025-12-23 22:40:05 +08:00
parent 66ead87d47
commit bba2dbd360
20 changed files with 514 additions and 472 deletions

View File

@ -8,7 +8,15 @@ import 'package:starcitizen_doctor/common/utils/log.dart';
class SCLoggerHelper { class SCLoggerHelper {
static Future<String?> getLogFilePath() async { 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; Map<String, String> envVars = Platform.environment;
final appDataPath = envVars["appdata"]; final appDataPath = envVars["appdata"];
if (appDataPath == null) { if (appDataPath == null) {
@ -21,6 +29,14 @@ class SCLoggerHelper {
} }
static Future<String?> getShaderCachePath() async { 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; Map<String, String> envVars = Platform.environment;
final appDataPath = envVars["LOCALAPPDATA"]; final appDataPath = envVars["LOCALAPPDATA"];
if (appDataPath == null) { if (appDataPath == null) {
@ -31,6 +47,23 @@ class SCLoggerHelper {
return scCachePath; 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 { static Future<List?> getLauncherLogList() async {
if (!Platform.isWindows) return []; if (!Platform.isWindows) return [];
try { try {

View File

@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:hive_ce/hive.dart'; 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/utils/log.dart';
import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32; import 'package:starcitizen_doctor/common/rust/api/win32_api.dart' as win32;
@ -41,7 +42,7 @@ class SystemHelper {
if (path != null && path != "") { if (path != null && path != "") {
if (await File(path).exists()) { if (await File(path).exists()) {
if (skipEXE) { if (skipEXE) {
return "${path.toString().replaceAll("\\RSI Launcher.exe", "")}\\"; return "${path.toString().replaceAll("\\RSI Launcher.exe".platformPath, "")}\\".platformPath;
} }
return path; return path;
} }

View File

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:fluent_ui/fluent_ui.dart'; import 'package:fluent_ui/fluent_ui.dart';
@ -112,7 +113,7 @@ class MultiWindowManager {
await Window.initialize(); await Window.initialize();
if (windowAppState.windowsVersion >= 10) { if (Platform.isWindows && windowAppState.windowsVersion >= 10) {
await Window.setEffect(effect: WindowEffect.acrylic); await Window.setEffect(effect: WindowEffect.acrylic);
} }

View File

@ -10,6 +10,7 @@ import 'package:path_provider/path_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:starcitizen_doctor/api/analytics.dart'; import 'package:starcitizen_doctor/api/analytics.dart';
import 'package:starcitizen_doctor/common/helper/log_helper.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/common/utils/log.dart';
import 'package:starcitizen_doctor/data/app_unp4k_p4k_item_data.dart'; import 'package:starcitizen_doctor/data/app_unp4k_p4k_item_data.dart';
import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart'; import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart';
@ -80,7 +81,7 @@ class Unp4kCModel extends _$Unp4kCModel {
void _init() async { void _init() async {
final gamePath = getGamePath(); final gamePath = getGamePath();
final gameP4kPath = "$gamePath\\Data.p4k"; final gameP4kPath = "$gamePath\\Data.p4k".platformPath;
try { try {
state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading); state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading);

View File

@ -30,13 +30,9 @@ class LocalizationDialogUI extends HookConsumerWidget {
return ContentDialog( return ContentDialog(
title: makeTitle(context, model, state), title: makeTitle(context, model, state),
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: MediaQuery maxWidth: MediaQuery.of(context).size.width * .7,
.of(context) minHeight: MediaQuery.of(context).size.height * .9,
.size ),
.width * .7, minHeight: MediaQuery
.of(context)
.size
.height * .9),
content: Padding( content: Padding(
padding: const EdgeInsets.only(left: 12, right: 12, top: 12), padding: const EdgeInsets.only(left: 12, right: 12, top: 12),
child: SingleChildScrollView( child: SingleChildScrollView(
@ -44,7 +40,8 @@ class LocalizationDialogUI extends HookConsumerWidget {
children: [ children: [
AnimatedSize( AnimatedSize(
duration: const Duration(milliseconds: 130), duration: const Duration(milliseconds: 130),
child: state.patchStatus?.key == true && child:
state.patchStatus?.key == true &&
state.patchStatus?.value == S.current.home_action_info_game_built_in state.patchStatus?.value == S.current.home_action_info_game_built_in
? Padding( ? Padding(
padding: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.only(bottom: 12),
@ -52,40 +49,35 @@ class LocalizationDialogUI extends HookConsumerWidget {
title: Text(S.current.home_action_info_warning), title: Text(S.current.home_action_info_warning),
content: Text(S.current.localization_info_machine_translation_warning), content: Text(S.current.localization_info_machine_translation_warning),
severity: InfoBarSeverity.info, severity: InfoBarSeverity.info,
style: InfoBarThemeData(decoration: (severity) { style: InfoBarThemeData(
decoration: (severity) {
return const BoxDecoration(color: Color.fromRGBO(155, 7, 7, 1.0)); return const BoxDecoration(color: Color.fromRGBO(155, 7, 7, 1.0));
}, iconColor: (severity) { },
iconColor: (severity) {
return Colors.white; return Colors.white;
}), },
),
), ),
) )
: SizedBox( : SizedBox(width: MediaQuery.of(context).size.width),
width: MediaQuery
.of(context)
.size
.width,
),
), ),
makeToolsListContainer(context, model, state), makeToolsListContainer(context, model, state),
makeListContainer( makeListContainer(S.current.localization_info_translation, [
S.current.localization_info_translation,
[
if (state.patchStatus == null) if (state.patchStatus == null)
makeLoading(context) makeLoading(context)
else else ...[
...[
const SizedBox(height: 6), const SizedBox(height: 6),
Row( Row(
children: [ children: [
Center( Center(
child: Text(S.current.localization_info_enabled( child: Text(
LocalizationUIModel.languageSupport[state.selectedLanguage] ?? "")), S.current.localization_info_enabled(
LocalizationUIModel.languageSupport[state.selectedLanguage] ?? "",
),
),
), ),
const Spacer(), const Spacer(),
ToggleSwitch( ToggleSwitch(checked: state.patchStatus?.key == true, onChanged: model.updateLangCfg),
checked: state.patchStatus?.key == true,
onChanged: model.updateLangCfg,
)
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
@ -94,15 +86,18 @@ class LocalizationDialogUI extends HookConsumerWidget {
Expanded( Expanded(
child: Row( child: Row(
children: [ children: [
Text(S.current.localization_info_installed_version( Text(
"${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? S S.current.localization_info_installed_version(
.current.home_localization_msg_version_advanced : ""}")), "${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? S.current.home_localization_msg_version_advanced : ""}",
),
),
SizedBox(width: 24), SizedBox(width: 24),
if (state.installedCommunityInputMethodSupportVersion != null) if (state.installedCommunityInputMethodSupportVersion != null)
Text( Text(
S.current.input_method_community_input_method_support_version( S.current.input_method_community_input_method_support_version(
state.installedCommunityInputMethodSupportVersion ?? "?"), state.installedCommunityInputMethodSupportVersion ?? "?",
) ),
),
], ],
), ),
), ),
@ -120,7 +115,8 @@ class LocalizationDialogUI extends HookConsumerWidget {
Text(S.current.localization_action_translation_feedback), Text(S.current.localization_action_translation_feedback),
], ],
), ),
)), ),
),
const SizedBox(width: 16), const SizedBox(width: 16),
Button( Button(
onPressed: model.doDelIniFile(), onPressed: model.doDelIniFile(),
@ -133,21 +129,18 @@ class LocalizationDialogUI extends HookConsumerWidget {
Text(S.current.localization_action_uninstall_translation), Text(S.current.localization_action_uninstall_translation),
], ],
), ),
)), ),
),
], ],
), ),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Container( Container(color: Colors.white.withValues(alpha: .1), height: 1),
color: Colors.white.withValues(alpha: .1),
height: 1,
),
const SizedBox(height: 12), const SizedBox(height: 12),
if (state.apiLocalizationData == null) if (state.apiLocalizationData == null)
makeLoading(context) makeLoading(context)
else else if (state.apiLocalizationData!.isEmpty)
if (state.apiLocalizationData!.isEmpty)
Center( Center(
child: Text( child: Text(
S.current.localization_info_no_translation_available, S.current.localization_info_no_translation_available,
@ -166,10 +159,9 @@ class LocalizationDialogUI extends HookConsumerWidget {
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
itemCount: state.apiLocalizationData?.length ?? 0, itemCount: state.apiLocalizationData?.length ?? 0,
) ),
], ],
], ], context),
context),
], ],
), ),
), ),
@ -177,8 +169,13 @@ class LocalizationDialogUI extends HookConsumerWidget {
); );
} }
Widget makeRemoteList(BuildContext context, LocalizationUIModel model, MapEntry<String, ScLocalizationData> item, Widget makeRemoteList(
LocalizationUIState state, int index) { BuildContext context,
LocalizationUIModel model,
MapEntry<String, ScLocalizationData> item,
LocalizationUIState state,
int index,
) {
final isWorking = state.workingVersion.isNotEmpty; final isWorking = state.workingVersion.isNotEmpty;
final isMineWorking = state.workingVersion == item.key; final isMineWorking = state.workingVersion == item.key;
final isInstalled = state.patchStatus?.value == item.key; final isInstalled = state.patchStatus?.value == item.key;
@ -207,10 +204,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text("${item.value.info}", style: const TextStyle(fontSize: 19)),
"${item.value.info}",
style: const TextStyle(fontSize: 19),
),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
S.current.localization_info_version_number(item.value.versionName ?? ""), S.current.localization_info_version_number(item.value.versionName ?? ""),
@ -230,12 +224,8 @@ class LocalizationDialogUI extends HookConsumerWidget {
), ),
), ),
if (isMineWorking) if (isMineWorking)
const Padding( const Padding(padding: EdgeInsets.only(right: 12), child: ProgressRing())
padding: EdgeInsets.only(right: 12), else ...[
child: ProgressRing(),
)
else
...[
Icon( Icon(
isInstalled isInstalled
? FluentIcons.check_mark ? FluentIcons.check_mark
@ -252,18 +242,12 @@ class LocalizationDialogUI extends HookConsumerWidget {
: (isItemEnabled : (isItemEnabled
? S.current.localization_action_install ? S.current.localization_action_install
: S.current.localization_info_unavailable), : S.current.localization_info_unavailable),
style: TextStyle( style: TextStyle(color: Colors.white.withValues(alpha: .8)),
color: Colors.white.withValues(alpha: .8),
),
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
if ((!isInstalled) && isItemEnabled) if ((!isInstalled) && isItemEnabled)
Icon( Icon(FluentIcons.chevron_right, size: 14, color: Colors.white.withValues(alpha: .6)),
FluentIcons.chevron_right, ],
size: 14,
color: Colors.white.withValues(alpha: .6),
)
]
], ],
), ),
if (item.value.note != null) ...[ if (item.value.note != null) ...[
@ -272,10 +256,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
"${item.value.note}", "${item.value.note}",
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(color: Colors.white.withValues(alpha: .4), fontSize: 13),
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, Widget makeListContainer(
{List<Widget> actions = const [], bool gridViewMode = false, int gridViewCrossAxisCount = 2}) { String title,
List<Widget> children,
BuildContext context, {
List<Widget> actions = const [],
bool gridViewMode = false,
int gridViewCrossAxisCount = 2,
}) {
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.only(bottom: 12),
child: AnimatedSize( child: AnimatedSize(
duration: const Duration(milliseconds: 130), duration: const Duration(milliseconds: 130),
child: Container( child: Container(
decoration: BoxDecoration(color: FluentTheme decoration: BoxDecoration(color: FluentTheme.of(context).cardColor, borderRadius: BorderRadius.circular(7)),
.of(context)
.cardColor, borderRadius: BorderRadius.circular(7)),
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 12, bottom: 12, left: 24, right: 24), padding: const EdgeInsets.only(top: 12, bottom: 12, left: 24, right: 24),
child: Column( child: Column(
@ -303,21 +288,13 @@ class LocalizationDialogUI extends HookConsumerWidget {
children: [ children: [
Row( Row(
children: [ children: [
Text( Text(title, style: const TextStyle(fontSize: 22)),
title,
style: const TextStyle(fontSize: 22),
),
const Spacer(), const Spacer(),
if (actions.isNotEmpty) ...actions, if (actions.isNotEmpty) ...actions,
], ],
), ),
const SizedBox( const SizedBox(height: 6),
height: 6, Container(color: Colors.white.withValues(alpha: .1), height: 1),
),
Container(
color: Colors.white.withValues(alpha: .1),
height: 1,
),
const SizedBox(height: 12), const SizedBox(height: 12),
if (gridViewMode) if (gridViewMode)
AlignedGridView.count( AlignedGridView.count(
@ -332,7 +309,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
itemCount: children.length, itemCount: children.length,
) )
else else
...children ...children,
], ],
), ),
), ),
@ -344,37 +321,30 @@ class LocalizationDialogUI extends HookConsumerWidget {
Widget makeTitle(BuildContext context, LocalizationUIModel model, LocalizationUIState state) { Widget makeTitle(BuildContext context, LocalizationUIModel model, LocalizationUIState state) {
return Row( return Row(
children: [ children: [
IconButton( IconButton(icon: const Icon(FluentIcons.back, size: 22), onPressed: model.onBack(context)),
icon: const Icon(
FluentIcons.back,
size: 22,
),
onPressed: model.onBack(context)),
const SizedBox(width: 12), const SizedBox(width: 12),
Text(S.current.home_action_localization_management), Text(S.current.home_action_localization_management),
const SizedBox(width: 24), const SizedBox(width: 24),
Text( Expanded(
child: Text(
"${model.getScInstallPath()}", "${model.getScInstallPath()}",
style: const TextStyle(fontSize: 13), style: const TextStyle(fontSize: 13),
maxLines: 1,
overflow: TextOverflow.ellipsis,
), ),
const Spacer(), ),
SizedBox(width: 24),
SizedBox( SizedBox(
height: 36, height: 36,
child: Row( child: Row(
children: [ children: [
Text( Text(S.current.localization_info_language, style: const TextStyle(fontSize: 16)),
S.current.localization_info_language,
style: const TextStyle(fontSize: 16),
),
const SizedBox(width: 12), const SizedBox(width: 12),
ComboBox<String>( ComboBox<String>(
value: state.selectedLanguage, value: state.selectedLanguage,
items: [ items: [
for (final lang in LocalizationUIModel.languageSupport.entries) for (final lang in LocalizationUIModel.languageSupport.entries)
ComboBoxItem( ComboBoxItem(value: lang.key, child: Text(lang.value)),
value: lang.key,
child: Text(lang.value),
)
], ],
onChanged: state.workingVersion.isNotEmpty onChanged: state.workingVersion.isNotEmpty
? null ? null
@ -382,17 +352,15 @@ class LocalizationDialogUI extends HookConsumerWidget {
if (v == null) return; if (v == null) return;
model.selectLang(v); model.selectLang(v);
}, },
) ),
], ],
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Button( Button(
onPressed: model.doRefresh(), onPressed: model.doRefresh(),
child: const Padding( child: const Padding(padding: EdgeInsets.all(6), child: Icon(FluentIcons.refresh)),
padding: EdgeInsets.all(6), ),
child: Icon(FluentIcons.refresh),
)),
], ],
); );
} }
@ -403,10 +371,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
const Icon(FluentIcons.c_plus_plus, size: 24), const Icon(FluentIcons.c_plus_plus, size: 24),
(S.current.home_localization_action_rsi_launcher_localization), (S.current.home_localization_action_rsi_launcher_localization),
), ),
"advanced": ( "advanced": (const Icon(FluentIcons.queue_advanced, size: 24), (S.current.home_localization_action_advanced)),
const Icon(FluentIcons.queue_advanced, size: 24),
(S.current.home_localization_action_advanced),
),
"custom_files": ( "custom_files": (
const Icon(FluentIcons.custom_activity, size: 24), const Icon(FluentIcons.custom_activity, size: 24),
(S.current.home_localization_action_install_customize), (S.current.home_localization_action_install_customize),
@ -453,9 +418,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
: null, : null,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: FluentTheme color: FluentTheme.of(context).cardColor,
.of(context)
.cardColor,
borderRadius: BorderRadius.circular(7), borderRadius: BorderRadius.circular(7),
), ),
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
@ -466,11 +429,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
Text(item.value.$2), Text(item.value.$2),
const SizedBox(width: 12), const SizedBox(width: 12),
const Spacer(), const Spacer(),
Icon( Icon(FluentIcons.chevron_right, size: 14, color: Colors.white.withValues(alpha: .6)),
FluentIcons.chevron_right,
size: 14,
color: Colors.white.withValues(alpha: .6),
)
], ],
), ),
), ),
@ -479,6 +438,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
], ],
context, context,
gridViewMode: true, gridViewMode: true,
gridViewCrossAxisCount: 3); gridViewCrossAxisCount: 3,
);
} }
} }

View File

@ -48,13 +48,12 @@ abstract class LocalizationUIState with _$LocalizationUIState {
class LocalizationUIModel extends _$LocalizationUIModel { class LocalizationUIModel extends _$LocalizationUIModel {
static const languageSupport = {"chinese_(simplified)": NoL10n.langZHS, "chinese_(traditional)": NoL10n.langZHT}; 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 getDownloadDir() => _downloadDir;
Directory get _scDataDir => Directory("${ref.read(homeUIModelProvider).scInstalledPath}\\data"); Directory get _scDataDir => Directory("${ref.read(homeUIModelProvider).scInstalledPath}\\data".platformPath);
File get _cfgFile => File("${_scDataDir.absolute.path}\\system.cfg".platformPath);
File get _cfgFile => File("${_scDataDir.absolute.path}\\system.cfg");
StreamSubscription? _customizeDirListenSub; StreamSubscription? _customizeDirListenSub;
@ -155,7 +154,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
} }
void checkUserCfg(BuildContext context) async { void checkUserCfg(BuildContext context) async {
final userCfgFile = File("$_scInstallPath\\USER.cfg"); final userCfgFile = File("$_scInstallPath\\USER.cfg".platformPath);
if (await userCfgFile.exists()) { if (await userCfgFile.exists()) {
final cfgString = await userCfgFile.readAsString(); final cfgString = await userCfgFile.readAsString();
if (cfgString.contains("g_language") && !cfgString.contains("g_language=${state.selectedLanguage}")) { if (cfgString.contains("g_language") && !cfgString.contains("g_language=${state.selectedLanguage}")) {
@ -241,7 +240,9 @@ class LocalizationUIModel extends _$LocalizationUIModel {
VoidCallback? doDelIniFile() { VoidCallback? doDelIniFile() {
return () async { 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(); if (await iniFile.exists()) await iniFile.delete();
await updateLangCfg(false); await updateLangCfg(false);
await _updateStatus(); await _updateStatus();
@ -249,7 +250,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
} }
String getCustomizeFileName(String path) { String getCustomizeFileName(String path) {
return path.split("\\").last; return path.split("\\".platformPath).last;
} }
Future<void> installFormString( Future<void> installFormString(
@ -261,7 +262,9 @@ class LocalizationUIModel extends _$LocalizationUIModel {
BuildContext? context, BuildContext? context,
}) async { }) async {
dPrint("LocalizationUIModel -> installFormString $versionName"); 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 (versionName.isNotEmpty) {
if (!globalIni.toString().endsWith("\n")) { if (!globalIni.toString().endsWith("\n")) {
globalIni.write("\n"); globalIni.write("\n");
@ -323,7 +326,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
} }
Future<Map<String, String>?> getCommunityInputMethodSupportData() async { 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); final iniFile = File(iniPath);
if (!await iniFile.exists()) { if (!await iniFile.exists()) {
return {}; return {};
@ -351,7 +354,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
} }
Future<(String, String)> getIniContentWithoutCommunityInputMethodSupportData() async { 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); final iniFile = File(iniPath);
if (!await iniFile.exists()) { if (!await iniFile.exists()) {
return ("", ""); return ("", "");
@ -383,7 +386,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
}) async { }) async {
AnalyticsApi.touch("install_localization"); AnalyticsApi.touch("install_localization");
final savePath = File("${_downloadDir.absolute.path}\\${value.versionName}.sclang"); final savePath = File("${_downloadDir.absolute.path}\\${value.versionName}.sclang".platformPath);
try { try {
state = state.copyWith(workingVersion: value.versionName!); state = state.copyWith(workingVersion: value.versionName!);
if (!await savePath.exists()) { if (!await savePath.exists()) {
@ -488,7 +491,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
} }
Future<void> _updateStatus() async { 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( final patchStatus = MapEntry(
await _getLangCfgEnableLang(lang: state.selectedLanguage!), await _getLangCfgEnableLang(lang: state.selectedLanguage!),
await _getInstalledIniVersion(iniPath), await _getInstalledIniVersion(iniPath),
@ -535,7 +538,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
if (gamePath.isEmpty) { if (gamePath.isEmpty) {
gamePath = _scInstallPath; 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; if (!await cfgFile.exists()) return false;
final str = (await cfgFile.readAsString()).replaceAll(" ", ""); final str = (await cfgFile.readAsString()).replaceAll(" ", "");
return str.contains("sys_languages=$lang") && return str.contains("sys_languages=$lang") &&
@ -573,13 +576,13 @@ class LocalizationUIModel extends _$LocalizationUIModel {
for (var scInstallPath in homeState.scInstallPaths) { 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(); final dirList = await scDataDir.list().toList();
for (var element in dirList) { for (var element in dirList) {
for (var lang in languageSupport.keys) { for (var lang in languageSupport.keys) {
if (element.path.contains(lang) && await _getLangCfgEnableLang(lang: lang, gamePath: scInstallPath)) { 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 || if (installedVersion == S.current.home_action_info_game_built_in ||
installedVersion == S.current.localization_info_custom_files) { installedVersion == S.current.localization_info_custom_files) {
continue; continue;

View File

@ -104,11 +104,11 @@ class SettingsUIModel extends _$SettingsUIModel {
final fileName = r.files.first.path!.platformPath; final fileName = r.files.first.path!.platformPath;
dPrint(fileName); dPrint(fileName);
final fileNameRegExp = RegExp( final fileNameRegExp = RegExp(
r'^(.*\\StarCitizen\\.*\\)Bin64\\StarCitizen\.exe$'.platformPath, r'^(.*[/\\]starcitizen[/\\].*[/\\])bin64[/\\]starcitizen\.exe$',
caseSensitive: false, caseSensitive: false,
); );
if (fileNameRegExp.hasMatch(fileName)) { 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, ''); String extractedPath = fileName.replaceFirst(pathRegex, '');
await _saveCustomPath("custom_game_path", extractedPath); await _saveCustomPath("custom_game_path", extractedPath);
if (!context.mounted) return; if (!context.mounted) return;

View File

@ -70,8 +70,11 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
workingText.value = S.current.tools_rsi_launcher_enhance_working_msg1; workingText.value = S.current.tools_rsi_launcher_enhance_working_msg1;
if ((await SystemHelper.getPID("RSI Launcher")).isNotEmpty) { if ((await SystemHelper.getPID("RSI Launcher")).isNotEmpty) {
if (!context.mounted) return; if (!context.mounted) return;
showToast(context, S.current.tools_action_info_rsi_launcher_running_warning, showToast(
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35)); context,
S.current.tools_action_info_rsi_launcher_running_warning,
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35),
);
workingText.value = ""; workingText.value = "";
return; return;
} }
@ -92,16 +95,16 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
return ContentDialog( return ContentDialog(
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .48), constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .48),
title: Row(children: [ title: Row(
children: [
IconButton( IconButton(
icon: const Icon( icon: const Icon(FluentIcons.back, size: 22),
FluentIcons.back, onPressed: workingText.value.isEmpty ? Navigator.of(context).pop : null,
size: 22,
), ),
onPressed: workingText.value.isEmpty ? Navigator.of(context).pop : null),
const SizedBox(width: 12), const SizedBox(width: 12),
Text(S.current.tools_rsi_launcher_enhance_title), Text(S.current.tools_rsi_launcher_enhance_title),
]), ],
),
content: AnimatedSize( content: AnimatedSize(
duration: const Duration(milliseconds: 130), duration: const Duration(milliseconds: 130),
child: Column( child: Column(
@ -112,17 +115,16 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
InfoBar( InfoBar(
title: const SizedBox(), title: const SizedBox(),
content: Text(S.current.home_localization_action_rsi_launcher_no_game_path_msg), content: Text(S.current.home_localization_action_rsi_launcher_no_game_path_msg),
style: InfoBarThemeData(decoration: (severity) { style: InfoBarThemeData(
return BoxDecoration( decoration: (severity) {
color: Colors.orange, return BoxDecoration(color: Colors.orange);
); },
}, iconColor: (severity) { iconColor: (severity) {
return Colors.white; return Colors.white;
}), },
), ),
const SizedBox(
height: 12,
), ),
const SizedBox(height: 12),
], ],
if (workingText.value.isNotEmpty) ...[ if (workingText.value.isNotEmpty) ...[
Center( Center(
@ -144,19 +146,17 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
Expanded( Expanded(
child: Text( child: Text(
S.current.tools_rsi_launcher_enhance_msg_version(assarState.value?.version ?? ""), S.current.tools_rsi_launcher_enhance_msg_version(assarState.value?.version ?? ""),
style: TextStyle( style: TextStyle(color: Colors.white.withValues(alpha: .6)),
color: Colors.white.withValues(alpha: .6),
),
), ),
), ),
Text( Text(
S.current.tools_rsi_launcher_enhance_msg_patch_status((assarState.value?.isPatchInstalled ?? false) S.current.tools_rsi_launcher_enhance_msg_patch_status(
(assarState.value?.isPatchInstalled ?? false)
? S.current.localization_info_installed ? S.current.localization_info_installed
: S.current.tools_action_info_not_installed), : S.current.tools_action_info_not_installed,
style: TextStyle( ),
color: Colors.white.withValues(alpha: .6), style: TextStyle(color: Colors.white.withValues(alpha: .6)),
), ),
)
], ],
), ),
if (assarState.value?.serverData.isEmpty ?? true) ...[ if (assarState.value?.serverData.isEmpty ?? true) ...[
@ -181,17 +181,15 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
const SizedBox(height: 3), const SizedBox(height: 3),
Text( Text(
S.current.tools_rsi_launcher_enhance_subtitle_localization, S.current.tools_rsi_launcher_enhance_subtitle_localization,
style: TextStyle( style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .6)),
fontSize: 13,
color: Colors.white.withValues(alpha: .6),
),
), ),
], ],
)), ),
),
ComboBox( ComboBox(
items: [ items: [
for (final key in supportLocalizationMap.keys) for (final key in supportLocalizationMap.keys)
ComboBoxItem(value: key, child: Text(supportLocalizationMap[key]!)) ComboBoxItem(value: key, child: Text(supportLocalizationMap[key]!)),
], ],
value: assarState.value?.enabledLocalization, value: assarState.value?.enabledLocalization,
onChanged: (v) { onChanged: (v) {
@ -199,7 +197,8 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
}, },
), ),
], ],
)), ),
),
const SizedBox(height: 3), const SizedBox(height: 3),
if (assarState.value?.enableDownloaderBoost != null) ...[ if (assarState.value?.enableDownloaderBoost != null) ...[
IconButton( IconButton(
@ -214,11 +213,15 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
children: [ children: [
Icon(expandEnhance.value ? FluentIcons.chevron_up : FluentIcons.chevron_down), Icon(expandEnhance.value ? FluentIcons.chevron_up : FluentIcons.chevron_down),
const SizedBox(width: 12), const SizedBox(width: 12),
Text(expandEnhance.value Text(
expandEnhance.value
? S.current.tools_rsi_launcher_enhance_action_fold ? S.current.tools_rsi_launcher_enhance_action_fold
: S.current.tools_rsi_launcher_enhance_action_expand), : S.current.tools_rsi_launcher_enhance_action_expand,
),
], ],
))), ),
),
),
], ],
), ),
), ),
@ -229,11 +232,10 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
S.current.tools_rsi_launcher_enhance_note_title, S.current.tools_rsi_launcher_enhance_note_title,
Column( Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [Text(S.current.tools_rsi_launcher_enhance_note_msg)],
Text(S.current.tools_rsi_launcher_enhance_note_msg),
],
), ),
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55)); constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .55),
);
if (!userOK) return; if (!userOK) return;
} }
expandEnhance.value = !expandEnhance.value; expandEnhance.value = !expandEnhance.value;
@ -247,7 +249,8 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
color: FluentTheme.of(context).cardColor, color: FluentTheme.of(context).cardColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Row(children: [ child: Row(
children: [
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -256,20 +259,20 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
const SizedBox(height: 3), const SizedBox(height: 3),
Text( Text(
S.current.tools_rsi_launcher_enhance_subtitle_download_booster, S.current.tools_rsi_launcher_enhance_subtitle_download_booster,
style: TextStyle( style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .6)),
fontSize: 13,
color: Colors.white.withValues(alpha: .6),
),
), ),
], ],
)), ),
),
ToggleSwitch( ToggleSwitch(
onChanged: (value) { onChanged: (value) {
assarState.value = assarState.value?.copyWith(enableDownloaderBoost: value); assarState.value = assarState.value?.copyWith(enableDownloaderBoost: value);
}, },
checked: assarState.value?.enableDownloaderBoost ?? false, checked: assarState.value?.enableDownloaderBoost ?? false,
) ),
])), ],
),
),
], ],
const SizedBox(height: 12), const SizedBox(height: 12),
Center( Center(
@ -278,7 +281,9 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6),
child: Text(S.current.tools_rsi_launcher_enhance_action_install), child: Text(S.current.tools_rsi_launcher_enhance_action_install),
))), ),
),
),
], ],
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
@ -300,7 +305,7 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
return null; return null;
} }
dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherPath ==== $lPath"); dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherPath ==== $lPath");
final dataPath = "${lPath}resources\\app.asar"; final dataPath = "${lPath}resources\\app.asar".platformPath;
dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherDataPath ==== $dataPath"); dPrint("[RsiLauncherEnhanceDialogUI] rsiLauncherDataPath ==== $dataPath");
try { try {
final data = await asar_api.getRsiLauncherAsarData(asarPath: dataPath); final data = await asar_api.getRsiLauncherAsarData(asarPath: dataPath);
@ -333,7 +338,10 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
} }
Future<String> _loadEnhanceData( 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 globalModel = ref.read(appGlobalModelProvider);
final enhancePath = "${globalModel.applicationSupportDir}/launcher_enhance_data"; final enhancePath = "${globalModel.applicationSupportDir}/launcher_enhance_data";
@ -418,11 +426,13 @@ class RsiLauncherEnhanceDialogUI extends HookConsumerWidget {
for (final line in serverScriptLines) { for (final line in serverScriptLines) {
final lineTrim = line.trim(); final lineTrim = line.trim();
if (lineTrim.startsWith(SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START)) { if (lineTrim.startsWith(SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START)) {
scriptBuffer scriptBuffer.writeln(
.writeln("$SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START\"${assarState.value!.enabledLocalization}\";"); "$SC_TOOLBOX_ENABLED_LOCALIZATION_SCRIPT_START\"${assarState.value!.enabledLocalization}\";",
);
} else if (lineTrim.startsWith(SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START)) { } else if (lineTrim.startsWith(SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START)) {
scriptBuffer scriptBuffer.writeln(
.writeln("$SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START${assarState.value!.enableDownloaderBoost};"); "$SC_TOOLBOX_ENABLE_DOWNLOADER_BOOST_SCRIPT_START${assarState.value!.enableDownloaderBoost};",
);
} else { } else {
scriptBuffer.writeln(line); scriptBuffer.writeln(line);
} }

View File

@ -72,6 +72,7 @@ class ToolsUIModel extends _$ToolsUIModel {
} }
try { try {
items = [ items = [
if (Platform.isWindows)
ToolsItemData( ToolsItemData(
"systemnfo", "systemnfo",
S.current.tools_action_view_system_info, S.current.tools_action_view_system_info,
@ -108,6 +109,7 @@ class ToolsUIModel extends _$ToolsUIModel {
if (!context.mounted) return; if (!context.mounted) return;
items.add(await _addP4kCard(context)); items.add(await _addP4kCard(context));
items.addAll([ items.addAll([
if (Platform.isWindows)
ToolsItemData( ToolsItemData(
"hosts_booster", "hosts_booster",
S.current.tools_action_hosts_acceleration_experimental, S.current.tools_action_hosts_acceleration_experimental,
@ -129,6 +131,7 @@ class ToolsUIModel extends _$ToolsUIModel {
const Icon(FluentIcons.c_plus_plus, size: 24), const Icon(FluentIcons.c_plus_plus, size: 24),
onTap: () => rsiEnhance(context), onTap: () => rsiEnhance(context),
), ),
if (Platform.isWindows)
ToolsItemData( ToolsItemData(
"reinstall_eac", "reinstall_eac",
S.current.tools_action_reinstall_easyanticheat, S.current.tools_action_reinstall_easyanticheat,
@ -136,6 +139,7 @@ class ToolsUIModel extends _$ToolsUIModel {
const Icon(FluentIcons.game, size: 24), const Icon(FluentIcons.game, size: 24),
onTap: () => _reinstallEAC(context), onTap: () => _reinstallEAC(context),
), ),
if (Platform.isWindows)
ToolsItemData( ToolsItemData(
"rsilauncher_admin_mode", "rsilauncher_admin_mode",
S.current.tools_action_rsi_launcher_admin_mode, S.current.tools_action_rsi_launcher_admin_mode,
@ -169,8 +173,10 @@ class ToolsUIModel extends _$ToolsUIModel {
if (!context.mounted) return; if (!context.mounted) return;
items.add(await _addPhotographyCard(context)); items.add(await _addPhotographyCard(context));
state = state.copyWith(items: items); state = state.copyWith(items: items);
if (Platform.isWindows) {
if (!context.mounted) return; if (!context.mounted) return;
items.addAll(await _addNvmePatchCard(context)); items.addAll(await _addNvmePatchCard(context));
}
state = state.copyWith(items: items, isItemLoading: false); state = state.copyWith(items: items, isItemLoading: false);
} catch (e) { } catch (e) {
if (!context.mounted) return; if (!context.mounted) return;
@ -250,7 +256,10 @@ class ToolsUIModel extends _$ToolsUIModel {
Future<ToolsItemData> _addShaderCard(BuildContext context) async { Future<ToolsItemData> _addShaderCard(BuildContext context) async {
final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath(); final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath();
final shaderSize = final shaderSize =
((await SystemHelper.getDirLen(gameShaderCachePath ?? "", skipPath: ["$gameShaderCachePath\\Crashes"])) / ((await SystemHelper.getDirLen(
gameShaderCachePath ?? "",
skipPath: ["$gameShaderCachePath\\Crashes".platformPath],
)) /
1024 / 1024 /
1024) 1024)
.toStringAsFixed(4); .toStringAsFixed(4);
@ -300,7 +309,8 @@ class ToolsUIModel extends _$ToolsUIModel {
// 使 // 使
final latestVersion = versions.first; 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); final file = File(settingsPath);
if (!await file.exists()) return (-1, latestVersion); if (!await file.exists()) return (-1, latestVersion);
@ -627,6 +637,7 @@ class ToolsUIModel extends _$ToolsUIModel {
final result = await showDialog<String>( final result = await showDialog<String>(
context: context, context: context,
builder: (dialogContext) => ContentDialog( builder: (dialogContext) => ContentDialog(
constraints: BoxConstraints(maxWidth: 380),
title: Text(S.current.tools_shader_clean_dialog_title), title: Text(S.current.tools_shader_clean_dialog_title),
content: Column( content: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,

View File

@ -4,10 +4,10 @@ project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change # The name of the executable created for the application. Change this to change
# the on-disk name of your application. # 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: # The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID # 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 # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake. # versions of CMake.
@ -53,6 +53,7 @@ add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies. # System-level dependencies.
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 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}") add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
@ -73,6 +74,7 @@ apply_standard_settings(${BINARY_NAME})
# Add dependency libraries. Add any application-specific dependencies here. # Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter) target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 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. # Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble) 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}" install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
endif() 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)

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -4,6 +4,7 @@
#ifdef GDK_WINDOWING_X11 #ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h> #include <gdk/gdkx.h>
#endif #endif
#include <glib.h>
#include "flutter/generated_plugin_registrant.h" #include "flutter/generated_plugin_registrant.h"
@ -42,11 +43,11 @@ static void my_application_activate(GApplication* application) {
if (use_header_bar) { if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar)); 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_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else { } else {
gtk_window_set_title(window, "app"); gtk_window_set_title(window, "SC汉化盒子");
} }
gtk_window_set_default_size(window, 1280, 720); gtk_window_set_default_size(window, 1280, 720);

View File

@ -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(); let mut main_js_content: Vec<u8> = Vec::new();
asar.files().iter().for_each(|v| { asar.files().iter().for_each(|v| {
let (path, file) = v; let (path, file) = v;
let path_string = path.clone().into_os_string().into_string().unwrap(); 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") { if path_string.starts_with("app/static/js/main.") && path_string.ends_with(".js") {
main_js_path = path_string; main_js_path = path_string;
main_js_content = file.data().to_vec(); main_js_content = file.data().to_vec();
} }

View File

@ -12,12 +12,16 @@ use tao::dpi::{LogicalPosition, LogicalSize};
use tao::event::{Event, WindowEvent}; use tao::event::{Event, WindowEvent};
use tao::event_loop::{ControlFlow, EventLoop, EventLoopBuilder}; use tao::event_loop::{ControlFlow, EventLoop, EventLoopBuilder};
use tao::platform::run_return::EventLoopExtRunReturn; use tao::platform::run_return::EventLoopExtRunReturn;
use tao::window::{Icon, Window, WindowBuilder}; use tao::window::{Icon, Window, WindowBuilder};
use wry::{NewWindowResponse, PageLoadEvent, WebView, WebViewBuilder}; use wry::{NewWindowResponse, PageLoadEvent, WebView, WebViewBuilder};
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use tao::platform::windows::EventLoopBuilderExtWindows; use tao::platform::windows::EventLoopBuilderExtWindows;
#[cfg(target_os = "linux")]
use tao::platform::unix::EventLoopBuilderExtUnix;
use crate::api::webview_api::{ use crate::api::webview_api::{
WebViewCommand, WebViewConfiguration, WebViewEvent, WebViewNavigationState, WebViewCommand, WebViewConfiguration, WebViewEvent, WebViewNavigationState,
}; };
@ -263,14 +267,10 @@ fn run_webview_loop(
is_closed: Arc<AtomicBool>, is_closed: Arc<AtomicBool>,
) { ) {
// Create event loop with any_thread support for non-main thread execution // 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() let mut event_loop: EventLoop<UserEvent> = EventLoopBuilder::with_user_event()
.with_any_thread(true) .with_any_thread(true)
.build(); .build();
#[cfg(not(target_os = "windows"))]
let mut event_loop: EventLoop<UserEvent> = EventLoopBuilder::with_user_event().build();
let proxy = event_loop.create_proxy(); let proxy = event_loop.create_proxy();
// Load window icon from embedded ICO file // Load window icon from embedded ICO file