From e1ed30b6e65520a993c7beeb89ce3affc9469ae6 Mon Sep 17 00:00:00 2001 From: xkeyC <3334969096@qq.com> Date: Thu, 4 Dec 2025 16:06:49 +0800 Subject: [PATCH] feat: update unp4k --- lib/common/rust/api/unp4k_api.dart | 17 +- lib/common/rust/api/unp4k_api.freezed.dart | 43 +-- lib/common/rust/frb_generated.dart | 61 ++-- lib/common/rust/frb_generated.io.dart | 50 +-- lib/data/app_unp4k_p4k_item_data.dart | 37 +- lib/provider/unp4kc.dart | 13 +- lib/provider/unp4kc.g.dart | 2 +- .../advanced_localization_ui_model.dart | 4 +- .../advanced_localization_ui_model.g.dart | 2 +- lib/ui/tools/unp4kc/unp4kc_ui.dart | 322 ++++++++---------- rust/Cargo.lock | 2 +- rust/Cargo.toml | 2 +- rust/src/api/unp4k_api.rs | 174 +++++----- rust/src/frb_generated.rs | 44 ++- 14 files changed, 362 insertions(+), 411 deletions(-) diff --git a/lib/common/rust/api/unp4k_api.dart b/lib/common/rust/api/unp4k_api.dart index 1505510..a362e5d 100644 --- a/lib/common/rust/api/unp4k_api.dart +++ b/lib/common/rust/api/unp4k_api.dart @@ -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 p4KOpen({required String p4KPath}) => +// These functions are ignored because they are not marked as `pub`: `dos_datetime_to_millis`, `ensure_files_loaded` + +/// 打开 P4K 文件(仅打开,不读取文件列表) +Future p4KOpen({required String p4KPath}) => RustLib.instance.api.crateApiUnp4KApiP4KOpen(p4KPath: p4KPath); +/// 获取文件数量(会触发文件列表加载) +Future p4KGetFileCount() => + RustLib.instance.api.crateApiUnp4KApiP4KGetFileCount(); + /// 获取所有文件列表 Future> p4KGetAllFiles() => RustLib.instance.api.crateApiUnp4KApiP4KGetAllFiles(); -/// 获取指定目录下的文件列表 -Future> p4KGetFilesInDirectory({required String directory}) => - RustLib.instance.api.crateApiUnp4KApiP4KGetFilesInDirectory( - directory: directory, - ); - /// 提取文件到内存 Future 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; } diff --git a/lib/common/rust/api/unp4k_api.freezed.dart b/lib/common/rust/api/unp4k_api.freezed.dart index 547fb30..d37457c 100644 --- a/lib/common/rust/api/unp4k_api.freezed.dart +++ b/lib/common/rust/api/unp4k_api.freezed.dart @@ -14,7 +14,7 @@ T _$identity(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 get copyWith => _$P4kFileItemCopyWithImpl 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 Function( String name, bool isDirectory, BigInt size, BigInt compressedSize)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(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 Function( String name, bool isDirectory, BigInt size, BigInt compressedSize) $default,) {final _that = this; +@optionalTypeArgs TResult when(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? Function( String name, bool isDirectory, BigInt size, BigInt compressedSize)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(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, )); } diff --git a/lib/common/rust/frb_generated.dart b/lib/common/rust/frb_generated.dart index f91728d..64a9bb9 100644 --- a/lib/common/rust/frb_generated.dart +++ b/lib/common/rust/frb_generated.dart @@ -70,7 +70,7 @@ class RustLib extends BaseEntrypoint { 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> crateApiUnp4KApiP4KGetAllFiles(); - Future> crateApiUnp4KApiP4KGetFilesInDirectory({ - required String directory, - }); + Future crateApiUnp4KApiP4KGetFileCount(); - Future crateApiUnp4KApiP4KOpen({required String p4KPath}); + Future crateApiUnp4KApiP4KOpen({required String p4KPath}); Future crateApiAsarApiRsiLauncherAsarDataWriteMainJs({ required RsiLauncherAsarData that, @@ -582,37 +580,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { const TaskConstMeta(debugName: "p4k_get_all_files", argNames: []); @override - Future> crateApiUnp4KApiP4KGetFilesInDirectory({ - required String directory, - }) { + Future 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 crateApiUnp4KApiP4KOpen({required String p4KPath}) { + Future 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 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; - 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 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 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 diff --git a/lib/common/rust/frb_generated.io.dart b/lib/common/rust/frb_generated.io.dart index 96dc3a7..cee2de8 100644 --- a/lib/common/rust/frb_generated.io.dart +++ b/lib/common/rust/frb_generated.io.dart @@ -54,6 +54,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected int dco_decode_i_32(dynamic raw); + @protected + PlatformInt64 dco_decode_i_64(dynamic raw); + @protected List dco_decode_list_String(dynamic raw); @@ -166,6 +169,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected int sse_decode_i_32(SseDeserializer deserializer); + @protected + PlatformInt64 sse_decode_i_64(SseDeserializer deserializer); + @protected List sse_decode_list_String(SseDeserializer deserializer); @@ -318,6 +324,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { 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 cst_encode_list_String(List raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -446,6 +458,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { 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 { @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 self, SseSerializer serializer); @@ -1064,32 +1080,17 @@ class RustLibWire implements BaseWire { _wire__crate__api__unp4k_api__p4k_get_all_filesPtr .asFunction(); - void wire__crate__api__unp4k_api__p4k_get_files_in_directory( - int port_, - ffi.Pointer 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, - ) - > - >( - '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>( + '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) - >(); + late final _wire__crate__api__unp4k_api__p4k_get_file_count = + _wire__crate__api__unp4k_api__p4k_get_file_countPtr + .asFunction(); 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 { diff --git a/lib/data/app_unp4k_p4k_item_data.dart b/lib/data/app_unp4k_p4k_item_data.dart index 465eb86..24eb83f 100644 --- a/lib/data/app_unp4k_p4k_item_data.dart +++ b/lib/data/app_unp4k_p4k_item_data.dart @@ -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 children = []; Map 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; } } diff --git a/lib/provider/unp4kc.dart b/lib/provider/unp4kc.dart index 3af3e75..dfcfc7d 100644 --- a/lib/provider/unp4kc.dart +++ b/lib/provider/unp4kc.dart @@ -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 = {}; @@ -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 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 内部路径) diff --git a/lib/provider/unp4kc.g.dart b/lib/provider/unp4kc.g.dart index 749885c..2dd3284 100644 --- a/lib/provider/unp4kc.g.dart +++ b/lib/provider/unp4kc.g.dart @@ -41,7 +41,7 @@ final class Unp4kCModelProvider } } -String _$unp4kCModelHash() => r'fe88d52b11464fdbded606bacbd833c1e908b738'; +String _$unp4kCModelHash() => r'a296a499158e78848a698c3fda92c4c88ff039be'; abstract class _$Unp4kCModel extends $Notifier { Unp4kcState build(); diff --git a/lib/ui/home/localization/advanced_localization_ui_model.dart b/lib/ui/home/localization/advanced_localization_ui_model.dart index 2e7c33d..9083bbf 100644 --- a/lib/ui/home/localization/advanced_localization_ui_model.dart +++ b/lib/ui/home/localization/advanced_localization_ui_model.dart @@ -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; } diff --git a/lib/ui/home/localization/advanced_localization_ui_model.g.dart b/lib/ui/home/localization/advanced_localization_ui_model.g.dart index 4b5d99d..7873116 100644 --- a/lib/ui/home/localization/advanced_localization_ui_model.g.dart +++ b/lib/ui/home/localization/advanced_localization_ui_model.g.dart @@ -47,7 +47,7 @@ final class AdvancedLocalizationUIModelProvider } String _$advancedLocalizationUIModelHash() => - r'c7cca8935ac7df2281e83297b11b6b82d94f7a59'; + r'5ff4d8156fbae4dcf69cb3fbcabfb9abda69ffbb'; abstract class _$AdvancedLocalizationUIModel extends $Notifier { diff --git a/lib/ui/tools/unp4kc/unp4kc_ui.dart b/lib/ui/tools/unp4kc/unp4kc_ui.dart index 3956fd2..26e9c23 100644 --- a/lib/ui/tools/unp4kc/unp4kc_ui.dart +++ b/lib/ui/tools/unp4kc/unp4kc_ui.dart @@ -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? files, List paths) { + Widget makeBody( + BuildContext context, + Unp4kcState state, + Unp4kCModel model, + List? files, + List 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(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)]), ), ); } diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 04a9e19..730e9df 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -3868,7 +3868,7 @@ checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" [[package]] name = "unp4k_rs" version = "0.1.0" -source = "git+https://github.com/StarCitizenToolBox/unp4k_rs?tag=V0.0.1#67f6ae242c480de9ee50ed03d31c948b68bf9522" +source = "git+https://github.com/StarCitizenToolBox/unp4k_rs?tag=V0.0.2#02867472dda1c18e81b0f635b8653fa86bd145cb" dependencies = [ "aes", "anyhow", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 9c65e22..06ca71c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -30,7 +30,7 @@ ort = { version = "2.0.0-rc.10", features = ["xnnpack", "download-binaries", "nd tokenizers = { version = "0.22", default-features = false, features = ["onig"] } ndarray = "0.17" serde_json = "1.0" -unp4k_rs = { git = "https://github.com/StarCitizenToolBox/unp4k_rs", tag = "V0.0.1" } +unp4k_rs = { git = "https://github.com/StarCitizenToolBox/unp4k_rs", tag = "V0.0.2" } [target.'cfg(windows)'.dependencies] windows = { version = "0.62.2", features = [ diff --git a/rust/src/api/unp4k_api.rs b/rust/src/api/unp4k_api.rs index a916458..43f6877 100644 --- a/rust/src/api/unp4k_api.rs +++ b/rust/src/api/unp4k_api.rs @@ -16,6 +16,36 @@ pub struct P4kFileItem { pub size: u64, /// 压缩后大小(字节) pub compressed_size: u64, + /// 文件修改时间(毫秒时间戳) + pub date_modified: i64, +} + +/// 将 DOS 日期时间转换为毫秒时间戳 +fn dos_datetime_to_millis(date: u16, time: u16) -> i64 { + let year = ((date >> 9) & 0x7F) as i32 + 1980; + let month = ((date >> 5) & 0x0F) as u32; + let day = (date & 0x1F) as u32; + let hour = ((time >> 11) & 0x1F) as u32; + let minute = ((time >> 5) & 0x3F) as u32; + let second = ((time & 0x1F) * 2) as u32; + + let days_since_epoch = { + let mut days = 0i64; + for y in 1970..year { + days += if (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0) { 366 } else { 365 }; + } + let days_in_months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; + if month >= 1 && month <= 12 { + days += days_in_months[(month - 1) as usize] as i64; + if month > 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + days += 1; + } + } + days += (day as i64) - 1; + days + }; + + (days_since_epoch * 86400 + (hour as i64) * 3600 + (minute as i64) * 60 + (second as i64)) * 1000 } // 全局 P4K 读取器实例(用于保持状态) @@ -25,118 +55,84 @@ static GLOBAL_P4K_READER: once_cell::sync::Lazy>>> = static GLOBAL_P4K_FILES: once_cell::sync::Lazy>>> = once_cell::sync::Lazy::new(|| Arc::new(Mutex::new(HashMap::new()))); -/// 打开 P4K 文件 -pub async fn p4k_open(p4k_path: String) -> Result { +/// 打开 P4K 文件(仅打开,不读取文件列表) +pub async fn p4k_open(p4k_path: String) -> Result<()> { let path = PathBuf::from(&p4k_path); if !path.exists() { return Err(anyhow!("P4K file not found: {}", p4k_path)); } // 在后台线程执行阻塞操作 - let (reader, file_count, files_map) = tokio::task::spawn_blocking(move || { + let reader = tokio::task::spawn_blocking(move || { let reader = P4kFile::open(&path)?; - let entries = reader.entries(); - let file_count = entries.len(); - - let mut files_map = HashMap::new(); - for entry in entries { - // 将路径转换为 Windows 风格,以 \ 开头 - let name = if entry.name.starts_with("\\") { - entry.name.clone() - } else { - format!("\\{}", entry.name.replace("/", "\\")) - }; - files_map.insert(name, entry.clone()); - } - - Ok::<_, anyhow::Error>((reader, file_count, files_map)) + Ok::<_, anyhow::Error>(reader) }) .await??; *GLOBAL_P4K_READER.lock().unwrap() = Some(reader); - *GLOBAL_P4K_FILES.lock().unwrap() = files_map; + // 清空之前的文件列表缓存 + GLOBAL_P4K_FILES.lock().unwrap().clear(); - Ok(file_count) + Ok(()) +} + +/// 确保文件列表已加载(内部使用) +fn ensure_files_loaded() -> Result { + let mut files = GLOBAL_P4K_FILES.lock().unwrap(); + if !files.is_empty() { + return Ok(files.len()); + } + + let reader = GLOBAL_P4K_READER.lock().unwrap(); + if reader.is_none() { + return Err(anyhow!("P4K reader not initialized")); + } + + let entries = reader.as_ref().unwrap().entries(); + for entry in entries { + let name = if entry.name.starts_with("\\") { + entry.name.clone() + } else { + format!("\\{}", entry.name.replace("/", "\\")) + }; + files.insert(name, entry.clone()); + } + + Ok(files.len()) +} + +/// 获取文件数量(会触发文件列表加载) +pub async fn p4k_get_file_count() -> Result { + tokio::task::spawn_blocking(|| ensure_files_loaded()).await? } /// 获取所有文件列表 pub async fn p4k_get_all_files() -> Result> { - let files = GLOBAL_P4K_FILES.lock().unwrap(); - let mut result = Vec::with_capacity(files.len()); + tokio::task::spawn_blocking(|| { + ensure_files_loaded()?; + let files = GLOBAL_P4K_FILES.lock().unwrap(); + let mut result = Vec::with_capacity(files.len()); - for (name, entry) in files.iter() { - result.push(P4kFileItem { - name: name.clone(), - is_directory: false, - size: entry.uncompressed_size, - compressed_size: entry.compressed_size, - }); - } - - Ok(result) -} - -/// 获取指定目录下的文件列表 -pub async fn p4k_get_files_in_directory(directory: String) -> Result> { - let files = GLOBAL_P4K_FILES.lock().unwrap(); - let mut result = Vec::new(); - let mut dirs = std::collections::HashSet::new(); - - // 确保目录路径以 \ 开头和结尾 - let dir_path = if !directory.starts_with("\\") { - format!("\\{}", directory) - } else { - directory.clone() - }; - let dir_path = if !dir_path.ends_with("\\") { - format!("{}\\", dir_path) - } else { - dir_path - }; - - for (name, entry) in files.iter() { - if name.starts_with(&dir_path) { - let relative = &name[dir_path.len()..]; - if let Some(slash_pos) = relative.find("\\") { - // 这是一个子目录 - let subdir = &relative[..slash_pos]; - if !dirs.contains(subdir) { - dirs.insert(subdir.to_string()); - result.push(P4kFileItem { - name: format!("{}{}\\", dir_path, subdir), - is_directory: true, - size: 0, - compressed_size: 0, - }); - } - } else { - // 这是一个文件 - result.push(P4kFileItem { - name: name.clone(), - is_directory: false, - size: entry.uncompressed_size, - compressed_size: entry.compressed_size, - }); - } + for (name, entry) in files.iter() { + result.push(P4kFileItem { + name: name.clone(), + is_directory: false, + size: entry.uncompressed_size, + compressed_size: entry.compressed_size, + date_modified: dos_datetime_to_millis(entry.mod_date, entry.mod_time), + }); } - } - // 按目录优先,然后按名称排序 - result.sort_by(|a, b| { - if a.is_directory && !b.is_directory { - std::cmp::Ordering::Less - } else if !a.is_directory && b.is_directory { - std::cmp::Ordering::Greater - } else { - a.name.cmp(&b.name) - } - }); - - Ok(result) + Ok(result) + }) + .await? } /// 提取文件到内存 pub async fn p4k_extract_to_memory(file_path: String) -> Result> { + // 确保文件列表已加载 + tokio::task::spawn_blocking(|| ensure_files_loaded()).await??; + // 规范化路径 let normalized_path = if file_path.starts_with("\\") { file_path.clone() diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index 5ae813a..0dc8575 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -37,7 +37,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueNom, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -737964996; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1801517256; // Section: executor @@ -390,24 +390,20 @@ fn wire__crate__api__unp4k_api__p4k_get_all_files_impl( }, ) } -fn wire__crate__api__unp4k_api__p4k_get_files_in_directory_impl( +fn wire__crate__api__unp4k_api__p4k_get_file_count_impl( port_: flutter_rust_bridge::for_generated::MessagePort, - directory: impl CstDecode, ) { FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "p4k_get_files_in_directory", + debug_name: "p4k_get_file_count", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, }, move || { - let api_directory = directory.cst_decode(); move |context| async move { transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>( (move || async move { - let output_ok = - crate::api::unp4k_api::p4k_get_files_in_directory(api_directory) - .await?; + let output_ok = crate::api::unp4k_api::p4k_get_file_count().await?; Ok(output_ok) })() .await, @@ -713,6 +709,12 @@ impl CstDecode for i32 { self } } +impl CstDecode for i64 { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> i64 { + self + } +} impl CstDecode for i32 { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::http_package::MyHttpVersion { @@ -836,6 +838,13 @@ impl SseDecode for i32 { } } +impl SseDecode for i64 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_i64::().unwrap() + } +} + impl SseDecode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -999,11 +1008,13 @@ impl SseDecode for crate::api::unp4k_api::P4kFileItem { let mut var_isDirectory = ::sse_decode(deserializer); let mut var_size = ::sse_decode(deserializer); let mut var_compressedSize = ::sse_decode(deserializer); + let mut var_dateModified = ::sse_decode(deserializer); return crate::api::unp4k_api::P4kFileItem { name: var_name, is_directory: var_isDirectory, size: var_size, compressed_size: var_compressedSize, + date_modified: var_dateModified, }; } } @@ -1223,6 +1234,7 @@ impl flutter_rust_bridge::IntoDart for crate::api::unp4k_api::P4kFileItem { self.is_directory.into_into_dart().into_dart(), self.size.into_into_dart().into_dart(), self.compressed_size.into_into_dart().into_dart(), + self.date_modified.into_into_dart().into_dart(), ] .into_dart() } @@ -1400,6 +1412,13 @@ impl SseEncode for i32 { } } +impl SseEncode for i64 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_i64::(self).unwrap(); + } +} + impl SseEncode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1550,6 +1569,7 @@ impl SseEncode for crate::api::unp4k_api::P4kFileItem { ::sse_encode(self.is_directory, serializer); ::sse_encode(self.size, serializer); ::sse_encode(self.compressed_size, serializer); + ::sse_encode(self.date_modified, serializer); } } @@ -1809,6 +1829,7 @@ mod io { is_directory: self.is_directory.cst_decode(), size: self.size.cst_decode(), compressed_size: self.compressed_size.cst_decode(), + date_modified: self.date_modified.cst_decode(), } } } @@ -1869,6 +1890,7 @@ mod io { is_directory: Default::default(), size: Default::default(), compressed_size: Default::default(), + date_modified: Default::default(), } } } @@ -2075,11 +2097,10 @@ mod io { } #[unsafe(no_mangle)] - pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_get_files_in_directory( + pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_get_file_count( port_: i64, - directory: *mut wire_cst_list_prim_u_8_strict, ) { - wire__crate__api__unp4k_api__p4k_get_files_in_directory_impl(port_, directory) + wire__crate__api__unp4k_api__p4k_get_file_count_impl(port_) } #[unsafe(no_mangle)] @@ -2317,6 +2338,7 @@ mod io { is_directory: bool, size: u64, compressed_size: u64, + date_modified: i64, } #[repr(C)] #[derive(Clone, Copy)]