mirror of
https://mirror.ghproxy.com/https://github.com/StarCitizenToolBox/app.git
synced 2024-12-23 03:03:42 +08:00
feat: download Localization
This commit is contained in:
parent
308480095b
commit
07f2ab03cb
@ -215,7 +215,7 @@ class _$AppGlobalStateImpl implements _AppGlobalState {
|
||||
this.appLocale,
|
||||
this.appConfBox,
|
||||
this.backgroundImageAssetsPath =
|
||||
"assets/backgrounds/SC_01_Wallpaper_3840x2160.jpg"});
|
||||
"assets/backgrounds/SC_01_Wallpaper_3840x2160.webp"});
|
||||
|
||||
@override
|
||||
final String? deviceUUID;
|
||||
|
@ -167,7 +167,7 @@ class HomeUI extends HookConsumerWidget {
|
||||
)),
|
||||
const SizedBox(width: 12),
|
||||
Button(
|
||||
onPressed: () {},
|
||||
onPressed: () {},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(6),
|
||||
child: Icon(FluentIcons.folder_open),
|
||||
@ -788,15 +788,10 @@ class HomeUI extends HookConsumerWidget {
|
||||
|
||||
_onMenuTap(BuildContext context, String key, HomeUIModelState homeState,
|
||||
WidgetRef ref) async {
|
||||
String gameInstallReqInfo =
|
||||
S.current.home_action_info_valid_install_location_required;
|
||||
// String gameInstallReqInfo =
|
||||
// S.current.home_action_info_valid_install_location_required;
|
||||
switch (key) {
|
||||
case "localization":
|
||||
if (homeState.scInstalledPath == "not_install") {
|
||||
// TODO
|
||||
// ToolsUIModel.English(context, showNotGameInstallMsg: true);
|
||||
break;
|
||||
}
|
||||
final model = ref.watch(homeUIModelProvider.notifier);
|
||||
model.checkLocalizationUpdate();
|
||||
await showDialog(
|
||||
@ -807,14 +802,18 @@ class HomeUI extends HookConsumerWidget {
|
||||
model.checkLocalizationUpdate(skipReload: true);
|
||||
break;
|
||||
case "performance":
|
||||
if (homeState.scInstalledPath == "not_install") {
|
||||
showToast(context, gameInstallReqInfo);
|
||||
break;
|
||||
}
|
||||
context.push("/index/$key");
|
||||
break;
|
||||
return;
|
||||
// if (homeState.scInstalledPath == "not_install") {
|
||||
// showToast(context, gameInstallReqInfo);
|
||||
// break;
|
||||
// }
|
||||
// context.push("/index/$key");
|
||||
// break;
|
||||
case "game_doctor":
|
||||
return;
|
||||
default:
|
||||
context.push("/index/$key");
|
||||
return;
|
||||
// context.push("/index/$key");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ part of 'home_ui_model.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$homeUIModelHash() => r'6a768281606856766737a63aaeebb392c4613d2b';
|
||||
String _$homeUIModelHash() => r'422565027563e9bfd3ebddb08e518cade1c967d0';
|
||||
|
||||
/// See also [HomeUIModel].
|
||||
@ProviderFor(HomeUIModel)
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@ -9,7 +8,6 @@ import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/data/app_advanced_localization_data.dart';
|
||||
import 'package:starcitizen_doctor/data/sc_localization_data.dart';
|
||||
|
||||
import '../home_ui_model.dart';
|
||||
import 'advanced_localization_ui.json.dart';
|
||||
import 'localization_ui_model.dart';
|
||||
|
||||
@ -178,40 +176,7 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel {
|
||||
|
||||
Future<(String, String)> _readIni(LocalizationUIState localizationUIState,
|
||||
LocalizationUIModel localizationUIModel) async {
|
||||
final homeUIState = ref.read(homeUIModelProvider);
|
||||
final gameDir = homeUIState.scInstalledPath;
|
||||
if (gameDir == null) return ("", "");
|
||||
state = state.copyWith(
|
||||
workingText: S.current.home_localization_advanced_msg_reading_p4k);
|
||||
final p4kGlobalIni = await readEnglishInI(gameDir);
|
||||
dPrint("read p4kGlobalIni => ${p4kGlobalIni.length}");
|
||||
state = state.copyWith(
|
||||
workingText: S.current
|
||||
.home_localization_advanced_msg_reading_server_localization_text);
|
||||
|
||||
if (state.customizeGlobalIni != null) {
|
||||
final apiLocalizationData = ScLocalizationData(
|
||||
versionName: S.current.localization_info_custom_files,
|
||||
info: "Customize");
|
||||
state = state.copyWith(apiLocalizationData: apiLocalizationData);
|
||||
return (p4kGlobalIni, state.customizeGlobalIni!);
|
||||
} else {
|
||||
final apiLocalizationData =
|
||||
localizationUIState.apiLocalizationData?.values.firstOrNull;
|
||||
if (apiLocalizationData == null) return ("", "");
|
||||
final file = File(
|
||||
"${localizationUIModel.getDownloadDir().absolute.path}\\${apiLocalizationData.versionName}.sclang");
|
||||
if (!await file.exists()) {
|
||||
await localizationUIModel.downloadLocalizationFile(
|
||||
file, apiLocalizationData);
|
||||
}
|
||||
state = state.copyWith(apiLocalizationData: apiLocalizationData);
|
||||
final serverGlobalIni =
|
||||
(await compute(LocalizationUIModel.readArchive, file.absolute.path))
|
||||
.toString();
|
||||
dPrint("read serverGlobalIni => ${serverGlobalIni.length}");
|
||||
return (p4kGlobalIni, serverGlobalIni);
|
||||
}
|
||||
return ("", "");
|
||||
}
|
||||
|
||||
Future<String> readEnglishInI(String gameDir) async {
|
||||
|
@ -7,7 +7,7 @@ part of 'advanced_localization_ui_model.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$advancedLocalizationUIModelHash() =>
|
||||
r'60ccd50f54b948d16be001f5ea07972a0fd9ed3f';
|
||||
r'5aa7e690f1db04febf5a747f4c8cd2eb79a0ed44';
|
||||
|
||||
/// See also [AdvancedLocalizationUIModel].
|
||||
@ProviderFor(AdvancedLocalizationUIModel)
|
||||
|
@ -21,127 +21,19 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
|
||||
useEffect(() {
|
||||
addPostFrameCallback(() {
|
||||
model.checkUserCfg(context);
|
||||
// model.checkUserCfg(context);
|
||||
});
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
return ContentDialog(
|
||||
title: makeTitle(context, model, state),
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * .7,
|
||||
minHeight: MediaQuery.of(context).size.height * .9),
|
||||
constraints: const BoxConstraints(maxWidth: 1080, minHeight: 920),
|
||||
content: Padding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12, top: 12),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
AnimatedSize(
|
||||
duration: const Duration(milliseconds: 130),
|
||||
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,
|
||||
),
|
||||
),
|
||||
if (!(model.getScInstallPath() ?? "").contains("LIVE"))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: InfoBar(
|
||||
title: Text(S.current
|
||||
.home_localization_ptu_advanced_localization_tip_title),
|
||||
content: Text(S.current
|
||||
.home_localization_ptu_advanced_localization_tip_title_info),
|
||||
severity: InfoBarSeverity.info,
|
||||
style: InfoBarThemeData(decoration: (severity) {
|
||||
return BoxDecoration(color: Colors.orange);
|
||||
}, iconColor: (severity) {
|
||||
return Colors.white;
|
||||
}),
|
||||
),
|
||||
),
|
||||
makeListContainer(
|
||||
S.current.localization_info_translation_status,
|
||||
[
|
||||
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,
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Text(S.current.localization_info_installed_version(
|
||||
"${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? S.current.home_localization_msg_version_advanced : ""}")),
|
||||
const Spacer(),
|
||||
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),
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
context),
|
||||
makeListContainer(
|
||||
S.current.localization_info_community_translation,
|
||||
[
|
||||
@ -352,15 +244,37 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
const SizedBox(width: 12),
|
||||
Text(S.current.home_action_localization_management),
|
||||
const SizedBox(width: 24),
|
||||
Text(
|
||||
"${model.getScInstallPath()}",
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
// Text(
|
||||
// "${model.getScInstallPath()}",
|
||||
// style: const TextStyle(fontSize: 13),
|
||||
// ),
|
||||
const Spacer(),
|
||||
SizedBox(
|
||||
height: 36,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
S.current.localization_info_channel(""),
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
ComboBox<String>(
|
||||
value: state.selectedChannel,
|
||||
items: [
|
||||
for (final channel in ["LIVE", "PTU"])
|
||||
ComboBoxItem(
|
||||
value: channel,
|
||||
child: Text(channel),
|
||||
)
|
||||
],
|
||||
onChanged: state.workingVersion.isNotEmpty
|
||||
? null
|
||||
: (v) {
|
||||
if (v == null) return;
|
||||
model.selectChannel(v);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 24),
|
||||
Text(
|
||||
S.current.localization_info_language,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
@ -453,8 +367,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
|
||||
),
|
||||
confirm: S.current.localization_action_install,
|
||||
cancel: S.current.home_action_cancel,
|
||||
constraints:
|
||||
BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .45),
|
||||
constraints: const BoxConstraints(maxWidth: 720),
|
||||
);
|
||||
if (userOK) {
|
||||
if (!context.mounted) return;
|
||||
|
@ -1,26 +1,24 @@
|
||||
// ignore_for_file: avoid_build_context_in_providers
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:archive/archive_io.dart';
|
||||
import 'package:archive/archive.dart';
|
||||
import 'dart:js_interop';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/api/api.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/data/sc_localization_data.dart';
|
||||
import 'package:starcitizen_doctor/generated/no_l10n_strings.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import 'package:web/web.dart' as web;
|
||||
|
||||
part 'localization_ui_model.g.dart';
|
||||
|
||||
part 'localization_ui_model.freezed.dart';
|
||||
@ -29,6 +27,7 @@ part 'localization_ui_model.freezed.dart';
|
||||
class LocalizationUIState with _$LocalizationUIState {
|
||||
factory LocalizationUIState({
|
||||
String? selectedLanguage,
|
||||
@Default("LIVE") String selectedChannel,
|
||||
Map<String, ScLocalizationData>? apiLocalizationData,
|
||||
@Default("") String workingVersion,
|
||||
MapEntry<bool, String>? patchStatus,
|
||||
@ -44,16 +43,6 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
"chinese_(traditional)": NoL10n.langZHT,
|
||||
};
|
||||
|
||||
Directory get _downloadDir =>
|
||||
Directory("${appGlobalState.applicationSupportDir}\\Localizations");
|
||||
|
||||
Directory getDownloadDir() => _downloadDir;
|
||||
|
||||
Directory get _scDataDir =>
|
||||
Directory("${ref.read(homeUIModelProvider).scInstalledPath}\\data");
|
||||
|
||||
File get _cfgFile => File("${_scDataDir.absolute.path}\\system.cfg");
|
||||
|
||||
StreamSubscription? _customizeDirListenSub;
|
||||
|
||||
String get _scInstallPath => ref.read(homeUIModelProvider).scInstalledPath!;
|
||||
@ -66,9 +55,6 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
if (_scInstallPath == "not_install") {
|
||||
return;
|
||||
}
|
||||
ref.onDispose(() {
|
||||
_customizeDirListenSub?.cancel();
|
||||
_customizeDirListenSub = null;
|
||||
@ -85,14 +71,13 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
|
||||
Future<void> _loadData() async {
|
||||
_allVersionLocalizationData.clear();
|
||||
await _updateStatus();
|
||||
for (var lang in languageSupport.keys) {
|
||||
final l = await Api.getScLocalizationData(lang).unwrap();
|
||||
if (l != null) {
|
||||
if (lang == state.selectedLanguage) {
|
||||
final apiLocalizationData = <String, ScLocalizationData>{};
|
||||
for (var element in l) {
|
||||
final isPTU = !_scInstallPath.contains("LIVE");
|
||||
final isPTU = !state.selectedChannel.contains("LIVE");
|
||||
if (isPTU && element.gameChannel == "PTU") {
|
||||
apiLocalizationData[element.versionName ?? ""] = element;
|
||||
} else if (!isPTU && element.gameChannel == "PU") {
|
||||
@ -110,87 +95,19 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
}
|
||||
}
|
||||
|
||||
void checkUserCfg(BuildContext context) async {
|
||||
final userCfgFile = File("$_scInstallPath\\USER.cfg");
|
||||
if (await userCfgFile.exists()) {
|
||||
final cfgString = await userCfgFile.readAsString();
|
||||
if (cfgString.contains("g_language") &&
|
||||
!cfgString.contains("g_language=${state.selectedLanguage}")) {
|
||||
if (!context.mounted) return;
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.localization_info_remove_incompatible_translation_params,
|
||||
Text(S.current
|
||||
.localization_info_incompatible_translation_params_warning),
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * .35));
|
||||
if (ok == true) {
|
||||
var finalString = "";
|
||||
for (var item in cfgString.split("\n")) {
|
||||
if (!item.trim().startsWith("g_language")) {
|
||||
finalString = "$finalString$item\n";
|
||||
}
|
||||
}
|
||||
await userCfgFile.delete();
|
||||
await userCfgFile.create();
|
||||
await userCfgFile.writeAsString(finalString, flush: true);
|
||||
_loadData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateLangCfg(bool enable) async {
|
||||
Future<String> genLangCfg() async {
|
||||
final selectedLanguage = state.selectedLanguage!;
|
||||
final status = await _getLangCfgEnableLang(lang: selectedLanguage);
|
||||
final exists = await _cfgFile.exists();
|
||||
if (status == enable) {
|
||||
await _updateStatus();
|
||||
return;
|
||||
}
|
||||
StringBuffer newStr = StringBuffer();
|
||||
var str = <String>[];
|
||||
if (exists) {
|
||||
str = (await _cfgFile.readAsString()).replaceAll(" ", "").split("\n");
|
||||
if (!newStr.toString().contains("sys_languages=$selectedLanguage")) {
|
||||
newStr.writeln("sys_languages=$selectedLanguage");
|
||||
}
|
||||
if (enable) {
|
||||
if (exists) {
|
||||
for (var value in str) {
|
||||
if (value.contains("sys_languages")) {
|
||||
value = "sys_languages=$selectedLanguage";
|
||||
} else if (value.contains("g_language")) {
|
||||
value = "g_language=$selectedLanguage";
|
||||
} else if (value.contains("g_languageAudio")) {
|
||||
value = "g_language=english";
|
||||
}
|
||||
if (value.trim().isNotEmpty) newStr.writeln(value);
|
||||
}
|
||||
}
|
||||
if (!newStr.toString().contains("sys_languages=$selectedLanguage")) {
|
||||
newStr.writeln("sys_languages=$selectedLanguage");
|
||||
}
|
||||
if (!newStr.toString().contains("g_language=$selectedLanguage")) {
|
||||
newStr.writeln("g_language=$selectedLanguage");
|
||||
}
|
||||
if (!newStr.toString().contains("g_languageAudio")) {
|
||||
newStr.writeln("g_languageAudio=english");
|
||||
}
|
||||
} else {
|
||||
if (exists) {
|
||||
for (var value in str) {
|
||||
if (value.contains("sys_languages=")) {
|
||||
continue;
|
||||
} else if (value.contains("g_language")) {
|
||||
continue;
|
||||
}
|
||||
newStr.writeln(value);
|
||||
}
|
||||
}
|
||||
if (!newStr.toString().contains("g_language=$selectedLanguage")) {
|
||||
newStr.writeln("g_language=$selectedLanguage");
|
||||
}
|
||||
if (exists) await _cfgFile.delete(recursive: true);
|
||||
await _cfgFile.create(recursive: true);
|
||||
await _cfgFile.writeAsString(newStr.toString());
|
||||
await _updateStatus();
|
||||
if (!newStr.toString().contains("g_languageAudio")) {
|
||||
newStr.writeln("g_languageAudio=english");
|
||||
}
|
||||
return newStr.toString();
|
||||
}
|
||||
|
||||
void goFeedback() {
|
||||
@ -198,13 +115,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
}
|
||||
|
||||
VoidCallback? doDelIniFile() {
|
||||
return () async {
|
||||
final iniFile = File(
|
||||
"${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini");
|
||||
if (await iniFile.exists()) await iniFile.delete();
|
||||
await updateLangCfg(false);
|
||||
await _updateStatus();
|
||||
};
|
||||
return () async {};
|
||||
}
|
||||
|
||||
String getCustomizeFileName(String path) {
|
||||
@ -214,8 +125,7 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
installFormString(StringBuffer globalIni, String versionName,
|
||||
{bool? advanced}) async {
|
||||
dPrint("LocalizationUIModel -> installFormString $versionName");
|
||||
final iniFile = File(
|
||||
"${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini");
|
||||
|
||||
if (versionName.isNotEmpty) {
|
||||
if (!globalIni.toString().endsWith("\n")) {
|
||||
globalIni.write("\n");
|
||||
@ -226,39 +136,40 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
globalIni
|
||||
.write("_starcitizen_doctor_localization_version=$versionName\n");
|
||||
}
|
||||
final selectedLanguage = state.selectedLanguage!;
|
||||
final iniFileString = "\uFEFF${globalIni.toString().trim()}";
|
||||
final cfg = await genLangCfg();
|
||||
final archive = Archive();
|
||||
archive.addFile(ArchiveFile(
|
||||
"data/Localization/$selectedLanguage/global.ini", 0, iniFileString));
|
||||
archive.addFile(ArchiveFile("data/system.cfg", 0, cfg));
|
||||
final zip = await compute(_encodeZipFile, archive);
|
||||
if (zip == null) return;
|
||||
final blob = Blob.fromBytes(zip, opt: {
|
||||
"type": "application/zip",
|
||||
});
|
||||
final url = web.URL.createObjectURL(blob);
|
||||
jsDownloadBlobFile(url, "Localization_$versionName.zip");
|
||||
}
|
||||
|
||||
/// write cfg
|
||||
if (await _cfgFile.exists()) {}
|
||||
|
||||
/// write ini
|
||||
if (await iniFile.exists()) {
|
||||
await iniFile.delete();
|
||||
}
|
||||
await iniFile.create(recursive: true);
|
||||
await iniFile.writeAsString("\uFEFF${globalIni.toString().trim()}",
|
||||
flush: true);
|
||||
await updateLangCfg(true);
|
||||
await _updateStatus();
|
||||
List<int>? _encodeZipFile(Archive archive) {
|
||||
final zip = ZipEncoder().encode(archive);
|
||||
return zip;
|
||||
}
|
||||
|
||||
VoidCallback? doRemoteInstall(
|
||||
BuildContext context, ScLocalizationData value) {
|
||||
return () 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");
|
||||
try {
|
||||
state = state.copyWith(workingVersion: value.versionName!);
|
||||
if (!await savePath.exists()) {
|
||||
// download
|
||||
await downloadLocalizationFile(savePath, value);
|
||||
} else {
|
||||
dPrint("use cache $savePath");
|
||||
}
|
||||
final data = await downloadLocalizationFile(value);
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
// check file
|
||||
final globalIni = await compute(readArchive, savePath.absolute.path);
|
||||
final globalIni = await compute(readArchive, data);
|
||||
if (globalIni.isEmpty) {
|
||||
throw S.current.localization_info_corrupted_file;
|
||||
}
|
||||
@ -267,30 +178,25 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
if (!context.mounted) return;
|
||||
await showToast(
|
||||
context, S.current.localization_info_installation_error(e));
|
||||
if (await savePath.exists()) await savePath.delete();
|
||||
}
|
||||
state = state.copyWith(workingVersion: "");
|
||||
};
|
||||
}
|
||||
|
||||
Future<void> downloadLocalizationFile(
|
||||
File savePath, ScLocalizationData value) async {
|
||||
dPrint("downloading file to $savePath");
|
||||
Future<Uint8List> downloadLocalizationFile(ScLocalizationData value) async {
|
||||
dPrint("downloading downloadLocalizationFile ...");
|
||||
final downloadUrl =
|
||||
"${URLConf.gitlabLocalizationUrl}/archive/${value.versionName}.tar.gz";
|
||||
final r = await RSHttp.get(downloadUrl);
|
||||
if (r.statusCode == 200 && r.data != null) {
|
||||
await savePath.create(recursive: true);
|
||||
await savePath.writeAsBytes(r.data!, flush: true);
|
||||
return r.data!;
|
||||
} else {
|
||||
throw "statusCode Error : ${r.statusCode}";
|
||||
}
|
||||
}
|
||||
|
||||
static StringBuffer readArchive(String savePath) {
|
||||
final inputStream = InputFileStream(savePath);
|
||||
final archive =
|
||||
TarDecoder().decodeBytes(GZipDecoder().decodeBuffer(inputStream));
|
||||
static StringBuffer readArchive(Uint8List data) {
|
||||
final archive = TarDecoder().decodeBytes(GZipDecoder().decodeBytes(data));
|
||||
StringBuffer globalIni = StringBuffer("");
|
||||
for (var element in archive.files) {
|
||||
if (element.name.contains("global.ini")) {
|
||||
@ -331,57 +237,6 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
};
|
||||
}
|
||||
|
||||
_updateStatus() async {
|
||||
final patchStatus = MapEntry(
|
||||
await _getLangCfgEnableLang(lang: state.selectedLanguage!),
|
||||
await _getInstalledIniVersion(
|
||||
"${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"));
|
||||
final isInstalledAdvanced = await _checkAdvancedStatus(
|
||||
"${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini");
|
||||
state = state.copyWith(
|
||||
patchStatus: patchStatus, isInstalledAdvanced: isInstalledAdvanced);
|
||||
}
|
||||
|
||||
Future<bool> _checkAdvancedStatus(String path) async {
|
||||
final iniFile = File(path);
|
||||
if (!await iniFile.exists()) {
|
||||
return false;
|
||||
}
|
||||
final iniString = (await iniFile.readAsString());
|
||||
return iniString.contains("_starcitizen_doctor_localization_advanced=true");
|
||||
}
|
||||
|
||||
Future<bool> _getLangCfgEnableLang(
|
||||
{String lang = "", String gamePath = ""}) async {
|
||||
if (gamePath.isEmpty) {
|
||||
gamePath = _scInstallPath;
|
||||
}
|
||||
final cfgFile = File("${_scDataDir.absolute.path}\\system.cfg");
|
||||
if (!await cfgFile.exists()) return false;
|
||||
final str = (await cfgFile.readAsString()).replaceAll(" ", "");
|
||||
return str.contains("sys_languages=$lang") &&
|
||||
str.contains("g_language=$lang") &&
|
||||
str.contains("g_languageAudio=english");
|
||||
}
|
||||
|
||||
static Future<String> _getInstalledIniVersion(String iniPath) async {
|
||||
final iniFile = File(iniPath);
|
||||
if (!await iniFile.exists()) {
|
||||
return S.current.home_action_info_game_built_in;
|
||||
}
|
||||
final iniStringSplit = (await iniFile.readAsString()).split("\n");
|
||||
for (var i = iniStringSplit.length - 1; i > 0; i--) {
|
||||
if (iniStringSplit[i]
|
||||
.contains("_starcitizen_doctor_localization_version=")) {
|
||||
final v = iniStringSplit[i]
|
||||
.trim()
|
||||
.split("_starcitizen_doctor_localization_version=")[1];
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return S.current.localization_info_custom_files;
|
||||
}
|
||||
|
||||
Future<List<String>> checkLangUpdate({bool skipReload = false}) async {
|
||||
if (_scInstallPath == "not_install") {
|
||||
return [];
|
||||
@ -394,40 +249,29 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
if (homeState.scInstallPaths.isEmpty) return [];
|
||||
|
||||
List<String> updates = [];
|
||||
|
||||
for (var scInstallPath in homeState.scInstallPaths) {
|
||||
// 读取游戏安装文件夹
|
||||
final scDataDir = Directory("$scInstallPath\\data\\Localization");
|
||||
// 扫描目录确认已安装的语言
|
||||
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");
|
||||
if (installedVersion == S.current.home_action_info_game_built_in ||
|
||||
installedVersion == S.current.localization_info_custom_files) {
|
||||
continue;
|
||||
}
|
||||
final curData = _allVersionLocalizationData[lang];
|
||||
dPrint("check Localization update $scInstallPath");
|
||||
if (!(curData?.keys.contains(installedVersion) ?? false)) {
|
||||
// has update
|
||||
for (var channel in ConstConf.gameChannels) {
|
||||
if (scInstallPath.contains(channel)) {
|
||||
dPrint("check Localization update: has update -> $channel");
|
||||
updates.add(channel);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dPrint("check Localization update: up to date");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
|
||||
void selectChannel(String v) {
|
||||
state = state.copyWith(selectedChannel: v);
|
||||
_loadData();
|
||||
}
|
||||
}
|
||||
|
||||
@JS("Blob")
|
||||
extension type Blob._(JSObject _) implements JSObject {
|
||||
external factory Blob(JSArray<JSArrayBuffer> blobParts, JSAny? options);
|
||||
|
||||
factory Blob.fromBytes(List<int> bytes, {Map? opt}) {
|
||||
final data = Uint8List.fromList(bytes).buffer.toJS;
|
||||
|
||||
return Blob([data].toJS, opt?.jsify());
|
||||
}
|
||||
|
||||
external JSArrayBuffer? get blobParts;
|
||||
|
||||
external JSObject? get options;
|
||||
}
|
||||
|
||||
@JS()
|
||||
external void jsDownloadBlobFile(String blobUrl, String filename);
|
||||
|
@ -17,6 +17,7 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||
/// @nodoc
|
||||
mixin _$LocalizationUIState {
|
||||
String? get selectedLanguage => throw _privateConstructorUsedError;
|
||||
String get selectedChannel => throw _privateConstructorUsedError;
|
||||
Map<String, ScLocalizationData>? get apiLocalizationData =>
|
||||
throw _privateConstructorUsedError;
|
||||
String get workingVersion => throw _privateConstructorUsedError;
|
||||
@ -39,6 +40,7 @@ abstract class $LocalizationUIStateCopyWith<$Res> {
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? selectedLanguage,
|
||||
String selectedChannel,
|
||||
Map<String, ScLocalizationData>? apiLocalizationData,
|
||||
String workingVersion,
|
||||
MapEntry<bool, String>? patchStatus,
|
||||
@ -62,6 +64,7 @@ class _$LocalizationUIStateCopyWithImpl<$Res, $Val extends LocalizationUIState>
|
||||
@override
|
||||
$Res call({
|
||||
Object? selectedLanguage = freezed,
|
||||
Object? selectedChannel = null,
|
||||
Object? apiLocalizationData = freezed,
|
||||
Object? workingVersion = null,
|
||||
Object? patchStatus = freezed,
|
||||
@ -73,6 +76,10 @@ class _$LocalizationUIStateCopyWithImpl<$Res, $Val extends LocalizationUIState>
|
||||
? _value.selectedLanguage
|
||||
: selectedLanguage // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
selectedChannel: null == selectedChannel
|
||||
? _value.selectedChannel
|
||||
: selectedChannel // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
apiLocalizationData: freezed == apiLocalizationData
|
||||
? _value.apiLocalizationData
|
||||
: apiLocalizationData // ignore: cast_nullable_to_non_nullable
|
||||
@ -107,6 +114,7 @@ abstract class _$$LocalizationUIStateImplCopyWith<$Res>
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? selectedLanguage,
|
||||
String selectedChannel,
|
||||
Map<String, ScLocalizationData>? apiLocalizationData,
|
||||
String workingVersion,
|
||||
MapEntry<bool, String>? patchStatus,
|
||||
@ -128,6 +136,7 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? selectedLanguage = freezed,
|
||||
Object? selectedChannel = null,
|
||||
Object? apiLocalizationData = freezed,
|
||||
Object? workingVersion = null,
|
||||
Object? patchStatus = freezed,
|
||||
@ -139,6 +148,10 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res>
|
||||
? _value.selectedLanguage
|
||||
: selectedLanguage // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
selectedChannel: null == selectedChannel
|
||||
? _value.selectedChannel
|
||||
: selectedChannel // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
apiLocalizationData: freezed == apiLocalizationData
|
||||
? _value._apiLocalizationData
|
||||
: apiLocalizationData // ignore: cast_nullable_to_non_nullable
|
||||
@ -168,6 +181,7 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res>
|
||||
class _$LocalizationUIStateImpl implements _LocalizationUIState {
|
||||
_$LocalizationUIStateImpl(
|
||||
{this.selectedLanguage,
|
||||
this.selectedChannel = "LIVE",
|
||||
final Map<String, ScLocalizationData>? apiLocalizationData,
|
||||
this.workingVersion = "",
|
||||
this.patchStatus,
|
||||
@ -178,6 +192,9 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
|
||||
|
||||
@override
|
||||
final String? selectedLanguage;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String selectedChannel;
|
||||
final Map<String, ScLocalizationData>? _apiLocalizationData;
|
||||
@override
|
||||
Map<String, ScLocalizationData>? get apiLocalizationData {
|
||||
@ -208,7 +225,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LocalizationUIState(selectedLanguage: $selectedLanguage, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)';
|
||||
return 'LocalizationUIState(selectedLanguage: $selectedLanguage, selectedChannel: $selectedChannel, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -218,6 +235,8 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
|
||||
other is _$LocalizationUIStateImpl &&
|
||||
(identical(other.selectedLanguage, selectedLanguage) ||
|
||||
other.selectedLanguage == selectedLanguage) &&
|
||||
(identical(other.selectedChannel, selectedChannel) ||
|
||||
other.selectedChannel == selectedChannel) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._apiLocalizationData, _apiLocalizationData) &&
|
||||
(identical(other.workingVersion, workingVersion) ||
|
||||
@ -234,6 +253,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
selectedLanguage,
|
||||
selectedChannel,
|
||||
const DeepCollectionEquality().hash(_apiLocalizationData),
|
||||
workingVersion,
|
||||
patchStatus,
|
||||
@ -253,6 +273,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
|
||||
abstract class _LocalizationUIState implements LocalizationUIState {
|
||||
factory _LocalizationUIState(
|
||||
{final String? selectedLanguage,
|
||||
final String selectedChannel,
|
||||
final Map<String, ScLocalizationData>? apiLocalizationData,
|
||||
final String workingVersion,
|
||||
final MapEntry<bool, String>? patchStatus,
|
||||
@ -262,6 +283,8 @@ abstract class _LocalizationUIState implements LocalizationUIState {
|
||||
@override
|
||||
String? get selectedLanguage;
|
||||
@override
|
||||
String get selectedChannel;
|
||||
@override
|
||||
Map<String, ScLocalizationData>? get apiLocalizationData;
|
||||
@override
|
||||
String get workingVersion;
|
||||
|
@ -7,7 +7,7 @@ part of 'localization_ui_model.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$localizationUIModelHash() =>
|
||||
r'd08a3d100c72b3f1f4a6c96944d4a73bb3c21808';
|
||||
r'404f40f1f2c04494ce570173262ee0d8579b30b4';
|
||||
|
||||
/// See also [LocalizationUIModel].
|
||||
@ProviderFor(LocalizationUIModel)
|
||||
|
@ -14,6 +14,8 @@ import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
|
||||
import 'package:web/web.dart' as web;
|
||||
|
||||
class SplashUI extends HookConsumerWidget {
|
||||
const SplashUI({super.key});
|
||||
|
||||
@ -102,7 +104,7 @@ class SplashUI extends HookConsumerWidget {
|
||||
if (userOk) {
|
||||
await appConf.put("splash_alert_info_version", _alertInfoVersion);
|
||||
} else {
|
||||
exit(0);
|
||||
web.window.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ dependencies:
|
||||
super_sliver_list: ^0.4.1
|
||||
re_editor: ^0.3.0
|
||||
re_highlight: ^0.0.3
|
||||
cross_file: ^0.3.4+2
|
||||
web: ^1.0.0
|
||||
dependency_overrides:
|
||||
http: ^1.1.2
|
||||
|
||||
|
@ -1,40 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
If you are serving your web app in a path other than the root, change the
|
||||
href value below to reflect the base path you are serving from.
|
||||
<!--
|
||||
If you are serving your web app in a path other than the root, change the
|
||||
href value below to reflect the base path you are serving from.
|
||||
|
||||
The path provided below has to start and end with a slash "/" in order for
|
||||
it to work correctly.
|
||||
The path provided below has to start and end with a slash "/" in order for
|
||||
it to work correctly.
|
||||
|
||||
For more details:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||
For more details:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||
|
||||
This is a placeholder for base href that will be replaced by the value of
|
||||
the `--base-href` argument provided to `flutter build`.
|
||||
-->
|
||||
<base href="$FLUTTER_BASE_HREF">
|
||||
This is a placeholder for base href that will be replaced by the value of
|
||||
the `--base-href` argument provided to `flutter build`.
|
||||
-->
|
||||
<base href="$FLUTTER_BASE_HREF">
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="A new Flutter project.">
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="A new Flutter project.">
|
||||
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="starcitizentoolbox_web">
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="starcitizentoolbox_web">
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
|
||||
<title>starcitizentoolbox_web</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<script type="application/javascript" src="/assets/packages/flutter_inappwebview_web/assets/web/web_support.js" defer></script>
|
||||
<title>starcitizentoolbox_web</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
|
||||
<script>
|
||||
|
||||
function jsDownloadBlobFile(blobUrl, filename) {
|
||||
const a = document.createElement("a");
|
||||
document.body.appendChild(a);
|
||||
a.href = blobUrl;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
setTimeout(() => {
|
||||
window.URL.revokeObjectURL(blobUrl);
|
||||
document.body.removeChild(a);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<script src="flutter_bootstrap.js" async></script>
|
||||
<script src="flutter_bootstrap.js" async></script>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user