mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-02-12 02:00:22 +00:00
feat: update unp4k
This commit is contained in:
@@ -8,20 +8,20 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
|
||||
part 'unp4k_api.freezed.dart';
|
||||
|
||||
/// 打开 P4K 文件
|
||||
Future<BigInt> p4KOpen({required String p4KPath}) =>
|
||||
// These functions are ignored because they are not marked as `pub`: `dos_datetime_to_millis`, `ensure_files_loaded`
|
||||
|
||||
/// 打开 P4K 文件(仅打开,不读取文件列表)
|
||||
Future<void> p4KOpen({required String p4KPath}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiP4KOpen(p4KPath: p4KPath);
|
||||
|
||||
/// 获取文件数量(会触发文件列表加载)
|
||||
Future<BigInt> p4KGetFileCount() =>
|
||||
RustLib.instance.api.crateApiUnp4KApiP4KGetFileCount();
|
||||
|
||||
/// 获取所有文件列表
|
||||
Future<List<P4kFileItem>> p4KGetAllFiles() =>
|
||||
RustLib.instance.api.crateApiUnp4KApiP4KGetAllFiles();
|
||||
|
||||
/// 获取指定目录下的文件列表
|
||||
Future<List<P4kFileItem>> p4KGetFilesInDirectory({required String directory}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiP4KGetFilesInDirectory(
|
||||
directory: directory,
|
||||
);
|
||||
|
||||
/// 提取文件到内存
|
||||
Future<Uint8List> p4KExtractToMemory({required String filePath}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiP4KExtractToMemory(filePath: filePath);
|
||||
@@ -46,5 +46,6 @@ sealed class P4kFileItem with _$P4kFileItem {
|
||||
required bool isDirectory,
|
||||
required BigInt size,
|
||||
required BigInt compressedSize,
|
||||
required PlatformInt64 dateModified,
|
||||
}) = _P4kFileItem;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$P4kFileItem {
|
||||
|
||||
String get name; bool get isDirectory; BigInt get size; BigInt get compressedSize;
|
||||
String get name; bool get isDirectory; BigInt get size; BigInt get compressedSize; PlatformInt64 get dateModified;
|
||||
/// Create a copy of P4kFileItem
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $P4kFileItemCopyWith<P4kFileItem> get copyWith => _$P4kFileItemCopyWithImpl<P4kF
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is P4kFileItem&&(identical(other.name, name) || other.name == name)&&(identical(other.isDirectory, isDirectory) || other.isDirectory == isDirectory)&&(identical(other.size, size) || other.size == size)&&(identical(other.compressedSize, compressedSize) || other.compressedSize == compressedSize));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is P4kFileItem&&(identical(other.name, name) || other.name == name)&&(identical(other.isDirectory, isDirectory) || other.isDirectory == isDirectory)&&(identical(other.size, size) || other.size == size)&&(identical(other.compressedSize, compressedSize) || other.compressedSize == compressedSize)&&(identical(other.dateModified, dateModified) || other.dateModified == dateModified));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,name,isDirectory,size,compressedSize);
|
||||
int get hashCode => Object.hash(runtimeType,name,isDirectory,size,compressedSize,dateModified);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'P4kFileItem(name: $name, isDirectory: $isDirectory, size: $size, compressedSize: $compressedSize)';
|
||||
return 'P4kFileItem(name: $name, isDirectory: $isDirectory, size: $size, compressedSize: $compressedSize, dateModified: $dateModified)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $P4kFileItemCopyWith<$Res> {
|
||||
factory $P4kFileItemCopyWith(P4kFileItem value, $Res Function(P4kFileItem) _then) = _$P4kFileItemCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String name, bool isDirectory, BigInt size, BigInt compressedSize
|
||||
String name, bool isDirectory, BigInt size, BigInt compressedSize, PlatformInt64 dateModified
|
||||
});
|
||||
|
||||
|
||||
@@ -62,13 +62,14 @@ class _$P4kFileItemCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of P4kFileItem
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? isDirectory = null,Object? size = null,Object? compressedSize = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? isDirectory = null,Object? size = null,Object? compressedSize = null,Object? dateModified = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,isDirectory: null == isDirectory ? _self.isDirectory : isDirectory // ignore: cast_nullable_to_non_nullable
|
||||
as bool,size: null == size ? _self.size : size // ignore: cast_nullable_to_non_nullable
|
||||
as BigInt,compressedSize: null == compressedSize ? _self.compressedSize : compressedSize // ignore: cast_nullable_to_non_nullable
|
||||
as BigInt,
|
||||
as BigInt,dateModified: null == dateModified ? _self.dateModified : dateModified // ignore: cast_nullable_to_non_nullable
|
||||
as PlatformInt64,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -150,10 +151,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name, bool isDirectory, BigInt size, BigInt compressedSize)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name, bool isDirectory, BigInt size, BigInt compressedSize, PlatformInt64 dateModified)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _P4kFileItem() when $default != null:
|
||||
return $default(_that.name,_that.isDirectory,_that.size,_that.compressedSize);case _:
|
||||
return $default(_that.name,_that.isDirectory,_that.size,_that.compressedSize,_that.dateModified);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -171,10 +172,10 @@ return $default(_that.name,_that.isDirectory,_that.size,_that.compressedSize);ca
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name, bool isDirectory, BigInt size, BigInt compressedSize) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name, bool isDirectory, BigInt size, BigInt compressedSize, PlatformInt64 dateModified) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _P4kFileItem():
|
||||
return $default(_that.name,_that.isDirectory,_that.size,_that.compressedSize);}
|
||||
return $default(_that.name,_that.isDirectory,_that.size,_that.compressedSize,_that.dateModified);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
@@ -188,10 +189,10 @@ return $default(_that.name,_that.isDirectory,_that.size,_that.compressedSize);}
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name, bool isDirectory, BigInt size, BigInt compressedSize)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name, bool isDirectory, BigInt size, BigInt compressedSize, PlatformInt64 dateModified)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _P4kFileItem() when $default != null:
|
||||
return $default(_that.name,_that.isDirectory,_that.size,_that.compressedSize);case _:
|
||||
return $default(_that.name,_that.isDirectory,_that.size,_that.compressedSize,_that.dateModified);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -203,13 +204,14 @@ return $default(_that.name,_that.isDirectory,_that.size,_that.compressedSize);ca
|
||||
|
||||
|
||||
class _P4kFileItem implements P4kFileItem {
|
||||
const _P4kFileItem({required this.name, required this.isDirectory, required this.size, required this.compressedSize});
|
||||
const _P4kFileItem({required this.name, required this.isDirectory, required this.size, required this.compressedSize, required this.dateModified});
|
||||
|
||||
|
||||
@override final String name;
|
||||
@override final bool isDirectory;
|
||||
@override final BigInt size;
|
||||
@override final BigInt compressedSize;
|
||||
@override final PlatformInt64 dateModified;
|
||||
|
||||
/// Create a copy of P4kFileItem
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -221,16 +223,16 @@ _$P4kFileItemCopyWith<_P4kFileItem> get copyWith => __$P4kFileItemCopyWithImpl<_
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _P4kFileItem&&(identical(other.name, name) || other.name == name)&&(identical(other.isDirectory, isDirectory) || other.isDirectory == isDirectory)&&(identical(other.size, size) || other.size == size)&&(identical(other.compressedSize, compressedSize) || other.compressedSize == compressedSize));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _P4kFileItem&&(identical(other.name, name) || other.name == name)&&(identical(other.isDirectory, isDirectory) || other.isDirectory == isDirectory)&&(identical(other.size, size) || other.size == size)&&(identical(other.compressedSize, compressedSize) || other.compressedSize == compressedSize)&&(identical(other.dateModified, dateModified) || other.dateModified == dateModified));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,name,isDirectory,size,compressedSize);
|
||||
int get hashCode => Object.hash(runtimeType,name,isDirectory,size,compressedSize,dateModified);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'P4kFileItem(name: $name, isDirectory: $isDirectory, size: $size, compressedSize: $compressedSize)';
|
||||
return 'P4kFileItem(name: $name, isDirectory: $isDirectory, size: $size, compressedSize: $compressedSize, dateModified: $dateModified)';
|
||||
}
|
||||
|
||||
|
||||
@@ -241,7 +243,7 @@ abstract mixin class _$P4kFileItemCopyWith<$Res> implements $P4kFileItemCopyWith
|
||||
factory _$P4kFileItemCopyWith(_P4kFileItem value, $Res Function(_P4kFileItem) _then) = __$P4kFileItemCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String name, bool isDirectory, BigInt size, BigInt compressedSize
|
||||
String name, bool isDirectory, BigInt size, BigInt compressedSize, PlatformInt64 dateModified
|
||||
});
|
||||
|
||||
|
||||
@@ -258,13 +260,14 @@ class __$P4kFileItemCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of P4kFileItem
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? isDirectory = null,Object? size = null,Object? compressedSize = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? isDirectory = null,Object? size = null,Object? compressedSize = null,Object? dateModified = null,}) {
|
||||
return _then(_P4kFileItem(
|
||||
name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,isDirectory: null == isDirectory ? _self.isDirectory : isDirectory // ignore: cast_nullable_to_non_nullable
|
||||
as bool,size: null == size ? _self.size : size // ignore: cast_nullable_to_non_nullable
|
||||
as BigInt,compressedSize: null == compressedSize ? _self.compressedSize : compressedSize // ignore: cast_nullable_to_non_nullable
|
||||
as BigInt,
|
||||
as BigInt,dateModified: null == dateModified ? _self.dateModified : dateModified // ignore: cast_nullable_to_non_nullable
|
||||
as PlatformInt64,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
String get codegenVersion => '2.11.1';
|
||||
|
||||
@override
|
||||
int get rustContentHash => -737964996;
|
||||
int get rustContentHash => 1801517256;
|
||||
|
||||
static const kDefaultExternalLibraryLoaderConfig =
|
||||
ExternalLibraryLoaderConfig(
|
||||
@@ -133,11 +133,9 @@ abstract class RustLibApi extends BaseApi {
|
||||
|
||||
Future<List<P4kFileItem>> crateApiUnp4KApiP4KGetAllFiles();
|
||||
|
||||
Future<List<P4kFileItem>> crateApiUnp4KApiP4KGetFilesInDirectory({
|
||||
required String directory,
|
||||
});
|
||||
Future<BigInt> crateApiUnp4KApiP4KGetFileCount();
|
||||
|
||||
Future<BigInt> crateApiUnp4KApiP4KOpen({required String p4KPath});
|
||||
Future<void> crateApiUnp4KApiP4KOpen({required String p4KPath});
|
||||
|
||||
Future<void> crateApiAsarApiRsiLauncherAsarDataWriteMainJs({
|
||||
required RsiLauncherAsarData that,
|
||||
@@ -582,37 +580,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
const TaskConstMeta(debugName: "p4k_get_all_files", argNames: []);
|
||||
|
||||
@override
|
||||
Future<List<P4kFileItem>> crateApiUnp4KApiP4KGetFilesInDirectory({
|
||||
required String directory,
|
||||
}) {
|
||||
Future<BigInt> crateApiUnp4KApiP4KGetFileCount() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(directory);
|
||||
return wire.wire__crate__api__unp4k_api__p4k_get_files_in_directory(
|
||||
port_,
|
||||
arg0,
|
||||
);
|
||||
return wire.wire__crate__api__unp4k_api__p4k_get_file_count(port_);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_list_p_4_k_file_item,
|
||||
decodeSuccessData: dco_decode_usize,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiUnp4KApiP4KGetFilesInDirectoryConstMeta,
|
||||
argValues: [directory],
|
||||
constMeta: kCrateApiUnp4KApiP4KGetFileCountConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiUnp4KApiP4KGetFilesInDirectoryConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "p4k_get_files_in_directory",
|
||||
argNames: ["directory"],
|
||||
);
|
||||
TaskConstMeta get kCrateApiUnp4KApiP4KGetFileCountConstMeta =>
|
||||
const TaskConstMeta(debugName: "p4k_get_file_count", argNames: []);
|
||||
|
||||
@override
|
||||
Future<BigInt> crateApiUnp4KApiP4KOpen({required String p4KPath}) {
|
||||
Future<void> crateApiUnp4KApiP4KOpen({required String p4KPath}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
@@ -620,7 +609,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return wire.wire__crate__api__unp4k_api__p4k_open(port_, arg0);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_usize,
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiUnp4KApiP4KOpenConstMeta,
|
||||
@@ -991,6 +980,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw as int;
|
||||
}
|
||||
|
||||
@protected
|
||||
PlatformInt64 dco_decode_i_64(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return dcoDecodeI64(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
List<String> dco_decode_list_String(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@@ -1073,13 +1068,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
P4kFileItem dco_decode_p_4_k_file_item(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 4)
|
||||
throw Exception('unexpected arr length: expect 4 but see ${arr.length}');
|
||||
if (arr.length != 5)
|
||||
throw Exception('unexpected arr length: expect 5 but see ${arr.length}');
|
||||
return P4kFileItem(
|
||||
name: dco_decode_String(arr[0]),
|
||||
isDirectory: dco_decode_bool(arr[1]),
|
||||
size: dco_decode_u_64(arr[2]),
|
||||
compressedSize: dco_decode_u_64(arr[3]),
|
||||
dateModified: dco_decode_i_64(arr[4]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1255,6 +1251,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return deserializer.buffer.getInt32();
|
||||
}
|
||||
|
||||
@protected
|
||||
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getPlatformInt64();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<String> sse_decode_list_String(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -1399,11 +1401,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
var var_isDirectory = sse_decode_bool(deserializer);
|
||||
var var_size = sse_decode_u_64(deserializer);
|
||||
var var_compressedSize = sse_decode_u_64(deserializer);
|
||||
var var_dateModified = sse_decode_i_64(deserializer);
|
||||
return P4kFileItem(
|
||||
name: var_name,
|
||||
isDirectory: var_isDirectory,
|
||||
size: var_size,
|
||||
compressedSize: var_compressedSize,
|
||||
dateModified: var_dateModified,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1652,6 +1656,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
serializer.buffer.putInt32(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
serializer.buffer.putPlatformInt64(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_String(List<String> self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@@ -1797,6 +1807,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
sse_encode_bool(self.isDirectory, serializer);
|
||||
sse_encode_u_64(self.size, serializer);
|
||||
sse_encode_u_64(self.compressedSize, serializer);
|
||||
sse_encode_i_64(self.dateModified, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
|
||||
@@ -54,6 +54,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
int dco_decode_i_32(dynamic raw);
|
||||
|
||||
@protected
|
||||
PlatformInt64 dco_decode_i_64(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<String> dco_decode_list_String(dynamic raw);
|
||||
|
||||
@@ -166,6 +169,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<String> sse_decode_list_String(SseDeserializer deserializer);
|
||||
|
||||
@@ -318,6 +324,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
return wire.cst_new_box_autoadd_u_64(cst_encode_u_64(raw));
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_i_64(PlatformInt64 raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw.toInt();
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_String> cst_encode_list_String(List<String> raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
@@ -446,6 +458,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
wireObj.is_directory = cst_encode_bool(apiObj.isDirectory);
|
||||
wireObj.size = cst_encode_u_64(apiObj.size);
|
||||
wireObj.compressed_size = cst_encode_u_64(apiObj.compressedSize);
|
||||
wireObj.date_modified = cst_encode_i_64(apiObj.dateModified);
|
||||
}
|
||||
|
||||
@protected
|
||||
@@ -571,6 +584,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_String(List<String> self, SseSerializer serializer);
|
||||
|
||||
@@ -1064,32 +1080,17 @@ class RustLibWire implements BaseWire {
|
||||
_wire__crate__api__unp4k_api__p4k_get_all_filesPtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__unp4k_api__p4k_get_files_in_directory(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> directory,
|
||||
) {
|
||||
return _wire__crate__api__unp4k_api__p4k_get_files_in_directory(
|
||||
port_,
|
||||
directory,
|
||||
);
|
||||
void wire__crate__api__unp4k_api__p4k_get_file_count(int port_) {
|
||||
return _wire__crate__api__unp4k_api__p4k_get_file_count(port_);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__unp4k_api__p4k_get_files_in_directoryPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_get_files_in_directory',
|
||||
late final _wire__crate__api__unp4k_api__p4k_get_file_countPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_get_file_count',
|
||||
);
|
||||
late final _wire__crate__api__unp4k_api__p4k_get_files_in_directory =
|
||||
_wire__crate__api__unp4k_api__p4k_get_files_in_directoryPtr
|
||||
.asFunction<
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)
|
||||
>();
|
||||
late final _wire__crate__api__unp4k_api__p4k_get_file_count =
|
||||
_wire__crate__api__unp4k_api__p4k_get_file_countPtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__unp4k_api__p4k_open(
|
||||
int port_,
|
||||
@@ -1592,6 +1593,9 @@ final class wire_cst_p_4_k_file_item extends ffi.Struct {
|
||||
|
||||
@ffi.Uint64()
|
||||
external int compressed_size;
|
||||
|
||||
@ffi.Int64()
|
||||
external int date_modified;
|
||||
}
|
||||
|
||||
final class wire_cst_list_p_4_k_file_item extends ffi.Struct {
|
||||
|
||||
@@ -2,46 +2,25 @@
|
||||
/// 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({this.name, this.size, this.compressedSize, this.isDirectory, this.dateModified});
|
||||
|
||||
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'];
|
||||
dateModified = json['dateModified'];
|
||||
}
|
||||
|
||||
String? name;
|
||||
num? size;
|
||||
num? compressedSize;
|
||||
bool? isDirectory;
|
||||
bool? isFile;
|
||||
bool? isEncrypted;
|
||||
bool? isUnicodeText;
|
||||
String? dateTime;
|
||||
num? version;
|
||||
|
||||
/// 文件修改时间(毫秒时间戳)
|
||||
int? dateModified;
|
||||
List<AppUnp4kP4kItemData> children = [];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@@ -50,11 +29,7 @@ class AppUnp4kP4kItemData {
|
||||
map['size'] = size;
|
||||
map['compressedSize'] = compressedSize;
|
||||
map['isDirectory'] = isDirectory;
|
||||
map['isFile'] = isFile;
|
||||
map['isEncrypted'] = isEncrypted;
|
||||
map['isUnicodeText'] = isUnicodeText;
|
||||
map['dateTime'] = dateTime;
|
||||
map['version'] = version;
|
||||
map['dateModified'] = dateModified;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,12 +51,12 @@ class Unp4kCModel extends _$Unp4kCModel {
|
||||
|
||||
final loadStartTime = DateTime.now();
|
||||
|
||||
// 使用 Rust API 打开 P4K 文件(异步)
|
||||
// 使用 Rust API 打开 P4K 文件(仅打开,不读取文件列表)
|
||||
await unp4k_api.p4KOpen(p4KPath: gameP4kPath);
|
||||
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading2);
|
||||
|
||||
// 获取所有文件列表(异步)
|
||||
// 获取所有文件列表(会触发文件列表加载)
|
||||
final p4kFiles = await unp4k_api.p4KGetAllFiles();
|
||||
|
||||
final files = <String, AppUnp4kP4kItemData>{};
|
||||
@@ -70,6 +70,7 @@ class Unp4kCModel extends _$Unp4kCModel {
|
||||
isDirectory: item.isDirectory,
|
||||
size: item.size.toInt(),
|
||||
compressedSize: item.compressedSize.toInt(),
|
||||
dateModified: item.dateModified,
|
||||
);
|
||||
|
||||
files[item.name] = fileData;
|
||||
@@ -158,7 +159,7 @@ class Unp4kCModel extends _$Unp4kCModel {
|
||||
tempOpenFile: const MapEntry("loading", ""),
|
||||
endMessage: S.current.tools_unp4k_msg_open_file(filePath),
|
||||
);
|
||||
extractFile(filePath, tempPath, mode: "extract_open");
|
||||
await extractFile(filePath, tempPath, mode: "extract_open");
|
||||
}
|
||||
|
||||
Future<void> extractFile(String filePath, String outputPath, {String mode = "extract"}) async {
|
||||
@@ -171,6 +172,7 @@ class Unp4kCModel extends _$Unp4kCModel {
|
||||
final fullOutputPath = "$outputPath$filePath";
|
||||
dPrint("extractFile .... $filePath -> $fullOutputPath");
|
||||
|
||||
// 使用 Rust API 提取到磁盘
|
||||
await unp4k_api.p4KExtractToDisk(filePath: filePath, outputPath: fullOutputPath);
|
||||
|
||||
if (mode == "extract_open") {
|
||||
@@ -198,11 +200,6 @@ class Unp4kCModel extends _$Unp4kCModel {
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkRunTimeError(String errorMessage) {
|
||||
// Rust 实现不再需要 .NET runtime,这个方法保留以兼容现有代码
|
||||
return false;
|
||||
}
|
||||
|
||||
/// 从 P4K 文件中提取指定文件到内存
|
||||
/// [p4kPath] P4K 文件路径
|
||||
/// [filePath] 要提取的文件路径(P4K 内部路径)
|
||||
|
||||
@@ -41,7 +41,7 @@ final class Unp4kCModelProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$unp4kCModelHash() => r'fe88d52b11464fdbded606bacbd833c1e908b738';
|
||||
String _$unp4kCModelHash() => r'a296a499158e78848a698c3fda92c4c88ff039be';
|
||||
|
||||
abstract class _$Unp4kCModel extends $Notifier<Unp4kcState> {
|
||||
Unp4kcState build();
|
||||
|
||||
@@ -229,9 +229,7 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel {
|
||||
return iniData;
|
||||
} catch (e) {
|
||||
final errorMessage = e.toString();
|
||||
if (Unp4kCModel.checkRunTimeError(errorMessage)) {
|
||||
AnalyticsApi.touch("advanced_localization_no_runtime");
|
||||
}
|
||||
// Rust 实现不再需要 .NET runtime 检查
|
||||
state = state.copyWith(errorMessage: errorMessage);
|
||||
// rethrow;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ final class AdvancedLocalizationUIModelProvider
|
||||
}
|
||||
|
||||
String _$advancedLocalizationUIModelHash() =>
|
||||
r'c7cca8935ac7df2281e83297b11b6b82d94f7a59';
|
||||
r'5ff4d8156fbae4dcf69cb3fbcabfb9abda69ffbb';
|
||||
|
||||
abstract class _$AdvancedLocalizationUIModel
|
||||
extends $Notifier<AdvancedLocalizationUIState> {
|
||||
|
||||
@@ -11,7 +11,6 @@ import 'package:starcitizen_doctor/data/app_unp4k_p4k_item_data.dart';
|
||||
import 'package:starcitizen_doctor/provider/unp4kc.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class UnP4kcUI extends HookConsumerWidget {
|
||||
const UnP4kcUI({super.key});
|
||||
@@ -28,14 +27,21 @@ class UnP4kcUI extends HookConsumerWidget {
|
||||
return null;
|
||||
}, const []);
|
||||
|
||||
return makeDefaultPage(context,
|
||||
title: S.current.tools_unp4k_title(model.getGamePath()),
|
||||
useBodyContainer: false,
|
||||
content: makeBody(context, state, model, files, paths));
|
||||
return makeDefaultPage(
|
||||
context,
|
||||
title: S.current.tools_unp4k_title(model.getGamePath()),
|
||||
useBodyContainer: false,
|
||||
content: makeBody(context, state, model, files, paths),
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeBody(BuildContext context, Unp4kcState state, Unp4kCModel model,
|
||||
List<AppUnp4kP4kItemData>? files, List<String> paths) {
|
||||
Widget makeBody(
|
||||
BuildContext context,
|
||||
Unp4kcState state,
|
||||
Unp4kCModel model,
|
||||
List<AppUnp4kP4kItemData>? files,
|
||||
List<String> paths,
|
||||
) {
|
||||
if (state.errorMessage.isNotEmpty) {
|
||||
return UnP4kErrorWidget(errorMessage: state.errorMessage);
|
||||
}
|
||||
@@ -47,10 +53,7 @@ class UnP4kcUI extends HookConsumerWidget {
|
||||
if (state.endMessage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"${state.endMessage}",
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
child: Text("${state.endMessage}", style: const TextStyle(fontSize: 12)),
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -58,10 +61,7 @@ class UnP4kcUI extends HookConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context)
|
||||
.cardColor
|
||||
.withValues(alpha: .06)),
|
||||
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor.withValues(alpha: .06)),
|
||||
height: 36,
|
||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||
child: SuperListView.builder(
|
||||
@@ -72,8 +72,7 @@ class UnP4kcUI extends HookConsumerWidget {
|
||||
if (path.isEmpty) {
|
||||
path = "\\";
|
||||
}
|
||||
final fullPath =
|
||||
"${paths.sublist(0, index + 1).join("\\")}\\";
|
||||
final fullPath = "${paths.sublist(0, index + 1).join("\\")}\\";
|
||||
return Row(
|
||||
children: [
|
||||
IconButton(
|
||||
@@ -82,181 +81,153 @@ class UnP4kcUI extends HookConsumerWidget {
|
||||
model.changeDir(fullPath, fullPath: true);
|
||||
},
|
||||
),
|
||||
const Icon(
|
||||
FluentIcons.chevron_right,
|
||||
size: 12,
|
||||
),
|
||||
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
|
||||
.withValues(alpha: .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
|
||||
.withValues(alpha: .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),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * .3,
|
||||
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor.withValues(alpha: .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.withValues(alpha: .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: [
|
||||
Text(
|
||||
FileSize.getSize(item.size),
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white
|
||||
.withValues(alpha: .6)),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
"${item.dateTime}",
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white
|
||||
.withValues(alpha: .6)),
|
||||
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.withValues(alpha: .6),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
item.dateModified != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(
|
||||
item.dateModified!,
|
||||
).toString().substring(0, 19)
|
||||
: "",
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
Icon(
|
||||
FluentIcons.chevron_right,
|
||||
size: 14,
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
)
|
||||
],
|
||||
const SizedBox(width: 3),
|
||||
Icon(
|
||||
FluentIcons.chevron_right,
|
||||
size: 14,
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: files?.length ?? 0,
|
||||
);
|
||||
},
|
||||
itemCount: files?.length ?? 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: state.tempOpenFile == null
|
||||
? Center(
|
||||
child: Text(S.current.tools_unp4k_view_file),
|
||||
)
|
||||
: state.tempOpenFile?.key == "loading"
|
||||
child: state.tempOpenFile == null
|
||||
? Center(child: Text(S.current.tools_unp4k_view_file))
|
||||
: 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 ?? ""))
|
||||
Expanded(child: _TextTempWidget(state.tempOpenFile?.value ?? ""))
|
||||
else
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(S.current
|
||||
.tools_unp4k_msg_unknown_file_type(
|
||||
state.tempOpenFile
|
||||
?.value ??
|
||||
"")),
|
||||
Text(
|
||||
S.current.tools_unp4k_msg_unknown_file_type(
|
||||
state.tempOpenFile?.value ?? "",
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
FilledButton(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.all(4),
|
||||
child: Text(S.current
|
||||
.action_open_folder),
|
||||
),
|
||||
onPressed: () {
|
||||
SystemHelper.openDir(state
|
||||
.tempOpenFile
|
||||
?.value ??
|
||||
"");
|
||||
})
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(S.current.action_open_folder),
|
||||
),
|
||||
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),
|
||||
),
|
||||
child: Text("${state.endMessage}", style: const TextStyle(fontSize: 12)),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -273,20 +244,20 @@ class _TextTempWidget extends HookConsumerWidget {
|
||||
final textData = useState<String?>(null);
|
||||
|
||||
useEffect(() {
|
||||
File(filePath).readAsString().then((value) {
|
||||
textData.value = value;
|
||||
}).catchError((err) {
|
||||
textData.value = "Error: $err";
|
||||
});
|
||||
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}'),
|
||||
readOnly: true,
|
||||
);
|
||||
return CodeEditor(controller: CodeLineEditingController.fromText('${textData.value}'), readOnly: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,39 +266,12 @@ class UnP4kErrorWidget extends StatelessWidget {
|
||||
|
||||
const UnP4kErrorWidget({super.key, required this.errorMessage});
|
||||
|
||||
static const _downloadUrl =
|
||||
"https://aka.ms/dotnet-core-applaunch?missing_runtime=true&arch=x64&rid=win-x64&os=win10&apphost_version=8.0.0";
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (Unp4kCModel.checkRunTimeError(errorMessage)) ...[
|
||||
Text(
|
||||
S.current.tools_unp4k_missing_runtime,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(S.current.tools_unp4k_missing_runtime_info),
|
||||
const SizedBox(height: 16),
|
||||
FilledButton(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
|
||||
child: Text(
|
||||
S.current.tools_unp4k_missing_runtime_action_install),
|
||||
),
|
||||
onPressed: () {
|
||||
launchUrlString(_downloadUrl);
|
||||
}),
|
||||
] else
|
||||
Text(errorMessage),
|
||||
],
|
||||
),
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [Text(errorMessage)]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user