feat: 高级汉化

This commit is contained in:
xkeyC 2024-05-05 14:59:07 +08:00
parent 0bb0f7deb4
commit f392463a84
12 changed files with 649 additions and 254 deletions

View File

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

View File

@ -42,6 +42,8 @@ class AppAdvancedLocalizationClassKeysData {
Map<String, String> valuesMap = {};
AppAdvancedLocalizationClassKeysDataMode mode =
AppAdvancedLocalizationClassKeysDataMode.localization;
bool lockMod = false;
bool isWorking = false;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
@ -56,4 +58,5 @@ enum AppAdvancedLocalizationClassKeysDataMode {
unLocalization,
localization,
mixed,
mixedNewline,
}

View File

@ -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,7 +86,10 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
IconButton(
onPressed:
item.isWorking ? null : () => _showContent(context, item),
icon: Padding(
padding: const EdgeInsets.only(left: 12, right: 12),
child: Row(
children: [
@ -57,6 +98,7 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
"${item.className}",
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.bold),
textAlign: TextAlign.start,
)),
Text(
"${item.valuesMap.length}",
@ -65,17 +107,58 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
color: Colors.white.withOpacity(.6),
),
),
const SizedBox(width: 6),
Icon(
FluentIcons.chevron_right,
color: Colors.white.withOpacity(.6),
size: 16,
),
],
),
),
),
Container(
margin: const EdgeInsets.only(top: 6, bottom: 12),
width: MediaQuery.of(context).size.width,
height: 1,
color: Colors.white.withOpacity(.1),
),
if (item.isWorking)
Column(
children: [
makeLoading(context),
const SizedBox(height: 6),
const Text("正在重新生成文本..."),
],
)
else ...[
Padding(
padding: const EdgeInsets.only(left: 12, right: 12),
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: 160,
height: 180,
child: SuperListView.builder(
itemCount: item.valuesMap.length,
padding: const EdgeInsets.only(left: 12, right: 12),
@ -93,10 +176,80 @@ 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,
),
),
),
),
);
},
);
},
);
}
}

View File

@ -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<String, AppAdvancedLocalizationClassKeysData>? classMap,
String? p4kGlobalIni,
String? serverGlobalIni,
ScLocalizationData? apiLocalizationData,
@Default(0) int p4kGlobalIniLines,
@Default(0) int serverGlobalIniLines,
}) = _AdvancedLocalizationUIState;
}
extension AdvancedLocalizationUIStateEx on AdvancedLocalizationUIState {
Map<AppAdvancedLocalizationClassKeysDataMode, String> 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 = <String, AppAdvancedLocalizationClassKeysData>{
@ -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<String> 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<String, AppAdvancedLocalizationClassKeysData>.from(state.classMap!);
classMap[item.id!] = item;
state = state.copyWith(classMap: classMap);
final p4kIniMap = readIniAsMap(state.p4kGlobalIni!);
final serverIniMap = readIniAsMap(state.serverGlobalIni!);
final newValuesMap = <String, String>{};
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<bool> 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;
}
}

View File

@ -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<AdvancedLocalizationUIState>
@ -39,7 +43,10 @@ abstract class $AdvancedLocalizationUIStateCopyWith<$Res> {
{String workingText,
Map<String, AppAdvancedLocalizationClassKeysData>? 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<String, AppAdvancedLocalizationClassKeysData>? 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<String, AppAdvancedLocalizationClassKeysData>? 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<String, AppAdvancedLocalizationClassKeysData>? 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;

View File

@ -7,7 +7,7 @@ part of 'advanced_localization_ui_model.dart';
// **************************************************************************
String _$advancedLocalizationUIModelHash() =>
r'2bb7dfc3cd8d45ecf5056083627136ad5cb7a285';
r'55c9d02d4c78112b2772f789c2758c3ea88808cb';
/// See also [AdvancedLocalizationUIModel].
@ProviderFor(AdvancedLocalizationUIModel)

View File

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

View File

@ -32,6 +32,7 @@ class LocalizationUIState with _$LocalizationUIState {
Map<String, ScLocalizationData>? apiLocalizationData,
@Default("") String workingVersion,
MapEntry<bool, String>? patchStatus,
bool? isInstalledAdvanced,
List<String>? 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<bool> _checkAdvancedStatus(String path) async {
final iniFile = File(path);
if (!await iniFile.exists()) {
return false;
}
final iniString = (await iniFile.readAsString());
return iniString.contains("_starcitizen_doctor_localization_advanced=true");
}
Future<bool> _getLangCfgEnableLang({String lang = ""}) async {

View File

@ -21,6 +21,7 @@ mixin _$LocalizationUIState {
throw _privateConstructorUsedError;
String get workingVersion => throw _privateConstructorUsedError;
MapEntry<bool, String>? get patchStatus => throw _privateConstructorUsedError;
bool? get isInstalledAdvanced => throw _privateConstructorUsedError;
List<String>? get customizeList => throw _privateConstructorUsedError;
bool get enableCustomize => throw _privateConstructorUsedError;
@ -40,6 +41,7 @@ abstract class $LocalizationUIStateCopyWith<$Res> {
Map<String, ScLocalizationData>? apiLocalizationData,
String workingVersion,
MapEntry<bool, String>? patchStatus,
bool? isInstalledAdvanced,
List<String>? 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<bool, String>?,
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<String, ScLocalizationData>? apiLocalizationData,
String workingVersion,
MapEntry<bool, String>? patchStatus,
bool? isInstalledAdvanced,
List<String>? 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<bool, String>?,
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<String, ScLocalizationData>? apiLocalizationData,
this.workingVersion = "",
this.patchStatus,
this.isInstalledAdvanced,
final List<String>? customizeList,
this.enableCustomize = false})
: _apiLocalizationData = apiLocalizationData,
@ -188,6 +202,8 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState {
final String workingVersion;
@override
final MapEntry<bool, String>? patchStatus;
@override
final bool? isInstalledAdvanced;
final List<String>? _customizeList;
@override
List<String>? 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<String, ScLocalizationData>? apiLocalizationData,
final String workingVersion,
final MapEntry<bool, String>? patchStatus,
final bool? isInstalledAdvanced,
final List<String>? customizeList,
final bool enableCustomize}) = _$LocalizationUIStateImpl;
@ -262,6 +282,8 @@ abstract class _LocalizationUIState implements LocalizationUIState {
@override
MapEntry<bool, String>? get patchStatus;
@override
bool? get isInstalledAdvanced;
@override
List<String>? get customizeList;
@override
bool get enableCustomize;

View File

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

View File

@ -267,6 +267,7 @@ class _TextTempWidget extends HookConsumerWidget {
return CodeEditor(
controller: CodeLineEditingController.fromText('${textData.value}'),
readOnly: true,
);
}
}

View File

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