mirror of
https://mirror.ghproxy.com/https://github.com/StarCitizenToolBox/app.git
synced 2024-12-22 23:23:43 +08:00
feat: unp4kc
This commit is contained in:
parent
dd17ddc92a
commit
90bb8e6611
BIN
assets/binary/unp4kc.zip
Normal file
BIN
assets/binary/unp4kc.zip
Normal file
Binary file not shown.
@ -29,6 +29,7 @@ import 'ui/home/downloader/home_downloader_ui.dart';
|
|||||||
import 'ui/home/game_doctor/game_doctor_ui.dart';
|
import 'ui/home/game_doctor/game_doctor_ui.dart';
|
||||||
import 'ui/index_ui.dart';
|
import 'ui/index_ui.dart';
|
||||||
import 'ui/settings/upgrade_dialog.dart';
|
import 'ui/settings/upgrade_dialog.dart';
|
||||||
|
import 'ui/tools/unp4kc/unp4kc_ui.dart';
|
||||||
|
|
||||||
part 'app.g.dart';
|
part 'app.g.dart';
|
||||||
|
|
||||||
@ -77,6 +78,13 @@ GoRouter router(RouterRef ref) {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
GoRoute(path: '/tools', builder: (_, __) => const SizedBox(), routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'unp4kc',
|
||||||
|
pageBuilder: (context, state) =>
|
||||||
|
myPageBuilder(context, state, const UnP4kcUI()),
|
||||||
|
),
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ part of 'app.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$routerHash() => r'e7b1e3a9fd74b4f00e3d71017615d7fb82bd649d';
|
String _$routerHash() => r'7ce5ef6a7a4f6f604a457dd050e04ee594c4760a';
|
||||||
|
|
||||||
/// See also [router].
|
/// See also [router].
|
||||||
@ProviderFor(router)
|
@ProviderFor(router)
|
||||||
@ -20,7 +20,7 @@ final routerProvider = AutoDisposeProvider<GoRouter>.internal(
|
|||||||
);
|
);
|
||||||
|
|
||||||
typedef RouterRef = AutoDisposeProviderRef<GoRouter>;
|
typedef RouterRef = AutoDisposeProviderRef<GoRouter>;
|
||||||
String _$appGlobalModelHash() => r'ae7f6704a80297ac3e0f70412c676a1829046831';
|
String _$appGlobalModelHash() => r'a604c415d2d855ede8727dd75ef991ab2afcf234';
|
||||||
|
|
||||||
/// See also [AppGlobalModel].
|
/// See also [AppGlobalModel].
|
||||||
@ProviderFor(AppGlobalModel)
|
@ProviderFor(AppGlobalModel)
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:archive/archive.dart';
|
import 'package:archive/archive.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||||
|
|
||||||
class BinaryModuleConf {
|
class BinaryModuleConf {
|
||||||
static const _modules = {
|
static const _modules = {
|
||||||
"aria2c": "0",
|
"aria2c": "0",
|
||||||
|
"unp4kc": "0",
|
||||||
};
|
};
|
||||||
|
|
||||||
static Future extractModule(List<String> modules, String workingDir) async {
|
static Future extractModule(List<String> modules, String workingDir) async {
|
||||||
@ -16,7 +18,8 @@ class BinaryModuleConf {
|
|||||||
final version = m.value;
|
final version = m.value;
|
||||||
final dir = "$workingDir\\$name";
|
final dir = "$workingDir\\$name";
|
||||||
final versionFile = File("$dir\\version");
|
final versionFile = File("$dir\\version");
|
||||||
if (await versionFile.exists() &&
|
if (kReleaseMode &&
|
||||||
|
await versionFile.exists() &&
|
||||||
(await versionFile.readAsString()).trim() == version) {
|
(await versionFile.readAsString()).trim() == version) {
|
||||||
dPrint(
|
dPrint(
|
||||||
"BinaryModuleConf.extractModule skip $name version == $version");
|
"BinaryModuleConf.extractModule skip $name version == $version");
|
||||||
|
60
lib/data/app_unp4k_p4k_item_data.dart
Normal file
60
lib/data/app_unp4k_p4k_item_data.dart
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/// name : "Data\\Textures\\planets\\surface\\ground\\architecture\\city\\city_suburbs_02_displ.dds.6"
|
||||||
|
/// size : 524288
|
||||||
|
/// compressedSize : 169812
|
||||||
|
/// isDirectory : false
|
||||||
|
/// isFile : true
|
||||||
|
/// isEncrypted : false
|
||||||
|
/// isUnicodeText : false
|
||||||
|
/// dateTime : "2019-12-16T15:11:18"
|
||||||
|
/// version : 45
|
||||||
|
|
||||||
|
class AppUnp4kP4kItemData {
|
||||||
|
AppUnp4kP4kItemData({
|
||||||
|
this.name,
|
||||||
|
this.size,
|
||||||
|
this.compressedSize,
|
||||||
|
this.isDirectory,
|
||||||
|
this.isFile,
|
||||||
|
this.isEncrypted,
|
||||||
|
this.isUnicodeText,
|
||||||
|
this.dateTime,
|
||||||
|
this.version,
|
||||||
|
});
|
||||||
|
|
||||||
|
AppUnp4kP4kItemData.fromJson(dynamic json) {
|
||||||
|
name = json['name'];
|
||||||
|
size = json['size'];
|
||||||
|
compressedSize = json['compressedSize'];
|
||||||
|
isDirectory = json['isDirectory'];
|
||||||
|
isFile = json['isFile'];
|
||||||
|
isEncrypted = json['isEncrypted'];
|
||||||
|
isUnicodeText = json['isUnicodeText'];
|
||||||
|
dateTime = json['dateTime'];
|
||||||
|
version = json['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
String? name;
|
||||||
|
num? size;
|
||||||
|
num? compressedSize;
|
||||||
|
bool? isDirectory;
|
||||||
|
bool? isFile;
|
||||||
|
bool? isEncrypted;
|
||||||
|
bool? isUnicodeText;
|
||||||
|
String? dateTime;
|
||||||
|
num? version;
|
||||||
|
List<AppUnp4kP4kItemData> children = [];
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final map = <String, dynamic>{};
|
||||||
|
map['name'] = name;
|
||||||
|
map['size'] = size;
|
||||||
|
map['compressedSize'] = compressedSize;
|
||||||
|
map['isDirectory'] = isDirectory;
|
||||||
|
map['isFile'] = isFile;
|
||||||
|
map['isEncrypted'] = isEncrypted;
|
||||||
|
map['isUnicodeText'] = isUnicodeText;
|
||||||
|
map['dateTime'] = dateTime;
|
||||||
|
map['version'] = version;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ part of 'aria2c.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$aria2cModelHash() => r'6685f6a716016113487de190a61f71196094526e';
|
String _$aria2cModelHash() => r'5431c2d9667f17ff03d0794711af22b015feda0d';
|
||||||
|
|
||||||
/// See also [Aria2cModel].
|
/// See also [Aria2cModel].
|
||||||
@ProviderFor(Aria2cModel)
|
@ProviderFor(Aria2cModel)
|
||||||
|
191
lib/provider/unp4kc.dart
Normal file
191
lib/provider/unp4kc.dart
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:file/memory.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/conf/binary_conf.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||||
|
import 'package:starcitizen_doctor/data/app_unp4k_p4k_item_data.dart';
|
||||||
|
import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart';
|
||||||
|
|
||||||
|
part 'unp4kc.freezed.dart';
|
||||||
|
|
||||||
|
part 'unp4kc.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class Unp4kcState with _$Unp4kcState {
|
||||||
|
const factory Unp4kcState({
|
||||||
|
required bool startUp,
|
||||||
|
Map<String, AppUnp4kP4kItemData>? files,
|
||||||
|
MemoryFileSystem? fs,
|
||||||
|
required String curPath,
|
||||||
|
String? endMessage,
|
||||||
|
MapEntry<String, String>? tempOpenFile,
|
||||||
|
}) = _Unp4kcState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
class Unp4kCModel extends _$Unp4kCModel {
|
||||||
|
Process? _process;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Unp4kcState build() {
|
||||||
|
state =
|
||||||
|
const Unp4kcState(startUp: false, curPath: '\\', endMessage: "初始化中...");
|
||||||
|
_init();
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolsUIState get _toolsState => ref.read(toolsUIModelProvider);
|
||||||
|
|
||||||
|
String getGamePath() => _toolsState.scInstalledPath;
|
||||||
|
|
||||||
|
void _init() async {
|
||||||
|
final execDir = "${appGlobalState.applicationBinaryModuleDir}\\unp4kc";
|
||||||
|
await BinaryModuleConf.extractModule(
|
||||||
|
["unp4kc"], appGlobalState.applicationBinaryModuleDir!);
|
||||||
|
final exec = "$execDir\\unp4kc.exe";
|
||||||
|
final ps = await Process.start(exec, []);
|
||||||
|
StringBuffer stringBuffer = StringBuffer();
|
||||||
|
_process = ps;
|
||||||
|
ps.stdout.listen((event) async {
|
||||||
|
final eventStr = String.fromCharCodes(event);
|
||||||
|
stringBuffer.write(eventStr);
|
||||||
|
if (!eventStr.endsWith("\n")) return;
|
||||||
|
final str = stringBuffer.toString().trim();
|
||||||
|
stringBuffer.clear();
|
||||||
|
try {
|
||||||
|
final eventJson = await compute(json.decode, str);
|
||||||
|
_handleMessage(eventJson, ps);
|
||||||
|
} catch (e) {
|
||||||
|
dPrint("[unp4kc] json error: $e");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ps.stderr.listen((event) {
|
||||||
|
final eventStr = String.fromCharCodes(event);
|
||||||
|
dPrint("[unp4kc] stderr: $eventStr");
|
||||||
|
});
|
||||||
|
state = state.copyWith(startUp: true);
|
||||||
|
ref.onDispose(() {
|
||||||
|
ps.kill();
|
||||||
|
dPrint("[unp4kc] kill ...");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleMessage(Map<String, dynamic> eventJson, Process ps) async {
|
||||||
|
final action = eventJson["action"];
|
||||||
|
final data = eventJson["data"];
|
||||||
|
final gamePath = getGamePath();
|
||||||
|
final gameP4kPath = "$gamePath\\Data.p4k";
|
||||||
|
switch (action.toString().trim()) {
|
||||||
|
case "info: startup":
|
||||||
|
ps.stdin.writeln(gameP4kPath);
|
||||||
|
state = state.copyWith(endMessage: "正在读取P4K 文件 ...");
|
||||||
|
break;
|
||||||
|
case "data: P4K_Files":
|
||||||
|
final p4kFiles = (data as List<dynamic>);
|
||||||
|
final files = <String, AppUnp4kP4kItemData>{};
|
||||||
|
final fs = MemoryFileSystem(style: FileSystemStyle.posix);
|
||||||
|
state = state.copyWith(endMessage: "正在处理文件 ...");
|
||||||
|
for (var i = 0; i < p4kFiles.length; i++) {
|
||||||
|
final item = AppUnp4kP4kItemData.fromJson(p4kFiles[i]);
|
||||||
|
item.name = "${item.name}";
|
||||||
|
files["\\${item.name}"] = item;
|
||||||
|
await fs
|
||||||
|
.file(item.name?.replaceAll("\\", "/") ?? "")
|
||||||
|
.create(recursive: true);
|
||||||
|
}
|
||||||
|
state = state.copyWith(
|
||||||
|
files: files, fs: fs, endMessage: "加载完毕:${files.length} 个文件");
|
||||||
|
break;
|
||||||
|
case "info: Extracted_Open":
|
||||||
|
final filePath = data.toString();
|
||||||
|
dPrint("[unp4kc] Extracted_Open file: $filePath");
|
||||||
|
const textExt = [".txt", ".xml", ".json", ".lua", ".cfg", ".ini"];
|
||||||
|
const imgExt = [".png"];
|
||||||
|
String openType = "unknown";
|
||||||
|
for (var element in textExt) {
|
||||||
|
if (filePath.endsWith(element)) {
|
||||||
|
openType = "text";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var element in imgExt) {
|
||||||
|
if (filePath.endsWith(element)) {
|
||||||
|
openType = "image";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = state.copyWith(
|
||||||
|
tempOpenFile: MapEntry(openType, filePath),
|
||||||
|
endMessage: "打开文件:$filePath");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dPrint("[unp4kc] unknown action: $action");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AppUnp4kP4kItemData>? getFiles() {
|
||||||
|
final path = state.curPath.replaceAll("\\", "/");
|
||||||
|
final fs = state.fs;
|
||||||
|
if (fs == null) return null;
|
||||||
|
final dir = fs.directory(path);
|
||||||
|
if (!dir.existsSync()) return [];
|
||||||
|
final files = dir.listSync(recursive: false, followLinks: false);
|
||||||
|
files.sort((a, b) {
|
||||||
|
if (a is Directory && b is File) {
|
||||||
|
return -1;
|
||||||
|
} else if (a is File && b is Directory) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return a.path.compareTo(b.path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
final result = <AppUnp4kP4kItemData>[];
|
||||||
|
for (var file in files) {
|
||||||
|
if (file is File) {
|
||||||
|
final f = state.files?[file.path.replaceAll("/", "\\")];
|
||||||
|
if (f != null) {
|
||||||
|
if (!(f.name?.startsWith("\\") ?? true)) {
|
||||||
|
f.name = "\\${f.name}";
|
||||||
|
}
|
||||||
|
result.add(f);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.add(AppUnp4kP4kItemData(
|
||||||
|
name: file.path.replaceAll("/", "\\"), isDirectory: true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeDir(String name, {bool fullPath = false}) {
|
||||||
|
if (fullPath) {
|
||||||
|
state = state.copyWith(curPath: name);
|
||||||
|
} else {
|
||||||
|
state = state.copyWith(curPath: "${state.curPath}$name\\");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openFile(String filePath) async {
|
||||||
|
final tempPath = "${appGlobalState.applicationSupportDir}\\temp\\unp4k\\";
|
||||||
|
state = state.copyWith(
|
||||||
|
tempOpenFile: const MapEntry("loading", ""),
|
||||||
|
endMessage: "读取文件:$filePath ...");
|
||||||
|
extractFile(filePath, tempPath, mode: "extract_open");
|
||||||
|
}
|
||||||
|
|
||||||
|
extractFile(String filePath, String outputPath,
|
||||||
|
{String mode = "extract"}) async {
|
||||||
|
// remove first \\
|
||||||
|
if (filePath.startsWith("\\")) {
|
||||||
|
filePath = filePath.substring(1);
|
||||||
|
}
|
||||||
|
outputPath = "$outputPath$filePath";
|
||||||
|
dPrint("extractFile .... $filePath");
|
||||||
|
_process?.stdin.writeln("$mode<:,:>$filePath<:,:>$outputPath");
|
||||||
|
}
|
||||||
|
}
|
269
lib/provider/unp4kc.freezed.dart
Normal file
269
lib/provider/unp4kc.freezed.dart
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
// 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 'unp4kc.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 _$Unp4kcState {
|
||||||
|
bool get startUp => throw _privateConstructorUsedError;
|
||||||
|
Map<String, AppUnp4kP4kItemData>? get files =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
MemoryFileSystem? get fs => throw _privateConstructorUsedError;
|
||||||
|
String get curPath => throw _privateConstructorUsedError;
|
||||||
|
String? get endMessage => throw _privateConstructorUsedError;
|
||||||
|
MapEntry<String, String>? get tempOpenFile =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$Unp4kcStateCopyWith<Unp4kcState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $Unp4kcStateCopyWith<$Res> {
|
||||||
|
factory $Unp4kcStateCopyWith(
|
||||||
|
Unp4kcState value, $Res Function(Unp4kcState) then) =
|
||||||
|
_$Unp4kcStateCopyWithImpl<$Res, Unp4kcState>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{bool startUp,
|
||||||
|
Map<String, AppUnp4kP4kItemData>? files,
|
||||||
|
MemoryFileSystem? fs,
|
||||||
|
String curPath,
|
||||||
|
String? endMessage,
|
||||||
|
MapEntry<String, String>? tempOpenFile});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$Unp4kcStateCopyWithImpl<$Res, $Val extends Unp4kcState>
|
||||||
|
implements $Unp4kcStateCopyWith<$Res> {
|
||||||
|
_$Unp4kcStateCopyWithImpl(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? startUp = null,
|
||||||
|
Object? files = freezed,
|
||||||
|
Object? fs = freezed,
|
||||||
|
Object? curPath = null,
|
||||||
|
Object? endMessage = freezed,
|
||||||
|
Object? tempOpenFile = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
startUp: null == startUp
|
||||||
|
? _value.startUp
|
||||||
|
: startUp // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
files: freezed == files
|
||||||
|
? _value.files
|
||||||
|
: files // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, AppUnp4kP4kItemData>?,
|
||||||
|
fs: freezed == fs
|
||||||
|
? _value.fs
|
||||||
|
: fs // ignore: cast_nullable_to_non_nullable
|
||||||
|
as MemoryFileSystem?,
|
||||||
|
curPath: null == curPath
|
||||||
|
? _value.curPath
|
||||||
|
: curPath // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
endMessage: freezed == endMessage
|
||||||
|
? _value.endMessage
|
||||||
|
: endMessage // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
tempOpenFile: freezed == tempOpenFile
|
||||||
|
? _value.tempOpenFile
|
||||||
|
: tempOpenFile // ignore: cast_nullable_to_non_nullable
|
||||||
|
as MapEntry<String, String>?,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$Unp4kcStateImplCopyWith<$Res>
|
||||||
|
implements $Unp4kcStateCopyWith<$Res> {
|
||||||
|
factory _$$Unp4kcStateImplCopyWith(
|
||||||
|
_$Unp4kcStateImpl value, $Res Function(_$Unp4kcStateImpl) then) =
|
||||||
|
__$$Unp4kcStateImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{bool startUp,
|
||||||
|
Map<String, AppUnp4kP4kItemData>? files,
|
||||||
|
MemoryFileSystem? fs,
|
||||||
|
String curPath,
|
||||||
|
String? endMessage,
|
||||||
|
MapEntry<String, String>? tempOpenFile});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$Unp4kcStateImplCopyWithImpl<$Res>
|
||||||
|
extends _$Unp4kcStateCopyWithImpl<$Res, _$Unp4kcStateImpl>
|
||||||
|
implements _$$Unp4kcStateImplCopyWith<$Res> {
|
||||||
|
__$$Unp4kcStateImplCopyWithImpl(
|
||||||
|
_$Unp4kcStateImpl _value, $Res Function(_$Unp4kcStateImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? startUp = null,
|
||||||
|
Object? files = freezed,
|
||||||
|
Object? fs = freezed,
|
||||||
|
Object? curPath = null,
|
||||||
|
Object? endMessage = freezed,
|
||||||
|
Object? tempOpenFile = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$Unp4kcStateImpl(
|
||||||
|
startUp: null == startUp
|
||||||
|
? _value.startUp
|
||||||
|
: startUp // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
files: freezed == files
|
||||||
|
? _value._files
|
||||||
|
: files // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, AppUnp4kP4kItemData>?,
|
||||||
|
fs: freezed == fs
|
||||||
|
? _value.fs
|
||||||
|
: fs // ignore: cast_nullable_to_non_nullable
|
||||||
|
as MemoryFileSystem?,
|
||||||
|
curPath: null == curPath
|
||||||
|
? _value.curPath
|
||||||
|
: curPath // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
endMessage: freezed == endMessage
|
||||||
|
? _value.endMessage
|
||||||
|
: endMessage // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
tempOpenFile: freezed == tempOpenFile
|
||||||
|
? _value.tempOpenFile
|
||||||
|
: tempOpenFile // ignore: cast_nullable_to_non_nullable
|
||||||
|
as MapEntry<String, String>?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$Unp4kcStateImpl with DiagnosticableTreeMixin implements _Unp4kcState {
|
||||||
|
const _$Unp4kcStateImpl(
|
||||||
|
{required this.startUp,
|
||||||
|
final Map<String, AppUnp4kP4kItemData>? files,
|
||||||
|
this.fs,
|
||||||
|
required this.curPath,
|
||||||
|
this.endMessage,
|
||||||
|
this.tempOpenFile})
|
||||||
|
: _files = files;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool startUp;
|
||||||
|
final Map<String, AppUnp4kP4kItemData>? _files;
|
||||||
|
@override
|
||||||
|
Map<String, AppUnp4kP4kItemData>? get files {
|
||||||
|
final value = _files;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_files is EqualUnmodifiableMapView) return _files;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final MemoryFileSystem? fs;
|
||||||
|
@override
|
||||||
|
final String curPath;
|
||||||
|
@override
|
||||||
|
final String? endMessage;
|
||||||
|
@override
|
||||||
|
final MapEntry<String, String>? tempOpenFile;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||||
|
return 'Unp4kcState(startUp: $startUp, files: $files, fs: $fs, curPath: $curPath, endMessage: $endMessage, tempOpenFile: $tempOpenFile)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties
|
||||||
|
..add(DiagnosticsProperty('type', 'Unp4kcState'))
|
||||||
|
..add(DiagnosticsProperty('startUp', startUp))
|
||||||
|
..add(DiagnosticsProperty('files', files))
|
||||||
|
..add(DiagnosticsProperty('fs', fs))
|
||||||
|
..add(DiagnosticsProperty('curPath', curPath))
|
||||||
|
..add(DiagnosticsProperty('endMessage', endMessage))
|
||||||
|
..add(DiagnosticsProperty('tempOpenFile', tempOpenFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$Unp4kcStateImpl &&
|
||||||
|
(identical(other.startUp, startUp) || other.startUp == startUp) &&
|
||||||
|
const DeepCollectionEquality().equals(other._files, _files) &&
|
||||||
|
(identical(other.fs, fs) || other.fs == fs) &&
|
||||||
|
(identical(other.curPath, curPath) || other.curPath == curPath) &&
|
||||||
|
(identical(other.endMessage, endMessage) ||
|
||||||
|
other.endMessage == endMessage) &&
|
||||||
|
(identical(other.tempOpenFile, tempOpenFile) ||
|
||||||
|
other.tempOpenFile == tempOpenFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
startUp,
|
||||||
|
const DeepCollectionEquality().hash(_files),
|
||||||
|
fs,
|
||||||
|
curPath,
|
||||||
|
endMessage,
|
||||||
|
tempOpenFile);
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$Unp4kcStateImplCopyWith<_$Unp4kcStateImpl> get copyWith =>
|
||||||
|
__$$Unp4kcStateImplCopyWithImpl<_$Unp4kcStateImpl>(this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Unp4kcState implements Unp4kcState {
|
||||||
|
const factory _Unp4kcState(
|
||||||
|
{required final bool startUp,
|
||||||
|
final Map<String, AppUnp4kP4kItemData>? files,
|
||||||
|
final MemoryFileSystem? fs,
|
||||||
|
required final String curPath,
|
||||||
|
final String? endMessage,
|
||||||
|
final MapEntry<String, String>? tempOpenFile}) = _$Unp4kcStateImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get startUp;
|
||||||
|
@override
|
||||||
|
Map<String, AppUnp4kP4kItemData>? get files;
|
||||||
|
@override
|
||||||
|
MemoryFileSystem? get fs;
|
||||||
|
@override
|
||||||
|
String get curPath;
|
||||||
|
@override
|
||||||
|
String? get endMessage;
|
||||||
|
@override
|
||||||
|
MapEntry<String, String>? get tempOpenFile;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$Unp4kcStateImplCopyWith<_$Unp4kcStateImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
25
lib/provider/unp4kc.g.dart
Normal file
25
lib/provider/unp4kc.g.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'unp4kc.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$unp4kCModelHash() => r'46b6ac12670a6ff6ffb9d5bc8a8d04c07c570a8c';
|
||||||
|
|
||||||
|
/// See also [Unp4kCModel].
|
||||||
|
@ProviderFor(Unp4kCModel)
|
||||||
|
final unp4kCModelProvider =
|
||||||
|
AutoDisposeNotifierProvider<Unp4kCModel, Unp4kcState>.internal(
|
||||||
|
Unp4kCModel.new,
|
||||||
|
name: r'unp4kCModelProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product') ? null : _$unp4kCModelHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef _$Unp4kCModel = AutoDisposeNotifier<Unp4kcState>;
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -7,7 +7,7 @@ part of 'game_doctor_ui_model.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$homeGameDoctorUIModelHash() =>
|
String _$homeGameDoctorUIModelHash() =>
|
||||||
r'b4132559510e3e59b1e2e330d9327ff8790df461';
|
r'137f6393bbbd76f3af0f7d0dd27d44d8473e42cc';
|
||||||
|
|
||||||
/// See also [HomeGameDoctorUIModel].
|
/// See also [HomeGameDoctorUIModel].
|
||||||
@ProviderFor(HomeGameDoctorUIModel)
|
@ProviderFor(HomeGameDoctorUIModel)
|
||||||
|
@ -7,7 +7,7 @@ part of 'localization_ui_model.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$localizationUIModelHash() =>
|
String _$localizationUIModelHash() =>
|
||||||
r'87152654734d322cd20d62baf050918adc9fd11b';
|
r'da9d0a3ae28825fd9331dd2b6db3d094cf3c0eb9';
|
||||||
|
|
||||||
/// See also [LocalizationUIModel].
|
/// See also [LocalizationUIModel].
|
||||||
@ProviderFor(LocalizationUIModel)
|
@ProviderFor(LocalizationUIModel)
|
||||||
|
@ -6,7 +6,7 @@ part of 'settings_ui_model.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$settingsUIModelHash() => r'acc2a90f5bbfc6ba82b17454e73881ac32b30b6a';
|
String _$settingsUIModelHash() => r'aab08176293b380f09c89e006f373fbfd7a7ba16';
|
||||||
|
|
||||||
/// See also [SettingsUIModel].
|
/// See also [SettingsUIModel].
|
||||||
@ProviderFor(SettingsUIModel)
|
@ProviderFor(SettingsUIModel)
|
||||||
|
@ -104,6 +104,13 @@ class ToolsUIModel extends _$ToolsUIModel {
|
|||||||
const Icon(FluentIcons.admin, size: 24),
|
const Icon(FluentIcons.admin, size: 24),
|
||||||
onTap: () => _adminRSILauncher(context),
|
onTap: () => _adminRSILauncher(context),
|
||||||
),
|
),
|
||||||
|
ToolsItemData(
|
||||||
|
"unp4kc",
|
||||||
|
"P4K 查看器",
|
||||||
|
"解包星际公民 p4k 文件",
|
||||||
|
const Icon(FontAwesomeIcons.fileZipper, size: 24),
|
||||||
|
onTap: () => _unp4kc(context),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
state = state.copyWith(items: items);
|
state = state.copyWith(items: items);
|
||||||
@ -573,4 +580,8 @@ class ToolsUIModel extends _$ToolsUIModel {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => const HostsBoosterDialogUI());
|
builder: (BuildContext context) => const HostsBoosterDialogUI());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_unp4kc(BuildContext context) async {
|
||||||
|
context.push("/tools/unp4kc");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ part of 'tools_ui_model.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$toolsUIModelHash() => r'e96ded635df8f59fb93d92f8dac337c44482dc1c';
|
String _$toolsUIModelHash() => r'5753016dc2cbcf3fafd2fa561d5b91a3295ca04b';
|
||||||
|
|
||||||
/// See also [ToolsUIModel].
|
/// See also [ToolsUIModel].
|
||||||
@ProviderFor(ToolsUIModel)
|
@ProviderFor(ToolsUIModel)
|
||||||
|
272
lib/ui/tools/unp4kc/unp4kc_ui.dart
Normal file
272
lib/ui/tools/unp4kc/unp4kc_ui.dart
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:file_sizes/file_sizes.dart';
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:re_editor/re_editor.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||||
|
import 'package:starcitizen_doctor/provider/unp4kc.dart';
|
||||||
|
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||||
|
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||||
|
|
||||||
|
class UnP4kcUI extends HookConsumerWidget {
|
||||||
|
const UnP4kcUI({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final state = ref.watch(unp4kCModelProvider);
|
||||||
|
final model = ref.read(unp4kCModelProvider.notifier);
|
||||||
|
final files = model.getFiles();
|
||||||
|
final paths = state.curPath.trim().split("\\");
|
||||||
|
return makeDefaultPage(context,
|
||||||
|
title: "P4K 查看器 -> ${model.getGamePath()}",
|
||||||
|
useBodyContainer: false,
|
||||||
|
content: state.files == null
|
||||||
|
? Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(child: makeLoading(context)),
|
||||||
|
if (state.endMessage != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
"${state.endMessage}",
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color:
|
||||||
|
FluentTheme.of(context).cardColor.withOpacity(.06)),
|
||||||
|
height: 36,
|
||||||
|
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||||
|
child: SuperListView.builder(
|
||||||
|
itemCount: paths.length - 1,
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
var path = paths[index];
|
||||||
|
if (path.isEmpty) {
|
||||||
|
path = "\\";
|
||||||
|
}
|
||||||
|
final fullPath =
|
||||||
|
"${paths.sublist(0, index + 1).join("\\")}\\";
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: Text(path),
|
||||||
|
onPressed: () {
|
||||||
|
model.changeDir(fullPath, fullPath: true);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
FluentIcons.chevron_right,
|
||||||
|
size: 12,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width * .3,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context)
|
||||||
|
.cardColor
|
||||||
|
.withOpacity(.01),
|
||||||
|
),
|
||||||
|
child: SuperListView.builder(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 6, bottom: 6, left: 3, right: 12),
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
final item = files![index];
|
||||||
|
final fileName = item.name
|
||||||
|
?.replaceAll(state.curPath.trim(), "") ??
|
||||||
|
"?";
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.only(top: 4, bottom: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context)
|
||||||
|
.cardColor
|
||||||
|
.withOpacity(.05),
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (item.isDirectory ?? false) {
|
||||||
|
model.changeDir(fileName);
|
||||||
|
} else {
|
||||||
|
model.openFile(item.name ?? "");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.only(left: 4, right: 4),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
if (item.isDirectory ?? false)
|
||||||
|
const Icon(
|
||||||
|
FluentIcons.folder_fill,
|
||||||
|
color:
|
||||||
|
Color.fromRGBO(255, 224, 138, 1),
|
||||||
|
)
|
||||||
|
else if (fileName.endsWith(".xml"))
|
||||||
|
const Icon(
|
||||||
|
FluentIcons.file_code,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const Icon(
|
||||||
|
FluentIcons.open_file,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
fileName,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 13),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (!(item.isDirectory ??
|
||||||
|
true)) ...[
|
||||||
|
const SizedBox(height: 1),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
FileSize.getSize(item.size),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: Colors.white
|
||||||
|
.withOpacity(.6)),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
"${item.dateTime}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: Colors.white
|
||||||
|
.withOpacity(.6)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
Icon(
|
||||||
|
FluentIcons.chevron_right,
|
||||||
|
size: 14,
|
||||||
|
color: Colors.white.withOpacity(.6),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: files?.length ?? 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
child: state.tempOpenFile == null
|
||||||
|
? const Center(
|
||||||
|
child: Text("单击文件以预览"),
|
||||||
|
)
|
||||||
|
: state.tempOpenFile?.key == "loading"
|
||||||
|
? makeLoading(context)
|
||||||
|
: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
if (state.tempOpenFile?.key == "text")
|
||||||
|
Expanded(
|
||||||
|
child: _TextTempWidget(
|
||||||
|
state.tempOpenFile?.value ??
|
||||||
|
""))
|
||||||
|
else
|
||||||
|
Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"未知文件类型\n${state.tempOpenFile?.value}"),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
FilledButton(
|
||||||
|
child: const Padding(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.all(4),
|
||||||
|
child: Text("打开文件夹"),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
SystemHelper.openDir(
|
||||||
|
state.tempOpenFile
|
||||||
|
?.value ??
|
||||||
|
"");
|
||||||
|
})
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
if (state.endMessage != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
"${state.endMessage}",
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TextTempWidget extends HookConsumerWidget {
|
||||||
|
final String filePath;
|
||||||
|
|
||||||
|
const _TextTempWidget(this.filePath);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final textData = useState<String?>(null);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
File(filePath).readAsString().then((value) {
|
||||||
|
textData.value = value;
|
||||||
|
}).catchError((err) {
|
||||||
|
textData.value = "Error: $err";
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}, const []);
|
||||||
|
|
||||||
|
if (textData.value == null) return makeLoading(context);
|
||||||
|
|
||||||
|
return CodeEditor(
|
||||||
|
controller: CodeLineEditingController.fromText('${textData.value}'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -84,6 +84,9 @@ dependencies:
|
|||||||
# path: ../../xkeyC/dart_aria2_rpc
|
# path: ../../xkeyC/dart_aria2_rpc
|
||||||
intl: ^0.18.0
|
intl: ^0.18.0
|
||||||
synchronized: ^3.1.0+1
|
synchronized: ^3.1.0+1
|
||||||
|
super_sliver_list: ^0.4.1
|
||||||
|
file: ^7.0.0
|
||||||
|
re_editor: ^0.1.0
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
http: ^1.1.2
|
http: ^1.1.2
|
||||||
|
|
||||||
|
@ -21,4 +21,4 @@ hickory-resolver = { version = "0.24" }
|
|||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
win32job = "2"
|
win32job = "2"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
scopeguard = "1.2"
|
scopeguard = "1.2"
|
@ -1,7 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
|
||||||
use crate::frb_generated::StreamSink;
|
|
||||||
|
|
||||||
|
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||||
|
|
||||||
|
use crate::frb_generated::StreamSink;
|
||||||
|
|
||||||
pub async fn start_process(
|
pub async fn start_process(
|
||||||
executable: String,
|
executable: String,
|
||||||
@ -37,12 +38,16 @@ pub async fn start_process(
|
|||||||
}
|
}
|
||||||
let stdout = child.stdout.take().expect("Failed to open stdout");
|
let stdout = child.stdout.take().expect("Failed to open stdout");
|
||||||
let stderr = child.stderr.take().expect("Failed to open stderr");
|
let stderr = child.stderr.take().expect("Failed to open stderr");
|
||||||
|
// let stdin = child.stdin.take().expect("Failed to open stderr");
|
||||||
|
|
||||||
let output_task = tokio::spawn(process_output(stdout, stream_sink_arc.clone()));
|
let output_task = tokio::spawn(process_output(stdout, stream_sink_arc.clone()));
|
||||||
let error_task = tokio::spawn(process_error(stderr, stream_sink_arc.clone()));
|
let error_task = tokio::spawn(process_error(stderr, stream_sink_arc.clone()));
|
||||||
|
// let input_task = tokio::spawn(process_input(stdin));
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = output_task => (),
|
_ = output_task => (),
|
||||||
_ = error_task => (),
|
_ = error_task => (),
|
||||||
|
// _ = input_task => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let exit_status = child.wait().await.expect("Failed to wait for child process");
|
let exit_status = child.wait().await.expect("Failed to wait for child process");
|
||||||
@ -82,5 +87,4 @@ async fn process_error<R>(stderr: R, stream_sink: Arc<StreamSink<String>>)
|
|||||||
println!("{}", line.trim());
|
println!("{}", line.trim());
|
||||||
stream_sink.add("error:".to_string() + &*line.trim().to_string()).unwrap();
|
stream_sink.add("error:".to_string() + &*line.trim().to_string()).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user