diff --git a/assets/advanced_localization.json b/assets/advanced_localization.json index 9b82051..7038d5e 100644 --- a/assets/advanced_localization.json +++ b/assets/advanced_localization.json @@ -1,48 +1,24 @@ { "class_keys": [ { - "id": "location", - "class_name": "地点", + "id": "location_opt", + "class_name": "地点-其他", "keys": [ "ATC_.*", - "Bacchus.*", - "Cano.*", - "Castra.*", - ".*_QT", "Crus_HistMarker_.*", - "Delamar.*", - "Ellis.*", "Enroll_.*", - ".*_QuantumMarker_.*", - "Goss.*", - "Hadrian.*", "Hurston_JumpPoint_.*", "Jurisdictions_Name_.*", "LandingPad_.*", - "Levski_Shop_Teach.*", "Lorville_.*", - "Magnus.*", "MiningClaim_.*", "MiningClaims_Desc_.*", "NavBeacon_.*", "NavPoint_.*", "Nav_.*", "Navpoint_.*", - "Nyx.*", "Orison_Destination_.*", - "Oso.*", - "Pyro.*", - "QT_.*", - "RR_.*", - "Shubin_.*", - "Solar_system_.*", - "Stanton.*", "Starfarer_Wreckage_QT_.*", - "Taranis.*", - "Tarpits.*", - "Tayac.*", - "Terra.*", - "Virgil.*", "area_.*", "cargodeck_.*", "dockingport_.*", @@ -55,193 +31,94 @@ "select_.*", "shop_name_.*", "vehicle_room_.*", - "warning_.*", - "weapon_stats.*", - "weapons_heal_.*" + "weapons_heal_.*", + "QT_.*", + "RR_.*", + "Shubin_.*", + "Solar_system_.*", + "Bacchus.*_Desc.*", + "Cano.*_Desc.*", + "Castra.*_Desc.*", + "Delamar.*_Desc.*", + "Ellis.*_Desc.*", + "Goss.*_Desc.*", + "Hadrian.*_Desc.*", + "Levski_Shop_Teach.*_Desc.*", + "Magnus.*_Desc.*", + "Nyx.*_Desc.*", + "Oso.*_Desc.*", + "Pyro.*_Desc.*", + "Stanton.*_Desc.*", + "Taranis.*_Desc.*", + "Tarpits.*_Desc.*", + "Tayac.*_Desc.*", + "Terra.*_Desc.*", + "Virgil.*_Desc.*" ] }, { - "id": "ui", - "class_name": "UI/HUD", + "id": "location", + "class_name": "地点-常用", "keys": [ - ".*_RepUI_.*", - "CharacterCustomizer_.*", - ".*_OEM_UI_.*", - "DownloadConnectivity_.*", - "DownloadError_.*", - "DownloadStalled_.*", - "DownloadState_.*", - "Download_BrowseForFolder", - "Exit_Screen_.*", - "FW2022_DatapadScreen_.*", - "FW2022_IFFIscreen_.*", - "FW2022_TransferScreen_.*", - "FW22_NT_Journal_Body_.*", - ".*_DisplayName_.*", - "FreightElevator_.*", - "Frontend_.*", - "Fines_.*", - "FireMode_.*", - "GraceWarnings_.*", - "HUD_.*", - "Heads_Male_.*", - "Hints_.*", - "Human_Crew_.*", - "Human_First_Names_.*", - "Human_Nicknames_.*", - "Human_Security_Titles_.*", - "Human_Surnames_.*", - "HurDynDrugUGF_.*", - "HurDynMining_.*", - "HurstonSecurity_.*", - "Inner_.*", - "Kareah_.*", - "KeypadButton_.*", - "KeypadDisplay_.*", - "Map_Header_.*", - "Maps_.*", - "Marker_.*", - "Markers_.*", - "MurderSpree.*", - "NPC_Interact_Generic_.*", - "Out_of_Quantum_fuel.*", - "Oxygen_Screen_.*", - "PAUSE_OPTIONS_.*", - "PBay_.*", - "PIT_.*", - "PrisonKeypadPass_.*", - "Prison_.*", - "Prisoner.*", - "RN_.*", - "ShipSelector_.*", - "SkyLine_.*", - "Takedown.*", - "UEERanks_.*", - "UGF_.*", - "UI_.*", - "UnitedWorkersOfHurston_.*", - "Usable_CommTower_.*", - "Usable_Terminal_.*", - "Vehicle_Container_.*", - "actor_.*", - "ad_.*", - "airlock_.*", - "annun_.*", - "cargoTransfer_HUD_.*", - "cargo_.*", - "cc_.*", - "chat_.*", - "citizencon16_.*", - "cockpit_screen_.*", - "comm_.*", - "comms_.*", - "concate_.*", - "constellation_rear_tube.*", - "crate_panel_.*", - "defend_UGF_.*", - "dfm_.*", - "diff_notification_ui_.*", - "docking_.*", - "ea_popup_.*", - "ea_ui_.*", - "elevator_.*", - "engineering_.*", - "entrance_.*", - "fine_terminal_.*", - "flightHUD_.*", - "frontend_.*", - "generic_.*", - "global_terminal_.*", - "group_.*", - "hack_program_.*", - "hud_.*", - "infopanel_.*", - "innerthought_.*", - "input_.*", - "install_.*", - "interaction_.*", - "interiormap_.*", - "invictus_.*", - "journal_.*", - "kiosk_.*", - "law_.*", - "lens_.*", - "loadout_.*", - "mG_.*", - "masterMode_.*", - "me_.*", - "med_.*", - "medbed_.*", - "mfd_.*", - "mm_LobbyStatus_.*", - "mobiGlas_.*", - "mobiglas_.*", - "navitem_.*", - "net_dialog_.*", - "operatorMode_.*", - "panel_.*", - "pause_.*", - "pit_.*", - "port_Name.*", - "quantum_hud_.*", - "rc_ScanInfo_.*", - "refinery_ui_.*", - "refueling_HUD_.*", - "refueling_ui_.*", - "respawn_hud_.*", - "salvage_.*", - "scan_data_.*", - "scrambleracesubmissions_.*", - "shop_ui_.*", - "sm_ui_.*", - "spaceship_.*", - "starmap_.*", - "stomp_warning_.*", - "swapWheel_.*", - "system_.*", - "target_hud_.*", - "terminal_.*", - "text_.*", - "transit_.*", - "tutorial_.*", - "ui_.*", - "unittest_.*", - "usable_.*", - "vehicle_Type.*", - "vehicle_class_.*", - "vehicle_deck_.*", - "vehicle_focus_.*", - "vehicle_gunnery_hud_.*", - "vehicle_interactor_.*" + "Bacchus(?!.*_Desc).*", + "Cano(?!.*_Desc).*", + "Castra(?!.*_Desc).*", + "Delamar(?!.*_Desc).*", + "Ellis(?!.*_Desc).*", + "Goss(?!.*_Desc).*", + "Hadrian(?!.*_Desc).*", + "Levski_Shop_Teach(?!.*_Desc).*", + "Magnus(?!.*_Desc).*", + "Nyx(?!.*_Desc).*", + "Oso(?!.*_Desc).*", + "Pyro(?!.*_Desc).*", + "Stanton(?!.*_Desc).*", + "Taranis(?!.*_Desc).*", + "Tarpits(?!.*_Desc).*", + "Tayac(?!.*_Desc).*", + "Terra(?!.*_Desc).*", + "Virgil(?!.*_Desc).*" ] }, { - "class_name": "物品", - "id": "thing", + "class_name": "物品-其他", + "id": "thing_opt", "keys": [ "BarMenu_.*", "FLOOR_Flair_Items.*", "FPS_AI_.*", - "ItemPort_port_NameCooler.*", - "Item_.*", + "ItemPort_.*", + "itemPort_.*", "destoryitems_.*", "destroyItems_.*", - "item.*", - "kopion_.*" + "kopion_.*", + "item_desc.*" ] }, { - "class_name": "载具", - "id": "vehicle", + "class_name": "物品-常用", + "id": "thing", + "keys": [ + "item_Name.*" + ] + }, + { + "class_name": "载具-其他", + "id": "vehicle_name", "keys": [ - "vehicle_Name.*", - "vehicle_name.*", "vehicle_Desc.*", "vehicl_Desc.*", "vehicel_Desc.*", "vehicles_.*" ] }, + { + "class_name": "载具-常用", + "id": "vehicle", + "keys": [ + "vehicle_Name.*" + ] + }, { "id": "mission", "class_name": "任务/日志", @@ -526,6 +403,163 @@ "covalexrand_.*", "covalexspec_.*" ] + }, + { + "id": "ui", + "class_name": "UI/HUD/菜单", + "keys": [ + ".*_RepUI_.*", + "CharacterCustomizer_.*", + ".*_OEM_UI_.*", + "DownloadConnectivity_.*", + "DownloadError_.*", + "DownloadStalled_.*", + "DownloadState_.*", + "Download_BrowseForFolder", + "Exit_Screen_.*", + "FW2022_DatapadScreen_.*", + "FW2022_IFFIscreen_.*", + "FW2022_TransferScreen_.*", + "FW22_NT_Journal_Body_.*", + ".*_DisplayName_.*", + "FreightElevator_.*", + "Frontend_.*", + "Fines_.*", + "FireMode_.*", + "GraceWarnings_.*", + "HUD_.*", + "Heads_Male_.*", + "Hints_.*", + "Human_Crew_.*", + "Human_First_Names_.*", + "Human_Nicknames_.*", + "Human_Security_Titles_.*", + "Human_Surnames_.*", + "HurDynDrugUGF_.*", + "HurDynMining_.*", + "HurstonSecurity_.*", + "Inner_.*", + "Kareah_.*", + "KeypadButton_.*", + "KeypadDisplay_.*", + "Map_Header_.*", + "Maps_.*", + "Marker_.*", + "Markers_.*", + "MurderSpree.*", + "NPC_Interact_Generic_.*", + "Out_of_Quantum_fuel.*", + "Oxygen_Screen_.*", + "PAUSE_OPTIONS_.*", + "PBay_.*", + "PIT_.*", + "PrisonKeypadPass_.*", + "Prison_.*", + "Prisoner.*", + "RN_.*", + "ShipSelector_.*", + "SkyLine_.*", + "Takedown.*", + "UEERanks_.*", + "UGF_.*", + "UI_.*", + "UnitedWorkersOfHurston_.*", + "Usable_CommTower_.*", + "Usable_Terminal_.*", + "Vehicle_Container_.*", + "actor_.*", + "ad_.*", + "airlock_.*", + "annun_.*", + "cargoTransfer_HUD_.*", + "cargo_.*", + "cc_.*", + "chat_.*", + "citizencon16_.*", + "cockpit_screen_.*", + "comm_.*", + "comms_.*", + "concate_.*", + "constellation_rear_tube.*", + "crate_panel_.*", + "defend_UGF_.*", + "dfm_.*", + "diff_notification_ui_.*", + "docking_.*", + "ea_popup_.*", + "ea_ui_.*", + "elevator_.*", + "engineering_.*", + "entrance_.*", + "fine_terminal_.*", + "flightHUD_.*", + "frontend_.*", + "generic_.*", + "global_terminal_.*", + "group_.*", + "hack_program_.*", + "hud_.*", + "infopanel_.*", + "innerthought_.*", + "input_.*", + "install_.*", + "interaction_.*", + "interiormap_.*", + "invictus_.*", + "journal_.*", + "kiosk_.*", + "law_.*", + "lens_.*", + "loadout_.*", + "mG_.*", + "masterMode_.*", + "me_.*", + "med_.*", + "medbed_.*", + "mfd_.*", + "mm_LobbyStatus_.*", + "mobiGlas_.*", + "mobiglas_.*", + "navitem_.*", + "net_dialog_.*", + "operatorMode_.*", + "panel_.*", + "pause_.*", + "pit_.*", + "port_Name.*", + "quantum_hud_.*", + "rc_ScanInfo_.*", + "refinery_ui_.*", + "refueling_HUD_.*", + "refueling_ui_.*", + "respawn_hud_.*", + "salvage_.*", + "scan_data_.*", + "scrambleracesubmissions_.*", + "shop_ui_.*", + "sm_ui_.*", + "spaceship_.*", + "starmap_.*", + "stomp_warning_.*", + "swapWheel_.*", + "system_.*", + "target_hud_.*", + "terminal_.*", + "text_.*", + "transit_.*", + "tutorial_.*", + "ui_.*", + "unittest_.*", + "usable_.*", + "vehicle_Type.*", + "vehicle_class_.*", + "vehicle_deck_.*", + "vehicle_focus_.*", + "vehicle_gunnery_hud_.*", + "vehicle_interactor_.*", + "weapon_stats.*", + "warning_.*" + ] } ] } \ No newline at end of file diff --git a/lib/data/app_advanced_localization_data.dart b/lib/data/app_advanced_localization_data.dart index e672f70..0d12947 100644 --- a/lib/data/app_advanced_localization_data.dart +++ b/lib/data/app_advanced_localization_data.dart @@ -42,6 +42,8 @@ class AppAdvancedLocalizationClassKeysData { Map valuesMap = {}; AppAdvancedLocalizationClassKeysDataMode mode = AppAdvancedLocalizationClassKeysDataMode.localization; + bool lockMod = false; + bool isWorking = false; Map toJson() { final map = {}; @@ -56,4 +58,5 @@ enum AppAdvancedLocalizationClassKeysDataMode { unLocalization, localization, mixed, + mixedNewline, } diff --git a/lib/ui/home/localization/advanced_localization_ui.dart b/lib/ui/home/localization/advanced_localization_ui.dart index 1ef3544..8cec39e 100644 --- a/lib/ui/home/localization/advanced_localization_ui.dart +++ b/lib/ui/home/localization/advanced_localization_ui.dart @@ -1,6 +1,12 @@ import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:re_editor/re_editor.dart'; +import 'package:re_highlight/languages/ini.dart'; +import 'package:re_highlight/styles/vs2015.dart'; +import 'package:starcitizen_doctor/data/app_advanced_localization_data.dart'; import 'package:starcitizen_doctor/ui/home/home_ui_model.dart'; import 'package:starcitizen_doctor/ui/home/localization/advanced_localization_ui_model.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; @@ -12,6 +18,7 @@ class AdvancedLocalizationUI extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final state = ref.watch(advancedLocalizationUIModelProvider); + final model = ref.read(advancedLocalizationUIModelProvider.notifier); final homeUIState = ref.watch(homeUIModelProvider); return makeDefaultPage( title: "高级汉化 -> ${homeUIState.scInstalledPath}", @@ -27,20 +34,51 @@ class AdvancedLocalizationUI extends HookConsumerWidget { ], ), ) - : _makeBody(context, homeUIState, state, ref)); + : Column( + children: [ + Row( + children: [ + const SizedBox(width: 12), + Expanded( + child: Text( + '已加载汉化版本:${state.apiLocalizationData?.versionName}')), + Text('汉化文本行数:${state.serverGlobalIniLines}' + ' P4K文本行数:${state.p4kGlobalIniLines}'), + const SizedBox(width: 32), + Button( + child: const Padding( + padding: EdgeInsets.only( + left: 12, right: 12, top: 4, bottom: 4), + child: Text("安装汉化"), + ), + onPressed: () async { + await model.doInstall().unwrap(context: context); + }), + const SizedBox(width: 12), + ], + ), + Expanded( + child: + _makeBody(context, homeUIState, state, ref, model)), + ], + )); } - Widget _makeBody(BuildContext context, HomeUIModelState homeUIState, - AdvancedLocalizationUIState state, WidgetRef ref) { + Widget _makeBody( + BuildContext context, + HomeUIModelState homeUIState, + AdvancedLocalizationUIState state, + WidgetRef ref, + AdvancedLocalizationUIModel model) { return AlignedGridView.count( - crossAxisCount: 3, + crossAxisCount: 4, crossAxisSpacing: 12, mainAxisSpacing: 12, padding: const EdgeInsets.all(12), itemBuilder: (BuildContext context, int index) { final item = state.classMap!.values.elementAt(index); return Container( - padding: const EdgeInsets.only(top: 12, bottom: 12), + padding: const EdgeInsets.only(top: 6, bottom: 12), decoration: BoxDecoration( color: Colors.white.withOpacity(.05), borderRadius: BorderRadius.circular(4), @@ -48,24 +86,35 @@ class AdvancedLocalizationUI extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(left: 12, right: 12), - child: Row( - children: [ - Expanded( - child: Text( - "${item.className}", - style: const TextStyle( - fontSize: 16, fontWeight: FontWeight.bold), - )), - Text( - "${item.valuesMap.length}", - style: TextStyle( - fontSize: 14, - color: Colors.white.withOpacity(.6), + IconButton( + onPressed: + item.isWorking ? null : () => _showContent(context, item), + icon: Padding( + padding: const EdgeInsets.only(left: 12, right: 12), + child: Row( + children: [ + Expanded( + child: Text( + "${item.className}", + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold), + textAlign: TextAlign.start, + )), + Text( + "${item.valuesMap.length}", + style: TextStyle( + fontSize: 14, + color: Colors.white.withOpacity(.6), + ), ), - ), - ], + const SizedBox(width: 6), + Icon( + FluentIcons.chevron_right, + color: Colors.white.withOpacity(.6), + size: 16, + ), + ], + ), ), ), Container( @@ -74,24 +123,59 @@ class AdvancedLocalizationUI extends HookConsumerWidget { height: 1, color: Colors.white.withOpacity(.1), ), - SizedBox( - height: 160, - child: SuperListView.builder( - itemCount: item.valuesMap.length, + if (item.isWorking) + Column( + children: [ + makeLoading(context), + const SizedBox(height: 6), + const Text("正在重新生成文本..."), + ], + ) + else ...[ + Padding( padding: const EdgeInsets.only(left: 12, right: 12), - itemBuilder: (BuildContext context, int index) { - final itemKey = item.valuesMap.keys.elementAt(index); - return Text( - "${item.valuesMap[itemKey]}", - maxLines: 1, - style: const TextStyle( - fontSize: 12, - overflow: TextOverflow.ellipsis, + child: Row( + children: [ + const Expanded(child: Text("模式")), + ComboBox( + value: item.mode, + items: [ + for (final type + in AppAdvancedLocalizationClassKeysDataMode + .values) + ComboBoxItem( + value: type, + child: Text(state.typeNames[type] ?? "-"), + ), + ], + onChanged: item.lockMod + ? null + : (v) => model.onChangeMod(item, + v as AppAdvancedLocalizationClassKeysDataMode), ), - ); - }, + ], + ), ), - ), + const SizedBox(height: 6), + SizedBox( + height: 180, + child: SuperListView.builder( + itemCount: item.valuesMap.length, + padding: const EdgeInsets.only(left: 12, right: 12), + itemBuilder: (BuildContext context, int index) { + final itemKey = item.valuesMap.keys.elementAt(index); + return Text( + "${item.valuesMap[itemKey]}", + maxLines: 1, + style: const TextStyle( + fontSize: 12, + overflow: TextOverflow.ellipsis, + ), + ); + }, + ), + ), + ], ], ), ); @@ -99,4 +183,73 @@ class AdvancedLocalizationUI extends HookConsumerWidget { itemCount: state.classMap?.length ?? 0, ); } + + _showContent( + BuildContext context, AppAdvancedLocalizationClassKeysData item) { + showDialog( + context: context, + builder: (BuildContext context) { + return HookConsumer( + builder: (BuildContext context, WidgetRef ref, Widget? child) { + final textData = useState(""); + + loadData() async { + final v = StringBuffer(""); + for (var element in item.valuesMap.entries) { + v.write("${element.key}=${element.value}\n"); + await Future.delayed(Duration.zero); + } + textData.value = v.toString(); + } + + useEffect(() { + loadData(); + return null; + }, const []); + + return ContentDialog( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width * .8, + ), + title: Row( + children: [ + IconButton( + icon: const Icon( + FluentIcons.back, + size: 22, + ), + onPressed: () => context.pop()), + const SizedBox( + width: 24, + ), + Text("预览:${item.className}"), + ], + ), + content: textData.value.isEmpty + ? makeLoading(context) + : Container( + decoration: BoxDecoration( + color: FluentTheme.of(context).cardColor, + borderRadius: BorderRadius.circular(7), + ), + child: CodeEditor( + readOnly: true, + controller: + CodeLineEditingController.fromText(textData.value), + style: CodeEditorStyle( + codeTheme: CodeHighlightTheme( + languages: { + 'ini': CodeHighlightThemeMode(mode: langIni) + }, + theme: vs2015Theme, + ), + ), + ), + ), + ); + }, + ); + }, + ); + } } diff --git a/lib/ui/home/localization/advanced_localization_ui_model.dart b/lib/ui/home/localization/advanced_localization_ui_model.dart index afdca6a..c0bb644 100644 --- a/lib/ui/home/localization/advanced_localization_ui_model.dart +++ b/lib/ui/home/localization/advanced_localization_ui_model.dart @@ -9,6 +9,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/common/utils/provider.dart'; import 'package:starcitizen_doctor/data/app_advanced_localization_data.dart'; +import 'package:starcitizen_doctor/data/sc_localization_data.dart'; import 'package:starcitizen_doctor/provider/unp4kc.dart'; import '../home_ui_model.dart'; @@ -25,9 +26,21 @@ class AdvancedLocalizationUIState with _$AdvancedLocalizationUIState { Map? classMap, String? p4kGlobalIni, String? serverGlobalIni, + ScLocalizationData? apiLocalizationData, + @Default(0) int p4kGlobalIniLines, + @Default(0) int serverGlobalIniLines, }) = _AdvancedLocalizationUIState; } +extension AdvancedLocalizationUIStateEx on AdvancedLocalizationUIState { + Map get typeNames => { + AppAdvancedLocalizationClassKeysDataMode.localization: "汉化", + AppAdvancedLocalizationClassKeysDataMode.unLocalization: "英文原文", + AppAdvancedLocalizationClassKeysDataMode.mixed: "双语", + AppAdvancedLocalizationClassKeysDataMode.mixedNewline: "双语(换行)", + }; +} + @riverpod class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel { @override @@ -47,10 +60,14 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel { if (ald.classKeys == null) return; state = state.copyWith(workingText: "正在分类 ..."); final m = await compute(_doClassIni, (ald, p4kGlobalIni, serverGlobalIni)); + final p4kGlobalIniLines = p4kGlobalIni.split("\n").length; + final serverGlobalIniLines = serverGlobalIni.split("\n").length; state = state.copyWith( workingText: "", p4kGlobalIni: p4kGlobalIni, serverGlobalIni: serverGlobalIni, + p4kGlobalIniLines: p4kGlobalIniLines, + serverGlobalIniLines: serverGlobalIniLines, classMap: m); } @@ -70,10 +87,12 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel { id: "un_localization", className: "未汉化", keys: [], - ); + ) + ..mode = AppAdvancedLocalizationClassKeysDataMode.unLocalization + ..lockMod = true; final unClass = AppAdvancedLocalizationClassKeysData( id: "un_class", - className: "未分类", + className: "其他", keys: [], ); final classMap = { @@ -84,18 +103,22 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel { final serverIniMap = readIniAsMap(serverGlobalIni); var regexList = classMap.values - .expand((c) => c.keys!.map((k) => MapEntry(c, RegExp(k)))) + .expand((c) => + c.keys!.map((k) => MapEntry(c, RegExp(k, caseSensitive: false)))) .toList(); iniKeysLoop: for (var p4kIniKey in p4kIniMap.keys) { final serverValue = serverIniMap[p4kIniKey]; - if (serverValue == null) { - unLocalization.valuesMap[p4kIniKey] = p4kIniMap[p4kIniKey] ?? ""; + if (serverValue == null || serverValue.trim().isEmpty) { + final p4kValue = p4kIniMap[p4kIniKey] ?? ""; + if (p4kValue.trim().isNotEmpty) { + unLocalization.valuesMap[p4kIniKey] = p4kValue; + } continue iniKeysLoop; } else { for (var item in regexList) { - if (item.value.hasMatch(p4kIniKey)) { + if (p4kIniKey.startsWith(item.value)) { item.key.valuesMap[p4kIniKey] = serverValue; serverIniMap.remove(p4kIniKey); continue iniKeysLoop; @@ -103,15 +126,15 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel { } } } - if (unLocalization.valuesMap.isNotEmpty) { - classMap[unLocalization.id!] = unLocalization; - } if (serverIniMap.isNotEmpty) { for (var element in serverIniMap.keys) { unClass.valuesMap[element] = serverIniMap[element] ?? ""; } classMap[unClass.id!] = unClass; } + if (unLocalization.valuesMap.isNotEmpty) { + classMap[unLocalization.id!] = unLocalization; + } return classMap; } @@ -150,6 +173,7 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel { await localizationUIModel.downloadLocalizationFile( file, apiLocalizationData); } + state = state.copyWith(apiLocalizationData: apiLocalizationData); final serverGlobalIni = (await compute(LocalizationUIModel.readArchive, file.absolute.path)) .toString(); @@ -158,13 +182,80 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel { } Future readEnglishInI(String gameDir) async { - final data = await Unp4kCModel.unp4kTools( + var data = await Unp4kCModel.unp4kTools( appGlobalState.applicationBinaryModuleDir!, [ "extract_memory", "$gameDir\\Data.p4k", "Data\\Localization\\english\\global.ini" ]); + + // remove bom + if (data.length > 3 && + data[0] == 0xEF && + data[1] == 0xBB && + data[2] == 0xBF) { + data = data.sublist(3); + } + final iniData = String.fromCharCodes(data); return iniData; } + + onChangeMod(AppAdvancedLocalizationClassKeysData item, + AppAdvancedLocalizationClassKeysDataMode mode) async { + if (item.lockMod) return; + item.mode = mode; + item.isWorking = true; + final classMap = + Map.from(state.classMap!); + classMap[item.id!] = item; + state = state.copyWith(classMap: classMap); + + final p4kIniMap = readIniAsMap(state.p4kGlobalIni!); + final serverIniMap = readIniAsMap(state.serverGlobalIni!); + final newValuesMap = {}; + + for (var kv in item.valuesMap.entries) { + switch (mode) { + case AppAdvancedLocalizationClassKeysDataMode.localization: + newValuesMap[kv.key] = serverIniMap[kv.key] ?? ""; + break; + case AppAdvancedLocalizationClassKeysDataMode.unLocalization: + newValuesMap[kv.key] = p4kIniMap[kv.key] ?? ""; + break; + case AppAdvancedLocalizationClassKeysDataMode.mixed: + newValuesMap[kv.key] = + "${serverIniMap[kv.key]} [${p4kIniMap[kv.key]}]"; + break; + case AppAdvancedLocalizationClassKeysDataMode.mixedNewline: + newValuesMap[kv.key] = + "${serverIniMap[kv.key]}\\n${p4kIniMap[kv.key]}"; + break; + } + await Future.delayed(Duration.zero); + } + item.valuesMap = newValuesMap; + item.isWorking = false; + classMap[item.id!] = item; + state = state.copyWith(classMap: classMap); + } + + Future doInstall() async { + state = state.copyWith(workingText: "生成汉化文件..."); + final classMap = state.classMap!; + final globalIni = StringBuffer(); + for (var item in classMap.values) { + for (var kv in item.valuesMap.entries) { + globalIni.write("${kv.key}=${kv.value}\n"); + await Future.delayed(Duration.zero); + } + } + state = state.copyWith(workingText: "安装汉化文件..."); + final localizationUIModel = ref.read(localizationUIModelProvider.notifier); + await localizationUIModel.installFormString( + globalIni, state.apiLocalizationData?.versionName ?? "-", + advanced: true); + state = state.copyWith(workingText: ""); + return true; + } } diff --git a/lib/ui/home/localization/advanced_localization_ui_model.freezed.dart b/lib/ui/home/localization/advanced_localization_ui_model.freezed.dart index d92a929..8609746 100644 --- a/lib/ui/home/localization/advanced_localization_ui_model.freezed.dart +++ b/lib/ui/home/localization/advanced_localization_ui_model.freezed.dart @@ -21,6 +21,10 @@ mixin _$AdvancedLocalizationUIState { throw _privateConstructorUsedError; String? get p4kGlobalIni => throw _privateConstructorUsedError; String? get serverGlobalIni => throw _privateConstructorUsedError; + ScLocalizationData? get apiLocalizationData => + throw _privateConstructorUsedError; + int get p4kGlobalIniLines => throw _privateConstructorUsedError; + int get serverGlobalIniLines => throw _privateConstructorUsedError; @JsonKey(ignore: true) $AdvancedLocalizationUIStateCopyWith @@ -39,7 +43,10 @@ abstract class $AdvancedLocalizationUIStateCopyWith<$Res> { {String workingText, Map? classMap, String? p4kGlobalIni, - String? serverGlobalIni}); + String? serverGlobalIni, + ScLocalizationData? apiLocalizationData, + int p4kGlobalIniLines, + int serverGlobalIniLines}); } /// @nodoc @@ -60,6 +67,9 @@ class _$AdvancedLocalizationUIStateCopyWithImpl<$Res, Object? classMap = freezed, Object? p4kGlobalIni = freezed, Object? serverGlobalIni = freezed, + Object? apiLocalizationData = freezed, + Object? p4kGlobalIniLines = null, + Object? serverGlobalIniLines = null, }) { return _then(_value.copyWith( workingText: null == workingText @@ -78,6 +88,18 @@ class _$AdvancedLocalizationUIStateCopyWithImpl<$Res, ? _value.serverGlobalIni : serverGlobalIni // ignore: cast_nullable_to_non_nullable as String?, + apiLocalizationData: freezed == apiLocalizationData + ? _value.apiLocalizationData + : apiLocalizationData // ignore: cast_nullable_to_non_nullable + as ScLocalizationData?, + p4kGlobalIniLines: null == p4kGlobalIniLines + ? _value.p4kGlobalIniLines + : p4kGlobalIniLines // ignore: cast_nullable_to_non_nullable + as int, + serverGlobalIniLines: null == serverGlobalIniLines + ? _value.serverGlobalIniLines + : serverGlobalIniLines // ignore: cast_nullable_to_non_nullable + as int, ) as $Val); } } @@ -95,7 +117,10 @@ abstract class _$$AdvancedLocalizationUIStateImplCopyWith<$Res> {String workingText, Map? classMap, String? p4kGlobalIni, - String? serverGlobalIni}); + String? serverGlobalIni, + ScLocalizationData? apiLocalizationData, + int p4kGlobalIniLines, + int serverGlobalIniLines}); } /// @nodoc @@ -115,6 +140,9 @@ class __$$AdvancedLocalizationUIStateImplCopyWithImpl<$Res> Object? classMap = freezed, Object? p4kGlobalIni = freezed, Object? serverGlobalIni = freezed, + Object? apiLocalizationData = freezed, + Object? p4kGlobalIniLines = null, + Object? serverGlobalIniLines = null, }) { return _then(_$AdvancedLocalizationUIStateImpl( workingText: null == workingText @@ -133,6 +161,18 @@ class __$$AdvancedLocalizationUIStateImplCopyWithImpl<$Res> ? _value.serverGlobalIni : serverGlobalIni // ignore: cast_nullable_to_non_nullable as String?, + apiLocalizationData: freezed == apiLocalizationData + ? _value.apiLocalizationData + : apiLocalizationData // ignore: cast_nullable_to_non_nullable + as ScLocalizationData?, + p4kGlobalIniLines: null == p4kGlobalIniLines + ? _value.p4kGlobalIniLines + : p4kGlobalIniLines // ignore: cast_nullable_to_non_nullable + as int, + serverGlobalIniLines: null == serverGlobalIniLines + ? _value.serverGlobalIniLines + : serverGlobalIniLines // ignore: cast_nullable_to_non_nullable + as int, )); } } @@ -146,7 +186,10 @@ class _$AdvancedLocalizationUIStateImpl {this.workingText = "", final Map? classMap, this.p4kGlobalIni, - this.serverGlobalIni}) + this.serverGlobalIni, + this.apiLocalizationData, + this.p4kGlobalIniLines = 0, + this.serverGlobalIniLines = 0}) : _classMap = classMap; @override @@ -166,10 +209,18 @@ class _$AdvancedLocalizationUIStateImpl final String? p4kGlobalIni; @override final String? serverGlobalIni; + @override + final ScLocalizationData? apiLocalizationData; + @override + @JsonKey() + final int p4kGlobalIniLines; + @override + @JsonKey() + final int serverGlobalIniLines; @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'AdvancedLocalizationUIState(workingText: $workingText, classMap: $classMap, p4kGlobalIni: $p4kGlobalIni, serverGlobalIni: $serverGlobalIni)'; + return 'AdvancedLocalizationUIState(workingText: $workingText, classMap: $classMap, p4kGlobalIni: $p4kGlobalIni, serverGlobalIni: $serverGlobalIni, apiLocalizationData: $apiLocalizationData, p4kGlobalIniLines: $p4kGlobalIniLines, serverGlobalIniLines: $serverGlobalIniLines)'; } @override @@ -180,7 +231,10 @@ class _$AdvancedLocalizationUIStateImpl ..add(DiagnosticsProperty('workingText', workingText)) ..add(DiagnosticsProperty('classMap', classMap)) ..add(DiagnosticsProperty('p4kGlobalIni', p4kGlobalIni)) - ..add(DiagnosticsProperty('serverGlobalIni', serverGlobalIni)); + ..add(DiagnosticsProperty('serverGlobalIni', serverGlobalIni)) + ..add(DiagnosticsProperty('apiLocalizationData', apiLocalizationData)) + ..add(DiagnosticsProperty('p4kGlobalIniLines', p4kGlobalIniLines)) + ..add(DiagnosticsProperty('serverGlobalIniLines', serverGlobalIniLines)); } @override @@ -194,7 +248,13 @@ class _$AdvancedLocalizationUIStateImpl (identical(other.p4kGlobalIni, p4kGlobalIni) || other.p4kGlobalIni == p4kGlobalIni) && (identical(other.serverGlobalIni, serverGlobalIni) || - other.serverGlobalIni == serverGlobalIni)); + other.serverGlobalIni == serverGlobalIni) && + (identical(other.apiLocalizationData, apiLocalizationData) || + other.apiLocalizationData == apiLocalizationData) && + (identical(other.p4kGlobalIniLines, p4kGlobalIniLines) || + other.p4kGlobalIniLines == p4kGlobalIniLines) && + (identical(other.serverGlobalIniLines, serverGlobalIniLines) || + other.serverGlobalIniLines == serverGlobalIniLines)); } @override @@ -203,7 +263,10 @@ class _$AdvancedLocalizationUIStateImpl workingText, const DeepCollectionEquality().hash(_classMap), p4kGlobalIni, - serverGlobalIni); + serverGlobalIni, + apiLocalizationData, + p4kGlobalIniLines, + serverGlobalIniLines); @JsonKey(ignore: true) @override @@ -219,7 +282,10 @@ abstract class _AdvancedLocalizationUIState {final String workingText, final Map? classMap, final String? p4kGlobalIni, - final String? serverGlobalIni}) = _$AdvancedLocalizationUIStateImpl; + final String? serverGlobalIni, + final ScLocalizationData? apiLocalizationData, + final int p4kGlobalIniLines, + final int serverGlobalIniLines}) = _$AdvancedLocalizationUIStateImpl; @override String get workingText; @@ -230,6 +296,12 @@ abstract class _AdvancedLocalizationUIState @override String? get serverGlobalIni; @override + ScLocalizationData? get apiLocalizationData; + @override + int get p4kGlobalIniLines; + @override + int get serverGlobalIniLines; + @override @JsonKey(ignore: true) _$$AdvancedLocalizationUIStateImplCopyWith<_$AdvancedLocalizationUIStateImpl> get copyWith => throw _privateConstructorUsedError; diff --git a/lib/ui/home/localization/advanced_localization_ui_model.g.dart b/lib/ui/home/localization/advanced_localization_ui_model.g.dart index 6ba7e97..cd60f40 100644 --- a/lib/ui/home/localization/advanced_localization_ui_model.g.dart +++ b/lib/ui/home/localization/advanced_localization_ui_model.g.dart @@ -7,7 +7,7 @@ part of 'advanced_localization_ui_model.dart'; // ************************************************************************** String _$advancedLocalizationUIModelHash() => - r'2bb7dfc3cd8d45ecf5056083627136ad5cb7a285'; + r'55c9d02d4c78112b2772f789c2758c3ea88808cb'; /// See also [AdvancedLocalizationUIModel]. @ProviderFor(AdvancedLocalizationUIModel) diff --git a/lib/ui/home/localization/localization_dialog_ui.dart b/lib/ui/home/localization/localization_dialog_ui.dart index da7248b..8a44a56 100644 --- a/lib/ui/home/localization/localization_dialog_ui.dart +++ b/lib/ui/home/localization/localization_dialog_ui.dart @@ -86,7 +86,7 @@ class LocalizationDialogUI extends HookConsumerWidget { Row( children: [ Text(S.current.localization_info_installed_version( - state.patchStatus?.value ?? "")), + "${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? " (高级汉化)" : ""}")), const Spacer(), if (state.patchStatus?.value != S.current.home_action_info_game_built_in) diff --git a/lib/ui/home/localization/localization_ui_model.dart b/lib/ui/home/localization/localization_ui_model.dart index 0ac6d15..deddcd6 100644 --- a/lib/ui/home/localization/localization_ui_model.dart +++ b/lib/ui/home/localization/localization_ui_model.dart @@ -32,6 +32,7 @@ class LocalizationUIState with _$LocalizationUIState { Map? apiLocalizationData, @Default("") String workingVersion, MapEntry? patchStatus, + bool? isInstalledAdvanced, List? customizeList, @Default(false) bool enableCustomize, }) = _LocalizationUIState; @@ -222,7 +223,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { if (!await f.exists()) return; state = state.copyWith(workingVersion: filePath); final str = await f.readAsString(); - await _installFormString( + await installFormString( StringBuffer(str), S.current .localization_info_custom_file(getCustomizeFileName(filePath))); @@ -230,14 +231,19 @@ class LocalizationUIModel extends _$LocalizationUIModel { }; } - _installFormString(StringBuffer globalIni, String versionName) async { + installFormString(StringBuffer globalIni, String versionName, + {bool? advanced}) async { final iniFile = File( "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"); if (versionName.isNotEmpty) { if (!globalIni.toString().endsWith("\n")) { globalIni.write("\n"); } - globalIni.write("_starcitizen_doctor_localization_version=$versionName"); + if (advanced ?? false) { + globalIni.write("_starcitizen_doctor_localization_advanced=true\n"); + } + globalIni + .write("_starcitizen_doctor_localization_version=$versionName\n"); } /// write cfg @@ -275,7 +281,7 @@ class LocalizationUIModel extends _$LocalizationUIModel { if (globalIni.isEmpty) { throw S.current.localization_info_corrupted_file; } - await _installFormString(globalIni, value.versionName ?? ""); + await installFormString(globalIni, value.versionName ?? ""); } catch (e) { if (!context.mounted) return; await showToast( @@ -348,7 +354,19 @@ class LocalizationUIModel extends _$LocalizationUIModel { await _getLangCfgEnableLang(lang: state.selectedLanguage!), await _getInstalledIniVersion( "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini")); - state = state.copyWith(patchStatus: patchStatus); + final isInstalledAdvanced = await _checkAdvancedStatus( + "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"); + state = state.copyWith( + patchStatus: patchStatus, isInstalledAdvanced: isInstalledAdvanced); + } + + Future _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 _getLangCfgEnableLang({String lang = ""}) async { diff --git a/lib/ui/home/localization/localization_ui_model.freezed.dart b/lib/ui/home/localization/localization_ui_model.freezed.dart index 4e9e57d..37399db 100644 --- a/lib/ui/home/localization/localization_ui_model.freezed.dart +++ b/lib/ui/home/localization/localization_ui_model.freezed.dart @@ -21,6 +21,7 @@ mixin _$LocalizationUIState { throw _privateConstructorUsedError; String get workingVersion => throw _privateConstructorUsedError; MapEntry? get patchStatus => throw _privateConstructorUsedError; + bool? get isInstalledAdvanced => throw _privateConstructorUsedError; List? get customizeList => throw _privateConstructorUsedError; bool get enableCustomize => throw _privateConstructorUsedError; @@ -40,6 +41,7 @@ abstract class $LocalizationUIStateCopyWith<$Res> { Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, + bool? isInstalledAdvanced, List? customizeList, bool enableCustomize}); } @@ -61,6 +63,7 @@ class _$LocalizationUIStateCopyWithImpl<$Res, $Val extends LocalizationUIState> Object? apiLocalizationData = freezed, Object? workingVersion = null, Object? patchStatus = freezed, + Object? isInstalledAdvanced = freezed, Object? customizeList = freezed, Object? enableCustomize = null, }) { @@ -81,6 +84,10 @@ class _$LocalizationUIStateCopyWithImpl<$Res, $Val extends LocalizationUIState> ? _value.patchStatus : patchStatus // ignore: cast_nullable_to_non_nullable as MapEntry?, + isInstalledAdvanced: freezed == isInstalledAdvanced + ? _value.isInstalledAdvanced + : isInstalledAdvanced // ignore: cast_nullable_to_non_nullable + as bool?, customizeList: freezed == customizeList ? _value.customizeList : customizeList // ignore: cast_nullable_to_non_nullable @@ -106,6 +113,7 @@ abstract class _$$LocalizationUIStateImplCopyWith<$Res> Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, + bool? isInstalledAdvanced, List? customizeList, bool enableCustomize}); } @@ -125,6 +133,7 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res> Object? apiLocalizationData = freezed, Object? workingVersion = null, Object? patchStatus = freezed, + Object? isInstalledAdvanced = freezed, Object? customizeList = freezed, Object? enableCustomize = null, }) { @@ -145,6 +154,10 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res> ? _value.patchStatus : patchStatus // ignore: cast_nullable_to_non_nullable as MapEntry?, + isInstalledAdvanced: freezed == isInstalledAdvanced + ? _value.isInstalledAdvanced + : isInstalledAdvanced // ignore: cast_nullable_to_non_nullable + as bool?, customizeList: freezed == customizeList ? _value._customizeList : customizeList // ignore: cast_nullable_to_non_nullable @@ -165,6 +178,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { final Map? apiLocalizationData, this.workingVersion = "", this.patchStatus, + this.isInstalledAdvanced, final List? customizeList, this.enableCustomize = false}) : _apiLocalizationData = apiLocalizationData, @@ -188,6 +202,8 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { final String workingVersion; @override final MapEntry? patchStatus; + @override + final bool? isInstalledAdvanced; final List? _customizeList; @override List? get customizeList { @@ -204,7 +220,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { @override String toString() { - return 'LocalizationUIState(selectedLanguage: $selectedLanguage, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, customizeList: $customizeList, enableCustomize: $enableCustomize)'; + return 'LocalizationUIState(selectedLanguage: $selectedLanguage, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList, enableCustomize: $enableCustomize)'; } @override @@ -220,6 +236,8 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { other.workingVersion == workingVersion) && (identical(other.patchStatus, patchStatus) || other.patchStatus == patchStatus) && + (identical(other.isInstalledAdvanced, isInstalledAdvanced) || + other.isInstalledAdvanced == isInstalledAdvanced) && const DeepCollectionEquality() .equals(other._customizeList, _customizeList) && (identical(other.enableCustomize, enableCustomize) || @@ -233,6 +251,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { const DeepCollectionEquality().hash(_apiLocalizationData), workingVersion, patchStatus, + isInstalledAdvanced, const DeepCollectionEquality().hash(_customizeList), enableCustomize); @@ -250,6 +269,7 @@ abstract class _LocalizationUIState implements LocalizationUIState { final Map? apiLocalizationData, final String workingVersion, final MapEntry? patchStatus, + final bool? isInstalledAdvanced, final List? customizeList, final bool enableCustomize}) = _$LocalizationUIStateImpl; @@ -262,6 +282,8 @@ abstract class _LocalizationUIState implements LocalizationUIState { @override MapEntry? get patchStatus; @override + bool? get isInstalledAdvanced; + @override List? get customizeList; @override bool get enableCustomize; diff --git a/lib/ui/home/localization/localization_ui_model.g.dart b/lib/ui/home/localization/localization_ui_model.g.dart index f194d40..6c2bae0 100644 --- a/lib/ui/home/localization/localization_ui_model.g.dart +++ b/lib/ui/home/localization/localization_ui_model.g.dart @@ -7,7 +7,7 @@ part of 'localization_ui_model.dart'; // ************************************************************************** String _$localizationUIModelHash() => - r'ed47da78fdc6adac904a17909f111640ac84563e'; + r'892a302b28ce4446cab7591f54008fbcc2e5eae0'; /// See also [LocalizationUIModel]. @ProviderFor(LocalizationUIModel) diff --git a/lib/ui/tools/unp4kc/unp4kc_ui.dart b/lib/ui/tools/unp4kc/unp4kc_ui.dart index d3f4306..b96513d 100644 --- a/lib/ui/tools/unp4kc/unp4kc_ui.dart +++ b/lib/ui/tools/unp4kc/unp4kc_ui.dart @@ -267,6 +267,7 @@ class _TextTempWidget extends HookConsumerWidget { return CodeEditor( controller: CodeLineEditingController.fromText('${textData.value}'), + readOnly: true, ); } } diff --git a/pubspec.yaml b/pubspec.yaml index abb06a8..18f0205 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,6 +62,7 @@ dependencies: super_sliver_list: ^0.4.1 file: ^7.0.0 re_editor: ^0.2.0 + re_highlight: ^0.0.3 dependency_overrides: http: ^1.1.2