feat: update unp4k

This commit is contained in:
xkeyC 2025-12-04 16:06:49 +08:00
parent e3c3986379
commit e1ed30b6e6
14 changed files with 362 additions and 411 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -41,7 +41,7 @@ final class Unp4kCModelProvider
}
}
String _$unp4kCModelHash() => r'fe88d52b11464fdbded606bacbd833c1e908b738';
String _$unp4kCModelHash() => r'a296a499158e78848a698c3fda92c4c88ff039be';
abstract class _$Unp4kCModel extends $Notifier<Unp4kcState> {
Unp4kcState build();

View File

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

View File

@ -47,7 +47,7 @@ final class AdvancedLocalizationUIModelProvider
}
String _$advancedLocalizationUIModelHash() =>
r'c7cca8935ac7df2281e83297b11b6b82d94f7a59';
r'5ff4d8156fbae4dcf69cb3fbcabfb9abda69ffbb';
abstract class _$AdvancedLocalizationUIModel
extends $Notifier<AdvancedLocalizationUIState> {

View File

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

2
rust/Cargo.lock generated
View File

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

View File

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

View File

@ -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<Arc<Mutex<Option<P4kFile>>>> =
static GLOBAL_P4K_FILES: once_cell::sync::Lazy<Arc<Mutex<HashMap<String, P4kEntry>>>> =
once_cell::sync::Lazy::new(|| Arc::new(Mutex::new(HashMap::new())));
/// 打开 P4K 文件
pub async fn p4k_open(p4k_path: String) -> Result<usize> {
/// 打开 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<usize> {
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<usize> {
tokio::task::spawn_blocking(|| ensure_files_loaded()).await?
}
/// 获取所有文件列表
pub async fn p4k_get_all_files() -> Result<Vec<P4kFileItem>> {
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<Vec<P4kFileItem>> {
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<Vec<u8>> {
// 确保文件列表已加载
tokio::task::spawn_blocking(|| ensure_files_loaded()).await??;
// 规范化路径
let normalized_path = if file_path.starts_with("\\") {
file_path.clone()

View File

@ -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<String>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
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<i32> for i32 {
self
}
}
impl CstDecode<i64> for i64 {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> i64 {
self
}
}
impl CstDecode<crate::http_package::MyHttpVersion> 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::<NativeEndian>().unwrap()
}
}
impl SseDecode for Vec<String> {
// 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 = <bool>::sse_decode(deserializer);
let mut var_size = <u64>::sse_decode(deserializer);
let mut var_compressedSize = <u64>::sse_decode(deserializer);
let mut var_dateModified = <i64>::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::<NativeEndian>(self).unwrap();
}
}
impl SseEncode for Vec<String> {
// 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 {
<bool>::sse_encode(self.is_directory, serializer);
<u64>::sse_encode(self.size, serializer);
<u64>::sse_encode(self.compressed_size, serializer);
<i64>::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)]