feat: AdvancedLocalization Init

This commit is contained in:
xkeyC 2024-05-03 22:35:31 +08:00
parent 4e506a11a0
commit 9a875fc5a5
14 changed files with 1175 additions and 38 deletions

View File

@ -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_.*"
]
}
]
}

View File

@ -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: [

View File

@ -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<GoRouter>.internal(
);
typedef RouterRef = AutoDisposeProviderRef<GoRouter>;
String _$appGlobalModelHash() => r'11a172d5fb19ad6566f1025354c8f8d91fe85a84';
String _$appGlobalModelHash() => r'9dccbb898714695ef8b640a5b5dfb361783a0f45';
/// See also [AppGlobalModel].
@ProviderFor(AppGlobalModel)

View File

@ -11,6 +11,7 @@ File? _logFile;
void dPrint(src) async {
if (kDebugMode) {
print(src);
return;
}
await _logLock.synchronized(() async {
try {

View File

@ -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<AppAdvancedLocalizationClassKeysData>? classKeys;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
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>() : [];
}
String? id;
String? className;
List<String>? keys;
Map<String, String> valuesMap = {};
AppAdvancedLocalizationClassKeysDataMode mode =
AppAdvancedLocalizationClassKeysDataMode.localization;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = id;
map['class_name'] = className;
map['keys'] = keys;
return map;
}
}
enum AppAdvancedLocalizationClassKeysDataMode {
unLocalization,
localization,
mixed,
}

View File

@ -6,7 +6,7 @@ part of 'unp4kc.dart';
// RiverpodGenerator
// **************************************************************************
String _$unp4kCModelHash() => r'69117c5857797683e2d080da9238bfecc5948898';
String _$unp4kCModelHash() => r'3b2325c72dcb47bf9207625b0a1d2a611f911b6f';
/// See also [Unp4kCModel].
@ProviderFor(Unp4kCModel)

View File

@ -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,
);
}
}

View File

@ -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<String, AppAdvancedLocalizationClassKeysData>? 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<void> _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<String, AppAdvancedLocalizationClassKeysData> _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 = <String, AppAdvancedLocalizationClassKeysData>{
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<String, String> readIniAsMap(String iniString) {
final iniMap = <String, String>{};
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<AppAdvancedLocalizationData> _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<String> 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;
}
}

View File

@ -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>(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<String, AppAdvancedLocalizationClassKeysData>? get classMap =>
throw _privateConstructorUsedError;
String? get p4kGlobalIni => throw _privateConstructorUsedError;
String? get serverGlobalIni => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$AdvancedLocalizationUIStateCopyWith<AdvancedLocalizationUIState>
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<String, AppAdvancedLocalizationClassKeysData>? 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<String, AppAdvancedLocalizationClassKeysData>?,
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<String, AppAdvancedLocalizationClassKeysData>? 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<String, AppAdvancedLocalizationClassKeysData>?,
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<String, AppAdvancedLocalizationClassKeysData>? classMap,
this.p4kGlobalIni,
this.serverGlobalIni})
: _classMap = classMap;
@override
@JsonKey()
final String workingText;
final Map<String, AppAdvancedLocalizationClassKeysData>? _classMap;
@override
Map<String, AppAdvancedLocalizationClassKeysData>? 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<String, AppAdvancedLocalizationClassKeysData>? classMap,
final String? p4kGlobalIni,
final String? serverGlobalIni}) = _$AdvancedLocalizationUIStateImpl;
@override
String get workingText;
@override
Map<String, AppAdvancedLocalizationClassKeysData>? get classMap;
@override
String? get p4kGlobalIni;
@override
String? get serverGlobalIni;
@override
@JsonKey(ignore: true)
_$$AdvancedLocalizationUIStateImplCopyWith<_$AdvancedLocalizationUIStateImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@ -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<AdvancedLocalizationUIState>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -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,

View File

@ -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<String, Map<String, ScLocalizationData>>
_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<void> 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));

View File

@ -7,7 +7,7 @@ part of 'localization_ui_model.dart';
// **************************************************************************
String _$localizationUIModelHash() =>
r'a4e33b337db587ad3e766450d9bd8ac4aa7b4c49';
r'ed47da78fdc6adac904a17909f111640ac84563e';
/// See also [LocalizationUIModel].
@ProviderFor(LocalizationUIModel)

View File

@ -6,7 +6,7 @@ part of 'tools_ui_model.dart';
// RiverpodGenerator
// **************************************************************************
String _$toolsUIModelHash() => r'6cf170210fa7a7c2c63bde9d0920c64a20a81263';
String _$toolsUIModelHash() => r'4c27a3df07cb000ac58b74b3da48d926d0b01ea8';
/// See also [ToolsUIModel].
@ProviderFor(ToolsUIModel)