From 9a875fc5a5cd5df29dd2bfea7d1b3f78767043ac Mon Sep 17 00:00:00 2001 From: xkeyC <3334969096@qq.com> Date: Fri, 3 May 2024 22:35:31 +0800 Subject: [PATCH] feat: AdvancedLocalization Init --- assets/advanced_localization.json | 531 ++++++++++++++++++ lib/app.dart | 5 + lib/app.g.dart | 4 +- lib/common/utils/log.dart | 1 + lib/data/app_advanced_localization_data.dart | 59 ++ lib/provider/unp4kc.g.dart | 2 +- .../advanced_localization_ui.dart | 102 ++++ .../advanced_localization_ui_model.dart | 170 ++++++ ...dvanced_localization_ui_model.freezed.dart | 236 ++++++++ .../advanced_localization_ui_model.g.dart | 28 + .../localization/localization_dialog_ui.dart | 29 +- .../localization/localization_ui_model.dart | 42 +- .../localization/localization_ui_model.g.dart | 2 +- lib/ui/tools/tools_ui_model.g.dart | 2 +- 14 files changed, 1175 insertions(+), 38 deletions(-) create mode 100644 assets/advanced_localization.json create mode 100644 lib/data/app_advanced_localization_data.dart create mode 100644 lib/ui/home/localization/advanced_localization_ui.dart create mode 100644 lib/ui/home/localization/advanced_localization_ui_model.dart create mode 100644 lib/ui/home/localization/advanced_localization_ui_model.freezed.dart create mode 100644 lib/ui/home/localization/advanced_localization_ui_model.g.dart diff --git a/assets/advanced_localization.json b/assets/advanced_localization.json new file mode 100644 index 0000000..9b82051 --- /dev/null +++ b/assets/advanced_localization.json @@ -0,0 +1,531 @@ +{ + "class_keys": [ + { + "id": "location", + "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_.*", + "garage_.*", + "hangar_.*", + "landing_pad_.*", + "pyro_.*", + "room_.*", + "security_.*", + "select_.*", + "shop_name_.*", + "vehicle_room_.*", + "warning_.*", + "weapon_stats.*", + "weapons_heal_.*" + ] + }, + { + "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_.*" + ] + }, + { + "class_name": "物品", + "id": "thing", + "keys": [ + "BarMenu_.*", + "FLOOR_Flair_Items.*", + "FPS_AI_.*", + "ItemPort_port_NameCooler.*", + "Item_.*", + "destoryitems_.*", + "destroyItems_.*", + "item.*", + "kopion_.*" + ] + }, + { + "class_name": "载具", + "id": "vehicle", + "keys": [ + "vehicle_Name.*", + "vehicle_name.*", + "vehicle_Desc.*", + "vehicl_Desc.*", + "vehicel_Desc.*", + "vehicles_.*" + ] + }, + { + "id": "mission", + "class_name": "任务/日志", + "keys": [ + "BasicSalvage_.*", + "BitZeros_.*", + "BHG_.*", + "BlacJac_.*", + "BountyHuntersGuild_.*", + "BrightSky_.*", + "CThing_.*", + "ChainedDeliveryModule_.*", + "Clovis_Safe_.*", + "CommArray_.*", + "Commissary_.*", + "Community_Service.*", + "CovalexIC_.*", + "Constantine_.*", + "Covalex_.*", + "Criminal_.*", + "CrusaderSecurity_.*", + "Crusader_ReputationJournal_.*", + "Crusader_from_.*", + "DC_room_.*", + "DataHeist_.*", + "Datapad_.*", + "Delivery_.*", + "Derelict_.*", + "DestroyDebris_.*", + "DestroyEvidence_.*", + "DestroyObj_Text_.*", + "DistributionCenter_.*", + "DynamicEvent_.*", + "Eckhart_.*", + "FPSPVEVS_.*", + "FTL_.*", + "FW22_NT_Journal_Title_.*", + "ForceDepletion_.*", + "GLoc_Bartender_Conv_.*", + "GMCapture_.*", + "GarethWIP_.*", + "Gathering_.*", + "Gen_NPC_Conv_.*", + "Greeter_.*", + "HeadHunters_.*", + "HexPenetrator_.*", + "Hurston_ReputationJournal_.*", + "Hurston_bounty_.*", + "Hurston_destroystash_.*", + "Hurston_eliminateall_.*", + "Hurston_eliminatespecifc_.*", + "Hurston_killcreatureslocation_.*", + "Hurston_searchbody_.*", + "IAE2951_.*", + "IAE2952_.*", + "IAE952_.*", + "Info_Kiosks_.*", + "Infractions_.*", + "Investigation_.*", + "Invictus2951_.*", + "Invictus_.*", + "JournalEntry_.*", + "JournalText.*", + "Journal_.*", + "Jumptown2_.*", + "JurisdictionJournals_.*", + "Kill_Civ_.*", + "LingFamily_.*", + "Ling_.*", + "LocalDelivery_.*", + "Local_Delivery_.*", + "MG_.*", + "MTPS_.*", + "MTProtectiveServices_.*", + "MilesEckhart_.*", + "Mission.*", + "Missions.*", + "Mtps_killallcreatures_.*", + "NB_.*", + "NPE_.*", + "NTLockdown_.*", + "Name_.*", + "NorthRock_.*", + "Orison_CRUShowroom_.*", + "Orison_DiscoverySpot_.*", + "OutlawSweep_.*", + "Outpost_.*", + "Pacheco_.*", + "PreventData_.*", + "ProtLife_.*", + "RAIN_.*", + "RacingRep_.*", + "Racing_Ship_DisplayName.*", + "RedWind_.*", + "RepScope_.*", + "RepStanding_.*", + "RepairOxygenKiosk_.*", + "Reststop_.*", + "RetakeLocation_.*", + "RetrieveConsignment_.*", + "RetrieveDatapad_.*", + "ReturnObjective_.*", + "ReturnToLocation_.*", + "Roughready_.*", + "Ruto_.*", + "SB_.*", + "SalvageContractor_.*", + "ScanVehicleLocationModule_.*", + "ScrambleRace_.*", + "ScreamingGalsons_.*", + "ServiceBeacon_.*", + "ShipName_.*", + "ShipStrip_.*", + "Special_Event_.*", + "Station_Illegal_.*", + "Staton4_NewBab_.*", + "StealEvidence_.*", + "TarPits_.*", + "Temp_.*", + "Terminal_.*", + "Text_.*", + "TimeSensative_.*", + "TimeSensitive_.*", + "TimeTrial_.*", + "TimedSalvage_.*", + "TransportGuild_.*", + "TravelObjective_.*", + "Tut.*", + "Tutorial.*", + "UDM_.*", + "Vaughn_.*", + "VendingMachien_.*", + "VendingMachin_.*", + "VendingMachine_.*", + "WSTR_.*", + "WantedLevel5_.*", + "XenoThreat_.*", + "acquirepart_.*", + "alt_criminal_collect_reclaimer_desc.*", + "aracersurvivesrace_.*", + "assassin_.*", + "assassination_.*", + "asteroidfield_.*", + "basesweep_.*", + "bitzeros_.*", + "blacJac_.*", + "blackbox_.*", + "blackboxillegal_.*", + "blackboxlegal_.*", + "boarders_.*", + "bounty_.*", + "bountymarker_.*", + "cave_.*", + "ccdemo19_.*", + "cdf_.*", + "cfp_.*", + "civilian_.*", + "claimsweep_.*", + "collect_reclaimer_.*", + "combatassist_.*", + "commarray_hack_.*", + "commarray_repair_.*", + "confirmkill_.*", + "constantine_.*", + "criminal_.*", + "crus_.*", + "crusader_.*", + "delivery_.*", + "deliverydecoy_.*", + "deploypiggyback_.*", + "deployprobe_.*", + "destroyblade_.*", + "destroyitem_.*", + "destroyitems_.*", + "destroyitemsspace_.*", + "destroyprobe_.*", + "destroyprobeillegal_.*", + "destroyprobelegal_.*", + "destroyprove_illegal_.*", + "destroystash_.*", + "dfmcrusader_.*", + "distraction_.*", + "distractionkill_.*", + "dusters_.*", + "ecn_.*", + "eliminatespecificracer_.*", + "escort_.*", + "escortscan_.*", + "fleetweek2950_.*", + "fps_.*", + "goupbounty_.*", + "groupbounty_.*", + "hack_prevent_.*", + "hdactivist_.*", + "headhunters_.*", + "hurston_.*", + "iae2953_.*", + "kareah_.*", + "kareahsweep_.*", + "killallanimal_.*", + "killallanimals_.*", + "killallkopion_.*", + "killallmarok_.*", + "killanimalslocation_.*", + "killatlocation_.*", + "killcollect_.*", + "killcollectanimal_.*", + "killcollectkopion_.*", + "killcollectmarok_.*", + "loc_.*", + "manufacturer_.*", + "meet_miles_.*", + "mgClovus_.*", + "mg_*", + "missionManager_.*", + "mission_.*", + "mtps_.*", + "murderspree_.*", + "mustcomesecond_.*", + "ninetails_.*", + "northrock_.*", + "notification_.*", + "openbounty_.*", + "outlawsweep_.*", + "outpost_.*", + "p_protectandresupply_.*", + "p_showdown_.*", + "pickanddestroy_.*", + "planetcollect_.*", + "prisonerbreak_.*", + "prisonermanifest_.*", + "protlife_.*", + "racelastforxcheckpoints_.*", + "recoverspace_.*", + "recoverstash_.*", + "recoverstolen_.*", + "recovery_.*", + "redwind_.*", + "roughready_.*", + "sandbox_.*", + "seachbody_.*", + "searchbody_.*", + "searchcrew_.*", + "sectorsweep_.*", + "shubin_.*", + "shuttle_.*", + "spacecargo_.*", + "spacecollect_.*", + "spacesteal_.*", + "stanton.*", + "stealfromship_.*", + "stealitem_.*", + "takecheckpointsxandy_.*", + "ugf_.*", + "vaughn_.*", + "winwithoutkillingracer_.*", + "xdamagetoracersinorder_.*", + "xenothreat_.*" + ] + }, + { + "id": "subtitle", + "class_name": "字幕", + "keys": [ + "DXSH_", + "Dlg_SC_.*", + "FW22_NT_Datapad_.*", + "FleetWeek2950_.*", + "GenResponse_.*", + "GenericLanding_.*", + "IT_Shared_.*", + "Imperilled_.*", + "MKTG_CUSTOMS1_CV_Access_.*", + "PH_PU_.*", + "PU_.*", + "Pacheco_.*", + "SC_ac_.*", + "SC_lz_.*", + "SM_SIMANN1_.*", + "contract_.*", + "covalex_.*", + "covalexrand_.*", + "covalexspec_.*" + ] + } + ] +} \ No newline at end of file diff --git a/lib/app.dart b/lib/app.dart index 819ad06..a86e29a 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -28,6 +28,7 @@ import 'data/app_version_data.dart'; import 'generated/no_l10n_strings.dart'; import 'ui/home/downloader/home_downloader_ui.dart'; import 'ui/home/game_doctor/game_doctor_ui.dart'; +import 'ui/home/localization/advanced_localization_ui.dart'; import 'ui/index_ui.dart'; import 'ui/settings/upgrade_dialog.dart'; import 'ui/tools/unp4kc/unp4kc_ui.dart'; @@ -77,6 +78,10 @@ GoRouter router(RouterRef ref) { pageBuilder: (context, state) => myPageBuilder(context, state, const HomePerformanceUI()), ), + GoRoute( + path: 'advanced_localization', + pageBuilder: (context, state) => + myPageBuilder(context, state, const AdvancedLocalizationUI())) ], ), GoRoute(path: '/tools', builder: (_, __) => const SizedBox(), routes: [ diff --git a/lib/app.g.dart b/lib/app.g.dart index 600b4ff..ec72d84 100644 --- a/lib/app.g.dart +++ b/lib/app.g.dart @@ -6,7 +6,7 @@ part of 'app.dart'; // RiverpodGenerator // ************************************************************************** -String _$routerHash() => r'7ce5ef6a7a4f6f604a457dd050e04ee594c4760a'; +String _$routerHash() => r'4fb9802d06347972b530f17ea7d66724a6ded997'; /// See also [router]. @ProviderFor(router) @@ -20,7 +20,7 @@ final routerProvider = AutoDisposeProvider.internal( ); typedef RouterRef = AutoDisposeProviderRef; -String _$appGlobalModelHash() => r'11a172d5fb19ad6566f1025354c8f8d91fe85a84'; +String _$appGlobalModelHash() => r'9dccbb898714695ef8b640a5b5dfb361783a0f45'; /// See also [AppGlobalModel]. @ProviderFor(AppGlobalModel) diff --git a/lib/common/utils/log.dart b/lib/common/utils/log.dart index 2184ab2..751328e 100644 --- a/lib/common/utils/log.dart +++ b/lib/common/utils/log.dart @@ -11,6 +11,7 @@ File? _logFile; void dPrint(src) async { if (kDebugMode) { print(src); + return; } await _logLock.synchronized(() async { try { diff --git a/lib/data/app_advanced_localization_data.dart b/lib/data/app_advanced_localization_data.dart new file mode 100644 index 0000000..e672f70 --- /dev/null +++ b/lib/data/app_advanced_localization_data.dart @@ -0,0 +1,59 @@ +class AppAdvancedLocalizationData { + AppAdvancedLocalizationData({ + this.classKeys, + }); + + AppAdvancedLocalizationData.fromJson(dynamic json) { + if (json['class_keys'] != null) { + classKeys = []; + json['class_keys'].forEach((v) { + classKeys?.add(AppAdvancedLocalizationClassKeysData.fromJson(v)); + }); + } + } + + List? classKeys; + + Map toJson() { + final map = {}; + if (classKeys != null) { + map['class_keys'] = classKeys?.map((v) => v.toJson()).toList(); + } + return map; + } +} + +class AppAdvancedLocalizationClassKeysData { + AppAdvancedLocalizationClassKeysData({ + this.id, + this.className, + this.keys, + }); + + AppAdvancedLocalizationClassKeysData.fromJson(dynamic json) { + id = json['id']; + className = json['class_name']; + keys = json['keys'] != null ? json['keys'].cast() : []; + } + + String? id; + String? className; + List? keys; + Map valuesMap = {}; + AppAdvancedLocalizationClassKeysDataMode mode = + AppAdvancedLocalizationClassKeysDataMode.localization; + + Map toJson() { + final map = {}; + map['id'] = id; + map['class_name'] = className; + map['keys'] = keys; + return map; + } +} + +enum AppAdvancedLocalizationClassKeysDataMode { + unLocalization, + localization, + mixed, +} diff --git a/lib/provider/unp4kc.g.dart b/lib/provider/unp4kc.g.dart index e643e32..08c2126 100644 --- a/lib/provider/unp4kc.g.dart +++ b/lib/provider/unp4kc.g.dart @@ -6,7 +6,7 @@ part of 'unp4kc.dart'; // RiverpodGenerator // ************************************************************************** -String _$unp4kCModelHash() => r'69117c5857797683e2d080da9238bfecc5948898'; +String _$unp4kCModelHash() => r'3b2325c72dcb47bf9207625b0a1d2a611f911b6f'; /// See also [Unp4kCModel]. @ProviderFor(Unp4kCModel) diff --git a/lib/ui/home/localization/advanced_localization_ui.dart b/lib/ui/home/localization/advanced_localization_ui.dart new file mode 100644 index 0000000..1ef3544 --- /dev/null +++ b/lib/ui/home/localization/advanced_localization_ui.dart @@ -0,0 +1,102 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:hooks_riverpod/hooks_riverpod.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'; +import 'package:super_sliver_list/super_sliver_list.dart'; + +class AdvancedLocalizationUI extends HookConsumerWidget { + const AdvancedLocalizationUI({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final state = ref.watch(advancedLocalizationUIModelProvider); + final homeUIState = ref.watch(homeUIModelProvider); + return makeDefaultPage( + title: "高级汉化 -> ${homeUIState.scInstalledPath}", + context, + content: state.workingText.isNotEmpty + ? Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const ProgressRing(), + const SizedBox(height: 12), + Text(state.workingText), + ], + ), + ) + : _makeBody(context, homeUIState, state, ref)); + } + + Widget _makeBody(BuildContext context, HomeUIModelState homeUIState, + AdvancedLocalizationUIState state, WidgetRef ref) { + return AlignedGridView.count( + crossAxisCount: 3, + 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), + decoration: BoxDecoration( + color: Colors.white.withOpacity(.05), + borderRadius: BorderRadius.circular(4), + ), + 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), + ), + ), + ], + ), + ), + Container( + margin: const EdgeInsets.only(top: 6, bottom: 12), + width: MediaQuery.of(context).size.width, + height: 1, + color: Colors.white.withOpacity(.1), + ), + SizedBox( + height: 160, + 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, + ), + ); + }, + ), + ), + ], + ), + ); + }, + itemCount: state.classMap?.length ?? 0, + ); + } +} diff --git a/lib/ui/home/localization/advanced_localization_ui_model.dart b/lib/ui/home/localization/advanced_localization_ui_model.dart new file mode 100644 index 0000000..afdca6a --- /dev/null +++ b/lib/ui/home/localization/advanced_localization_ui_model.dart @@ -0,0 +1,170 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +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/provider/unp4kc.dart'; + +import '../home_ui_model.dart'; +import 'localization_ui_model.dart'; + +part 'advanced_localization_ui_model.g.dart'; + +part 'advanced_localization_ui_model.freezed.dart'; + +@freezed +class AdvancedLocalizationUIState with _$AdvancedLocalizationUIState { + factory AdvancedLocalizationUIState({ + @Default("") String workingText, + Map? classMap, + String? p4kGlobalIni, + String? serverGlobalIni, + }) = _AdvancedLocalizationUIState; +} + +@riverpod +class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel { + @override + AdvancedLocalizationUIState build() { + final localizationUIState = ref.read(localizationUIModelProvider); + final localizationUIModel = ref.read(localizationUIModelProvider.notifier); + state = AdvancedLocalizationUIState(classMap: {}); + _init(localizationUIState, localizationUIModel); + return state; + } + + Future _init(LocalizationUIState localizationUIState, + LocalizationUIModel localizationUIModel) async { + final (p4kGlobalIni, serverGlobalIni) = + await _readIni(localizationUIState, localizationUIModel); + final ald = await _readClassJson(); + if (ald.classKeys == null) return; + state = state.copyWith(workingText: "正在分类 ..."); + final m = await compute(_doClassIni, (ald, p4kGlobalIni, serverGlobalIni)); + state = state.copyWith( + workingText: "", + p4kGlobalIni: p4kGlobalIni, + serverGlobalIni: serverGlobalIni, + classMap: m); + } + + static Map _doClassIni( + ( + AppAdvancedLocalizationData ald, + String p4kGlobalIni, + String serverGlobalIni + ) v, + ) { + final ( + AppAdvancedLocalizationData ald, + String p4kGlobalIni, + String serverGlobalIni, + ) = v; + final unLocalization = AppAdvancedLocalizationClassKeysData( + id: "un_localization", + className: "未汉化", + keys: [], + ); + final unClass = AppAdvancedLocalizationClassKeysData( + id: "un_class", + className: "未分类", + keys: [], + ); + final classMap = { + for (final keys in ald.classKeys!) keys.id ?? "": keys, + }; + + final p4kIniMap = readIniAsMap(p4kGlobalIni); + final serverIniMap = readIniAsMap(serverGlobalIni); + + var regexList = classMap.values + .expand((c) => c.keys!.map((k) => MapEntry(c, RegExp(k)))) + .toList(); + + iniKeysLoop: + for (var p4kIniKey in p4kIniMap.keys) { + final serverValue = serverIniMap[p4kIniKey]; + if (serverValue == null) { + unLocalization.valuesMap[p4kIniKey] = p4kIniMap[p4kIniKey] ?? ""; + continue iniKeysLoop; + } else { + for (var item in regexList) { + if (item.value.hasMatch(p4kIniKey)) { + item.key.valuesMap[p4kIniKey] = serverValue; + serverIniMap.remove(p4kIniKey); + continue iniKeysLoop; + } + } + } + } + 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; + } + return classMap; + } + + static Map readIniAsMap(String iniString) { + final iniMap = {}; + for (final line in iniString.split("\n")) { + final index = line.indexOf("="); + if (index == -1) continue; + final key = line.substring(0, index).trim(); + final value = line.substring(index + 1).trim(); + iniMap[key] = value; + } + return iniMap; + } + + Future _readClassJson() async { + final s = await rootBundle.loadString("assets/advanced_localization.json"); + return AppAdvancedLocalizationData.fromJson(json.decode(s)); + } + + 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: "读取 p4k 文件 ..."); + final p4kGlobalIni = await readEnglishInI(gameDir); + dPrint("read p4kGlobalIni => ${p4kGlobalIni.length}"); + state = state.copyWith(workingText: "获取汉化文本 ..."); + 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); + } + final serverGlobalIni = + (await compute(LocalizationUIModel.readArchive, file.absolute.path)) + .toString(); + dPrint("read serverGlobalIni => ${serverGlobalIni.length}"); + return (p4kGlobalIni, serverGlobalIni); + } + + Future readEnglishInI(String gameDir) async { + final data = await Unp4kCModel.unp4kTools( + appGlobalState.applicationBinaryModuleDir!, [ + "extract_memory", + "$gameDir\\Data.p4k", + "Data\\Localization\\english\\global.ini" + ]); + final iniData = String.fromCharCodes(data); + return iniData; + } +} diff --git a/lib/ui/home/localization/advanced_localization_ui_model.freezed.dart b/lib/ui/home/localization/advanced_localization_ui_model.freezed.dart new file mode 100644 index 0000000..d92a929 --- /dev/null +++ b/lib/ui/home/localization/advanced_localization_ui_model.freezed.dart @@ -0,0 +1,236 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'advanced_localization_ui_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$AdvancedLocalizationUIState { + String get workingText => throw _privateConstructorUsedError; + Map? get classMap => + throw _privateConstructorUsedError; + String? get p4kGlobalIni => throw _privateConstructorUsedError; + String? get serverGlobalIni => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $AdvancedLocalizationUIStateCopyWith + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AdvancedLocalizationUIStateCopyWith<$Res> { + factory $AdvancedLocalizationUIStateCopyWith( + AdvancedLocalizationUIState value, + $Res Function(AdvancedLocalizationUIState) then) = + _$AdvancedLocalizationUIStateCopyWithImpl<$Res, + AdvancedLocalizationUIState>; + @useResult + $Res call( + {String workingText, + Map? classMap, + String? p4kGlobalIni, + String? serverGlobalIni}); +} + +/// @nodoc +class _$AdvancedLocalizationUIStateCopyWithImpl<$Res, + $Val extends AdvancedLocalizationUIState> + implements $AdvancedLocalizationUIStateCopyWith<$Res> { + _$AdvancedLocalizationUIStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? workingText = null, + Object? classMap = freezed, + Object? p4kGlobalIni = freezed, + Object? serverGlobalIni = freezed, + }) { + return _then(_value.copyWith( + workingText: null == workingText + ? _value.workingText + : workingText // ignore: cast_nullable_to_non_nullable + as String, + classMap: freezed == classMap + ? _value.classMap + : classMap // ignore: cast_nullable_to_non_nullable + as Map?, + p4kGlobalIni: freezed == p4kGlobalIni + ? _value.p4kGlobalIni + : p4kGlobalIni // ignore: cast_nullable_to_non_nullable + as String?, + serverGlobalIni: freezed == serverGlobalIni + ? _value.serverGlobalIni + : serverGlobalIni // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$AdvancedLocalizationUIStateImplCopyWith<$Res> + implements $AdvancedLocalizationUIStateCopyWith<$Res> { + factory _$$AdvancedLocalizationUIStateImplCopyWith( + _$AdvancedLocalizationUIStateImpl value, + $Res Function(_$AdvancedLocalizationUIStateImpl) then) = + __$$AdvancedLocalizationUIStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String workingText, + Map? classMap, + String? p4kGlobalIni, + String? serverGlobalIni}); +} + +/// @nodoc +class __$$AdvancedLocalizationUIStateImplCopyWithImpl<$Res> + extends _$AdvancedLocalizationUIStateCopyWithImpl<$Res, + _$AdvancedLocalizationUIStateImpl> + implements _$$AdvancedLocalizationUIStateImplCopyWith<$Res> { + __$$AdvancedLocalizationUIStateImplCopyWithImpl( + _$AdvancedLocalizationUIStateImpl _value, + $Res Function(_$AdvancedLocalizationUIStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? workingText = null, + Object? classMap = freezed, + Object? p4kGlobalIni = freezed, + Object? serverGlobalIni = freezed, + }) { + return _then(_$AdvancedLocalizationUIStateImpl( + workingText: null == workingText + ? _value.workingText + : workingText // ignore: cast_nullable_to_non_nullable + as String, + classMap: freezed == classMap + ? _value._classMap + : classMap // ignore: cast_nullable_to_non_nullable + as Map?, + p4kGlobalIni: freezed == p4kGlobalIni + ? _value.p4kGlobalIni + : p4kGlobalIni // ignore: cast_nullable_to_non_nullable + as String?, + serverGlobalIni: freezed == serverGlobalIni + ? _value.serverGlobalIni + : serverGlobalIni // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$AdvancedLocalizationUIStateImpl + with DiagnosticableTreeMixin + implements _AdvancedLocalizationUIState { + _$AdvancedLocalizationUIStateImpl( + {this.workingText = "", + final Map? classMap, + this.p4kGlobalIni, + this.serverGlobalIni}) + : _classMap = classMap; + + @override + @JsonKey() + final String workingText; + final Map? _classMap; + @override + Map? get classMap { + final value = _classMap; + if (value == null) return null; + if (_classMap is EqualUnmodifiableMapView) return _classMap; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + final String? p4kGlobalIni; + @override + final String? serverGlobalIni; + + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'AdvancedLocalizationUIState(workingText: $workingText, classMap: $classMap, p4kGlobalIni: $p4kGlobalIni, serverGlobalIni: $serverGlobalIni)'; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('type', 'AdvancedLocalizationUIState')) + ..add(DiagnosticsProperty('workingText', workingText)) + ..add(DiagnosticsProperty('classMap', classMap)) + ..add(DiagnosticsProperty('p4kGlobalIni', p4kGlobalIni)) + ..add(DiagnosticsProperty('serverGlobalIni', serverGlobalIni)); + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AdvancedLocalizationUIStateImpl && + (identical(other.workingText, workingText) || + other.workingText == workingText) && + const DeepCollectionEquality().equals(other._classMap, _classMap) && + (identical(other.p4kGlobalIni, p4kGlobalIni) || + other.p4kGlobalIni == p4kGlobalIni) && + (identical(other.serverGlobalIni, serverGlobalIni) || + other.serverGlobalIni == serverGlobalIni)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + workingText, + const DeepCollectionEquality().hash(_classMap), + p4kGlobalIni, + serverGlobalIni); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$AdvancedLocalizationUIStateImplCopyWith<_$AdvancedLocalizationUIStateImpl> + get copyWith => __$$AdvancedLocalizationUIStateImplCopyWithImpl< + _$AdvancedLocalizationUIStateImpl>(this, _$identity); +} + +abstract class _AdvancedLocalizationUIState + implements AdvancedLocalizationUIState { + factory _AdvancedLocalizationUIState( + {final String workingText, + final Map? classMap, + final String? p4kGlobalIni, + final String? serverGlobalIni}) = _$AdvancedLocalizationUIStateImpl; + + @override + String get workingText; + @override + Map? get classMap; + @override + String? get p4kGlobalIni; + @override + String? get serverGlobalIni; + @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 new file mode 100644 index 0000000..6ba7e97 --- /dev/null +++ b/lib/ui/home/localization/advanced_localization_ui_model.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'advanced_localization_ui_model.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$advancedLocalizationUIModelHash() => + r'2bb7dfc3cd8d45ecf5056083627136ad5cb7a285'; + +/// See also [AdvancedLocalizationUIModel]. +@ProviderFor(AdvancedLocalizationUIModel) +final advancedLocalizationUIModelProvider = AutoDisposeNotifierProvider< + AdvancedLocalizationUIModel, AdvancedLocalizationUIState>.internal( + AdvancedLocalizationUIModel.new, + name: r'advancedLocalizationUIModelProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$advancedLocalizationUIModelHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$AdvancedLocalizationUIModel + = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/ui/home/localization/localization_dialog_ui.dart b/lib/ui/home/localization/localization_dialog_ui.dart index 63aba59..da7248b 100644 --- a/lib/ui/home/localization/localization_dialog_ui.dart +++ b/lib/ui/home/localization/localization_dialog_ui.dart @@ -2,6 +2,7 @@ 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:flutter_tilt/flutter_tilt.dart'; +import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:starcitizen_doctor/data/sc_localization_data.dart'; import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart'; @@ -145,7 +146,7 @@ class LocalizationDialogUI extends HookConsumerWidget { ], context, gridViewMode: true), - makeToolsListContainer(context, model), + makeToolsListContainer(context, model, state), ], ), ), @@ -445,8 +446,8 @@ class LocalizationDialogUI extends HookConsumerWidget { } } - Widget makeToolsListContainer( - BuildContext context, LocalizationUIModel model) { + Widget makeToolsListContainer(BuildContext context, LocalizationUIModel model, + LocalizationUIState state) { final toolsMenu = { "launcher_mod": ( const Icon(FluentIcons.c_plus_plus, size: 24), @@ -462,21 +463,29 @@ class LocalizationDialogUI extends HookConsumerWidget { ), }; + final enableTap = state.workingVersion.isEmpty; + return makeListContainer( "汉化工具", [ for (final item in toolsMenu.entries) Tilt( + disable: !enableTap, shadowConfig: const ShadowConfig(maxIntensity: .3), borderRadius: BorderRadius.circular(7), child: GestureDetector( - onTap: () async { - switch (item.key) { - case "launcher_mod": - ToolsUIModel.rsiEnhance(context); - break; - } - }, + onTap: enableTap + ? () async { + switch (item.key) { + case "launcher_mod": + ToolsUIModel.rsiEnhance(context); + break; + case "advanced": + context.push("/index/advanced_localization"); + break; + } + } + : null, child: Container( decoration: BoxDecoration( color: FluentTheme.of(context).cardColor, diff --git a/lib/ui/home/localization/localization_ui_model.dart b/lib/ui/home/localization/localization_ui_model.dart index bcb82f0..0ac6d15 100644 --- a/lib/ui/home/localization/localization_ui_model.dart +++ b/lib/ui/home/localization/localization_ui_model.dart @@ -17,7 +17,6 @@ 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/provider/unp4kc.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'; @@ -48,6 +47,8 @@ class LocalizationUIModel extends _$LocalizationUIModel { Directory get _downloadDir => Directory("${appGlobalState.applicationSupportDir}\\Localizations"); + Directory getDownloadDir() => _downloadDir; + Directory get _scDataDir => Directory("${ref.read(homeUIModelProvider).scInstalledPath}\\data"); @@ -79,17 +80,6 @@ class LocalizationUIModel extends _$LocalizationUIModel { await _loadData(); } - readEnglishInI() async { - final data = await Unp4kCModel.unp4kTools( - appGlobalState.applicationBinaryModuleDir!, [ - "extract_memory", - "$_scInstallPath\\Data.p4k", - "Data\\Localization\\english\\global.ini" - ]); - final iniData = String.fromCharCodes(data); - dPrint("read english ini => ${iniData.length}"); - } - final Map> _allVersionLocalizationData = {}; @@ -268,27 +258,20 @@ class LocalizationUIModel extends _$LocalizationUIModel { BuildContext context, ScLocalizationData value) { return () async { AnalyticsApi.touch("install_localization"); - final downloadUrl = - "${URLConf.gitlabLocalizationUrl}/archive/${value.versionName}.tar.gz"; + final savePath = File("${_downloadDir.absolute.path}\\${value.versionName}.sclang"); try { state = state.copyWith(workingVersion: value.versionName!); if (!await savePath.exists()) { // download - dPrint("downloading file to $savePath"); - final r = await RSHttp.get(downloadUrl); - if (r.statusCode == 200 && r.data != null) { - await savePath.writeAsBytes(r.data!); - } else { - throw "statusCode Error : ${r.statusCode}"; - } + await downloadLocalizationFile(savePath, value); } else { dPrint("use cache $savePath"); } await Future.delayed(const Duration(milliseconds: 300)); // check file - final globalIni = await compute(_readArchive, savePath.absolute.path); + final globalIni = await compute(readArchive, savePath.absolute.path); if (globalIni.isEmpty) { throw S.current.localization_info_corrupted_file; } @@ -303,7 +286,20 @@ class LocalizationUIModel extends _$LocalizationUIModel { }; } - static StringBuffer _readArchive(String savePath) { + Future downloadLocalizationFile( + File savePath, ScLocalizationData value) async { + dPrint("downloading file to $savePath"); + final downloadUrl = + "${URLConf.gitlabLocalizationUrl}/archive/${value.versionName}.tar.gz"; + final r = await RSHttp.get(downloadUrl); + if (r.statusCode == 200 && r.data != null) { + await savePath.writeAsBytes(r.data!); + } else { + throw "statusCode Error : ${r.statusCode}"; + } + } + + static StringBuffer readArchive(String savePath) { final inputStream = InputFileStream(savePath); final archive = TarDecoder().decodeBytes(GZipDecoder().decodeBuffer(inputStream)); diff --git a/lib/ui/home/localization/localization_ui_model.g.dart b/lib/ui/home/localization/localization_ui_model.g.dart index 4cf6163..f194d40 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'a4e33b337db587ad3e766450d9bd8ac4aa7b4c49'; + r'ed47da78fdc6adac904a17909f111640ac84563e'; /// See also [LocalizationUIModel]. @ProviderFor(LocalizationUIModel) diff --git a/lib/ui/tools/tools_ui_model.g.dart b/lib/ui/tools/tools_ui_model.g.dart index 59665ce..42cc88e 100644 --- a/lib/ui/tools/tools_ui_model.g.dart +++ b/lib/ui/tools/tools_ui_model.g.dart @@ -6,7 +6,7 @@ part of 'tools_ui_model.dart'; // RiverpodGenerator // ************************************************************************** -String _$toolsUIModelHash() => r'6cf170210fa7a7c2c63bde9d0920c64a20a81263'; +String _$toolsUIModelHash() => r'4c27a3df07cb000ac58b74b3da48d926d0b01ea8'; /// See also [ToolsUIModel]. @ProviderFor(ToolsUIModel)