mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-01-13 11:40:27 +00:00
Merge pull request #160 from StarCitizenToolBox/feat/unp4k_rs
Feat: unp4k rs
This commit is contained in:
commit
bf246e469f
Binary file not shown.
@ -6,10 +6,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
|
||||
class BinaryModuleConf {
|
||||
static const _modules = {
|
||||
"aria2c": "0",
|
||||
"unp4kc": "1",
|
||||
};
|
||||
static const _modules = {"aria2c": "0"};
|
||||
|
||||
static Future extractModule(List<String> modules, String workingDir) async {
|
||||
for (var m in _modules.entries) {
|
||||
@ -18,11 +15,8 @@ class BinaryModuleConf {
|
||||
final version = m.value;
|
||||
final dir = "$workingDir\\$name";
|
||||
final versionFile = File("$dir\\version");
|
||||
if (kReleaseMode &&
|
||||
await versionFile.exists() &&
|
||||
(await versionFile.readAsString()).trim() == version) {
|
||||
dPrint(
|
||||
"BinaryModuleConf.extractModule skip $name version == $version");
|
||||
if (kReleaseMode && await versionFile.exists() && (await versionFile.readAsString()).trim() == version) {
|
||||
dPrint("BinaryModuleConf.extractModule skip $name version == $version");
|
||||
continue;
|
||||
}
|
||||
// write model file
|
||||
|
||||
51
lib/common/rust/api/unp4k_api.dart
Normal file
51
lib/common/rust/api/unp4k_api.dart
Normal file
@ -0,0 +1,51 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
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';
|
||||
|
||||
// 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<Uint8List> p4KExtractToMemory({required String filePath}) =>
|
||||
RustLib.instance.api.crateApiUnp4KApiP4KExtractToMemory(filePath: filePath);
|
||||
|
||||
/// 提取文件到磁盘
|
||||
Future<void> p4KExtractToDisk({
|
||||
required String filePath,
|
||||
required String outputPath,
|
||||
}) => RustLib.instance.api.crateApiUnp4KApiP4KExtractToDisk(
|
||||
filePath: filePath,
|
||||
outputPath: outputPath,
|
||||
);
|
||||
|
||||
/// 关闭 P4K 读取器
|
||||
Future<void> p4KClose() => RustLib.instance.api.crateApiUnp4KApiP4KClose();
|
||||
|
||||
/// P4K 文件项信息
|
||||
@freezed
|
||||
sealed class P4kFileItem with _$P4kFileItem {
|
||||
const factory P4kFileItem({
|
||||
required String name,
|
||||
required bool isDirectory,
|
||||
required BigInt size,
|
||||
required BigInt compressedSize,
|
||||
required PlatformInt64 dateModified,
|
||||
}) = _P4kFileItem;
|
||||
}
|
||||
277
lib/common/rust/api/unp4k_api.freezed.dart
Normal file
277
lib/common/rust/api/unp4k_api.freezed.dart
Normal file
@ -0,0 +1,277 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'unp4k_api.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$P4kFileItem {
|
||||
|
||||
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)
|
||||
@pragma('vm:prefer-inline')
|
||||
$P4kFileItemCopyWith<P4kFileItem> get copyWith => _$P4kFileItemCopyWithImpl<P4kFileItem>(this as P4kFileItem, _$identity);
|
||||
|
||||
|
||||
|
||||
@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)&&(identical(other.dateModified, dateModified) || other.dateModified == dateModified));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,name,isDirectory,size,compressedSize,dateModified);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'P4kFileItem(name: $name, isDirectory: $isDirectory, size: $size, compressedSize: $compressedSize, dateModified: $dateModified)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
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, PlatformInt64 dateModified
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$P4kFileItemCopyWithImpl<$Res>
|
||||
implements $P4kFileItemCopyWith<$Res> {
|
||||
_$P4kFileItemCopyWithImpl(this._self, this._then);
|
||||
|
||||
final P4kFileItem _self;
|
||||
final $Res Function(P4kFileItem) _then;
|
||||
|
||||
/// 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,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,dateModified: null == dateModified ? _self.dateModified : dateModified // ignore: cast_nullable_to_non_nullable
|
||||
as PlatformInt64,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [P4kFileItem].
|
||||
extension P4kFileItemPatterns on P4kFileItem {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _P4kFileItem value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _P4kFileItem() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _P4kFileItem value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _P4kFileItem():
|
||||
return $default(_that);}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _P4kFileItem value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _P4kFileItem() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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,_that.dateModified);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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,_that.dateModified);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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,_that.dateModified);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _P4kFileItem implements P4kFileItem {
|
||||
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.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$P4kFileItemCopyWith<_P4kFileItem> get copyWith => __$P4kFileItemCopyWithImpl<_P4kFileItem>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@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)&&(identical(other.dateModified, dateModified) || other.dateModified == dateModified));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,name,isDirectory,size,compressedSize,dateModified);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'P4kFileItem(name: $name, isDirectory: $isDirectory, size: $size, compressedSize: $compressedSize, dateModified: $dateModified)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$P4kFileItemCopyWith<$Res> implements $P4kFileItemCopyWith<$Res> {
|
||||
factory _$P4kFileItemCopyWith(_P4kFileItem value, $Res Function(_P4kFileItem) _then) = __$P4kFileItemCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String name, bool isDirectory, BigInt size, BigInt compressedSize, PlatformInt64 dateModified
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$P4kFileItemCopyWithImpl<$Res>
|
||||
implements _$P4kFileItemCopyWith<$Res> {
|
||||
__$P4kFileItemCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _P4kFileItem _self;
|
||||
final $Res Function(_P4kFileItem) _then;
|
||||
|
||||
/// 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,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,dateModified: null == dateModified ? _self.dateModified : dateModified // ignore: cast_nullable_to_non_nullable
|
||||
as PlatformInt64,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@ -7,6 +7,7 @@ import 'api/asar_api.dart';
|
||||
import 'api/http_api.dart';
|
||||
import 'api/ort_api.dart';
|
||||
import 'api/rs_process.dart';
|
||||
import 'api/unp4k_api.dart';
|
||||
import 'api/win32_api.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
@ -69,7 +70,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
String get codegenVersion => '2.11.1';
|
||||
|
||||
@override
|
||||
int get rustContentHash => -518970253;
|
||||
int get rustContentHash => 1801517256;
|
||||
|
||||
static const kDefaultExternalLibraryLoaderConfig =
|
||||
ExternalLibraryLoaderConfig(
|
||||
@ -119,6 +120,23 @@ abstract class RustLibApi extends BaseApi {
|
||||
required bool useXnnpack,
|
||||
});
|
||||
|
||||
Future<void> crateApiUnp4KApiP4KClose();
|
||||
|
||||
Future<void> crateApiUnp4KApiP4KExtractToDisk({
|
||||
required String filePath,
|
||||
required String outputPath,
|
||||
});
|
||||
|
||||
Future<Uint8List> crateApiUnp4KApiP4KExtractToMemory({
|
||||
required String filePath,
|
||||
});
|
||||
|
||||
Future<List<P4kFileItem>> crateApiUnp4KApiP4KGetAllFiles();
|
||||
|
||||
Future<BigInt> crateApiUnp4KApiP4KGetFileCount();
|
||||
|
||||
Future<void> crateApiUnp4KApiP4KOpen({required String p4KPath});
|
||||
|
||||
Future<void> crateApiAsarApiRsiLauncherAsarDataWriteMainJs({
|
||||
required RsiLauncherAsarData that,
|
||||
required List<int> content,
|
||||
@ -456,6 +474,154 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
argNames: ["modelPath", "modelKey", "quantizationSuffix", "useXnnpack"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> crateApiUnp4KApiP4KClose() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire.wire__crate__api__unp4k_api__p4k_close(port_);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiUnp4KApiP4KCloseConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiUnp4KApiP4KCloseConstMeta =>
|
||||
const TaskConstMeta(debugName: "p4k_close", argNames: []);
|
||||
|
||||
@override
|
||||
Future<void> crateApiUnp4KApiP4KExtractToDisk({
|
||||
required String filePath,
|
||||
required String outputPath,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(filePath);
|
||||
var arg1 = cst_encode_String(outputPath);
|
||||
return wire.wire__crate__api__unp4k_api__p4k_extract_to_disk(
|
||||
port_,
|
||||
arg0,
|
||||
arg1,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiUnp4KApiP4KExtractToDiskConstMeta,
|
||||
argValues: [filePath, outputPath],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiUnp4KApiP4KExtractToDiskConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "p4k_extract_to_disk",
|
||||
argNames: ["filePath", "outputPath"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<Uint8List> crateApiUnp4KApiP4KExtractToMemory({
|
||||
required String filePath,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(filePath);
|
||||
return wire.wire__crate__api__unp4k_api__p4k_extract_to_memory(
|
||||
port_,
|
||||
arg0,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_list_prim_u_8_strict,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiUnp4KApiP4KExtractToMemoryConstMeta,
|
||||
argValues: [filePath],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiUnp4KApiP4KExtractToMemoryConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "p4k_extract_to_memory",
|
||||
argNames: ["filePath"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<List<P4kFileItem>> crateApiUnp4KApiP4KGetAllFiles() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire.wire__crate__api__unp4k_api__p4k_get_all_files(port_);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_list_p_4_k_file_item,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiUnp4KApiP4KGetAllFilesConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiUnp4KApiP4KGetAllFilesConstMeta =>
|
||||
const TaskConstMeta(debugName: "p4k_get_all_files", argNames: []);
|
||||
|
||||
@override
|
||||
Future<BigInt> crateApiUnp4KApiP4KGetFileCount() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire.wire__crate__api__unp4k_api__p4k_get_file_count(port_);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_usize,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiUnp4KApiP4KGetFileCountConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiUnp4KApiP4KGetFileCountConstMeta =>
|
||||
const TaskConstMeta(debugName: "p4k_get_file_count", argNames: []);
|
||||
|
||||
@override
|
||||
Future<void> crateApiUnp4KApiP4KOpen({required String p4KPath}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(p4KPath);
|
||||
return wire.wire__crate__api__unp4k_api__p4k_open(port_, arg0);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiUnp4KApiP4KOpenConstMeta,
|
||||
argValues: [p4KPath],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiUnp4KApiP4KOpenConstMeta =>
|
||||
const TaskConstMeta(debugName: "p4k_open", argNames: ["p4KPath"]);
|
||||
|
||||
@override
|
||||
Future<void> crateApiAsarApiRsiLauncherAsarDataWriteMainJs({
|
||||
required RsiLauncherAsarData that,
|
||||
@ -814,12 +980,24 @@ 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
|
||||
return (raw as List<dynamic>).map(dco_decode_String).toList();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<P4kFileItem> dco_decode_list_p_4_k_file_item(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return (raw as List<dynamic>).map(dco_decode_p_4_k_file_item).toList();
|
||||
}
|
||||
|
||||
@protected
|
||||
List<int> dco_decode_list_prim_u_8_loose(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@ -886,6 +1064,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return raw == null ? null : dco_decode_list_prim_u_8_strict(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
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 != 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]),
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
ProcessInfo dco_decode_process_info(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
@ -988,6 +1181,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return;
|
||||
}
|
||||
|
||||
@protected
|
||||
BigInt dco_decode_usize(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return dcoDecodeU64(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@ -1052,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
|
||||
@ -1064,6 +1269,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
return ans_;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<P4kFileItem> sse_decode_list_p_4_k_file_item(
|
||||
SseDeserializer deserializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
||||
var len_ = sse_decode_i_32(deserializer);
|
||||
var ans_ = <P4kFileItem>[];
|
||||
for (var idx_ = 0; idx_ < len_; ++idx_) {
|
||||
ans_.add(sse_decode_p_4_k_file_item(deserializer));
|
||||
}
|
||||
return ans_;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@ -1175,6 +1394,23 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
P4kFileItem sse_decode_p_4_k_file_item(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
var var_name = sse_decode_String(deserializer);
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
ProcessInfo sse_decode_process_info(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@ -1283,6 +1519,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
}
|
||||
|
||||
@protected
|
||||
BigInt sse_decode_usize(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getBigUint64();
|
||||
}
|
||||
|
||||
@protected
|
||||
bool cst_encode_bool(bool raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
@ -1414,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
|
||||
@ -1423,6 +1671,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_p_4_k_file_item(
|
||||
List<P4kFileItem> self,
|
||||
SseSerializer serializer,
|
||||
) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_i_32(self.length, serializer);
|
||||
for (final item in self) {
|
||||
sse_encode_p_4_k_file_item(item, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_loose(
|
||||
List<int> self,
|
||||
@ -1540,6 +1800,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_p_4_k_file_item(P4kFileItem self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_String(self.name, serializer);
|
||||
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
|
||||
void sse_encode_process_info(ProcessInfo self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
@ -1632,4 +1902,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
void sse_encode_unit(void self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_usize(BigInt self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
serializer.buffer.putBigUint64(self);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import 'api/asar_api.dart';
|
||||
import 'api/http_api.dart';
|
||||
import 'api/ort_api.dart';
|
||||
import 'api/rs_process.dart';
|
||||
import 'api/unp4k_api.dart';
|
||||
import 'api/win32_api.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
@ -53,9 +54,15 @@ 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);
|
||||
|
||||
@protected
|
||||
List<P4kFileItem> dco_decode_list_p_4_k_file_item(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<int> dco_decode_list_prim_u_8_loose(dynamic raw);
|
||||
|
||||
@ -89,6 +96,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw);
|
||||
|
||||
@protected
|
||||
P4kFileItem dco_decode_p_4_k_file_item(dynamic raw);
|
||||
|
||||
@protected
|
||||
ProcessInfo dco_decode_process_info(dynamic raw);
|
||||
|
||||
@ -122,6 +132,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void dco_decode_unit(dynamic raw);
|
||||
|
||||
@protected
|
||||
BigInt dco_decode_usize(dynamic raw);
|
||||
|
||||
@protected
|
||||
AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer);
|
||||
|
||||
@ -156,9 +169,17 @@ 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);
|
||||
|
||||
@protected
|
||||
List<P4kFileItem> sse_decode_list_p_4_k_file_item(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
|
||||
|
||||
@ -196,6 +217,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
P4kFileItem sse_decode_p_4_k_file_item(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
ProcessInfo sse_decode_process_info(SseDeserializer deserializer);
|
||||
|
||||
@ -237,6 +261,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
void sse_decode_unit(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
BigInt sse_decode_usize(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_encode_AnyhowException(
|
||||
AnyhowException raw,
|
||||
@ -297,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
|
||||
@ -307,6 +340,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
return ans;
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_p_4_k_file_item> cst_encode_list_p_4_k_file_item(
|
||||
List<P4kFileItem> raw,
|
||||
) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
final ans = wire.cst_new_list_p_4_k_file_item(raw.length);
|
||||
for (var i = 0; i < raw.length; ++i) {
|
||||
cst_api_fill_to_wire_p_4_k_file_item(raw[i], ans.ref.ptr[i]);
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> cst_encode_list_prim_u_8_loose(
|
||||
List<int> raw,
|
||||
@ -390,6 +435,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
return raw.toSigned(64).toInt();
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_usize(BigInt raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw.toSigned(64).toInt();
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_box_autoadd_rsi_launcher_asar_data(
|
||||
RsiLauncherAsarData apiObj,
|
||||
@ -398,6 +449,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
cst_api_fill_to_wire_rsi_launcher_asar_data(apiObj, wireObj.ref);
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_p_4_k_file_item(
|
||||
P4kFileItem apiObj,
|
||||
wire_cst_p_4_k_file_item wireObj,
|
||||
) {
|
||||
wireObj.name = cst_encode_String(apiObj.name);
|
||||
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
|
||||
void cst_api_fill_to_wire_process_info(
|
||||
ProcessInfo apiObj,
|
||||
@ -521,9 +584,18 @@ 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);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_p_4_k_file_item(
|
||||
List<P4kFileItem> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
|
||||
|
||||
@ -572,6 +644,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_p_4_k_file_item(P4kFileItem self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_process_info(ProcessInfo self, SseSerializer serializer);
|
||||
|
||||
@ -619,6 +694,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
|
||||
@protected
|
||||
void sse_encode_unit(void self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_usize(BigInt self, SseSerializer serializer);
|
||||
}
|
||||
|
||||
// Section: wire_class
|
||||
@ -917,6 +995,125 @@ class RustLibWire implements BaseWire {
|
||||
)
|
||||
>();
|
||||
|
||||
void wire__crate__api__unp4k_api__p4k_close(int port_) {
|
||||
return _wire__crate__api__unp4k_api__p4k_close(port_);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__unp4k_api__p4k_closePtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_close',
|
||||
);
|
||||
late final _wire__crate__api__unp4k_api__p4k_close =
|
||||
_wire__crate__api__unp4k_api__p4k_closePtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__unp4k_api__p4k_extract_to_disk(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> file_path,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> output_path,
|
||||
) {
|
||||
return _wire__crate__api__unp4k_api__p4k_extract_to_disk(
|
||||
port_,
|
||||
file_path,
|
||||
output_path,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__unp4k_api__p4k_extract_to_diskPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_extract_to_disk',
|
||||
);
|
||||
late final _wire__crate__api__unp4k_api__p4k_extract_to_disk =
|
||||
_wire__crate__api__unp4k_api__p4k_extract_to_diskPtr
|
||||
.asFunction<
|
||||
void Function(
|
||||
int,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>();
|
||||
|
||||
void wire__crate__api__unp4k_api__p4k_extract_to_memory(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> file_path,
|
||||
) {
|
||||
return _wire__crate__api__unp4k_api__p4k_extract_to_memory(
|
||||
port_,
|
||||
file_path,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__unp4k_api__p4k_extract_to_memoryPtr =
|
||||
_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_extract_to_memory',
|
||||
);
|
||||
late final _wire__crate__api__unp4k_api__p4k_extract_to_memory =
|
||||
_wire__crate__api__unp4k_api__p4k_extract_to_memoryPtr
|
||||
.asFunction<
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)
|
||||
>();
|
||||
|
||||
void wire__crate__api__unp4k_api__p4k_get_all_files(int port_) {
|
||||
return _wire__crate__api__unp4k_api__p4k_get_all_files(port_);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__unp4k_api__p4k_get_all_filesPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_get_all_files',
|
||||
);
|
||||
late final _wire__crate__api__unp4k_api__p4k_get_all_files =
|
||||
_wire__crate__api__unp4k_api__p4k_get_all_filesPtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
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_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_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_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> p4k_path,
|
||||
) {
|
||||
return _wire__crate__api__unp4k_api__p4k_open(port_, p4k_path);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__unp4k_api__p4k_openPtr =
|
||||
_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_open');
|
||||
late final _wire__crate__api__unp4k_api__p4k_open =
|
||||
_wire__crate__api__unp4k_api__p4k_openPtr
|
||||
.asFunction<
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)
|
||||
>();
|
||||
|
||||
void wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_rsi_launcher_asar_data> that,
|
||||
@ -1247,6 +1444,21 @@ class RustLibWire implements BaseWire {
|
||||
late final _cst_new_list_String = _cst_new_list_StringPtr
|
||||
.asFunction<ffi.Pointer<wire_cst_list_String> Function(int)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_p_4_k_file_item> cst_new_list_p_4_k_file_item(
|
||||
int len,
|
||||
) {
|
||||
return _cst_new_list_p_4_k_file_item(len);
|
||||
}
|
||||
|
||||
late final _cst_new_list_p_4_k_file_itemPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Pointer<wire_cst_list_p_4_k_file_item> Function(ffi.Int32)
|
||||
>
|
||||
>('frbgen_starcitizen_doctor_cst_new_list_p_4_k_file_item');
|
||||
late final _cst_new_list_p_4_k_file_item = _cst_new_list_p_4_k_file_itemPtr
|
||||
.asFunction<ffi.Pointer<wire_cst_list_p_4_k_file_item> Function(int)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> cst_new_list_prim_u_8_loose(
|
||||
int len,
|
||||
) {
|
||||
@ -1370,6 +1582,29 @@ final class wire_cst_list_prim_u_8_loose extends ffi.Struct {
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_p_4_k_file_item extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> name;
|
||||
|
||||
@ffi.Bool()
|
||||
external bool is_directory;
|
||||
|
||||
@ffi.Uint64()
|
||||
external int size;
|
||||
|
||||
@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 {
|
||||
external ffi.Pointer<wire_cst_p_4_k_file_item> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_process_info extends ffi.Struct {
|
||||
@ffi.Uint32()
|
||||
external int pid;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,18 +261,32 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
static String m103(v0) => "Launcher internal version information: ${v0}";
|
||||
|
||||
static String m104(v0) => "Opening file: ${v0}";
|
||||
static String m104(v0) => "Export Selected (${v0})";
|
||||
|
||||
static String m105(v0, v1) =>
|
||||
static String m105(v0) => "Extraction failed: ${v0}";
|
||||
|
||||
static String m106(v0) => "Extraction complete: ${v0}";
|
||||
|
||||
static String m107(v0) => "Extracting: ${v0}";
|
||||
|
||||
static String m108(v0) => "Extraction completed, ${v0} files total";
|
||||
|
||||
static String m109(v0) => "Current file: ${v0}";
|
||||
|
||||
static String m110(v0, v1) => "Extracting (${v0}/${v1})";
|
||||
|
||||
static String m111(v0) => "Opening file: ${v0}";
|
||||
|
||||
static String m112(v0, v1) =>
|
||||
"Loading complete: ${v0} files, time taken: ${v1} ms";
|
||||
|
||||
static String m106(v0) => "Reading file: ${v0}...";
|
||||
static String m113(v0) => "Reading file: ${v0}...";
|
||||
|
||||
static String m107(v0, v1) => "Processing files (${v0}/${v1})...";
|
||||
static String m114(v0, v1) => "Processing files (${v0}/${v1})...";
|
||||
|
||||
static String m108(v0) => "Unknown file type\n${v0}";
|
||||
static String m115(v0) => "Unknown file type\n${v0}";
|
||||
|
||||
static String m109(v0) => "P4K Viewer -> ${v0}";
|
||||
static String m116(v0) => "P4K Viewer -> ${v0}";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@ -2184,6 +2198,33 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tools_rsi_launcher_enhance_working_msg2": MessageLookupByLibrary.simpleMessage(
|
||||
"Installing patch, this will take some time depending on your computer\'s performance...",
|
||||
),
|
||||
"tools_unp4k_action_cancel_multi_select":
|
||||
MessageLookupByLibrary.simpleMessage("Cancel Multi-Select"),
|
||||
"tools_unp4k_action_deselect_all": MessageLookupByLibrary.simpleMessage(
|
||||
"Deselect All",
|
||||
),
|
||||
"tools_unp4k_action_export_selected": m104,
|
||||
"tools_unp4k_action_extract_failed": m105,
|
||||
"tools_unp4k_action_extract_success": m106,
|
||||
"tools_unp4k_action_extracting": m107,
|
||||
"tools_unp4k_action_multi_select": MessageLookupByLibrary.simpleMessage(
|
||||
"Multi-Select",
|
||||
),
|
||||
"tools_unp4k_action_save_as": MessageLookupByLibrary.simpleMessage(
|
||||
"Save As...",
|
||||
),
|
||||
"tools_unp4k_action_select_all": MessageLookupByLibrary.simpleMessage(
|
||||
"Select All",
|
||||
),
|
||||
"tools_unp4k_extract_cancelled": MessageLookupByLibrary.simpleMessage(
|
||||
"Extraction cancelled",
|
||||
),
|
||||
"tools_unp4k_extract_completed": m108,
|
||||
"tools_unp4k_extract_current_file": m109,
|
||||
"tools_unp4k_extract_dialog_title": MessageLookupByLibrary.simpleMessage(
|
||||
"Extract Files",
|
||||
),
|
||||
"tools_unp4k_extract_progress": m110,
|
||||
"tools_unp4k_missing_runtime": MessageLookupByLibrary.simpleMessage(
|
||||
"Missing Runtime",
|
||||
),
|
||||
@ -2195,18 +2236,42 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage(
|
||||
"Initializing...",
|
||||
),
|
||||
"tools_unp4k_msg_open_file": m104,
|
||||
"tools_unp4k_msg_read_completed": m105,
|
||||
"tools_unp4k_msg_read_file": m106,
|
||||
"tools_unp4k_msg_open_file": m111,
|
||||
"tools_unp4k_msg_read_completed": m112,
|
||||
"tools_unp4k_msg_read_file": m113,
|
||||
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
||||
"Reading P4K file...",
|
||||
),
|
||||
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
||||
"Processing files...",
|
||||
),
|
||||
"tools_unp4k_msg_reading3": m107,
|
||||
"tools_unp4k_msg_unknown_file_type": m108,
|
||||
"tools_unp4k_title": m109,
|
||||
"tools_unp4k_msg_reading3": m114,
|
||||
"tools_unp4k_msg_unknown_file_type": m115,
|
||||
"tools_unp4k_search_no_result": MessageLookupByLibrary.simpleMessage(
|
||||
"No matching files found",
|
||||
),
|
||||
"tools_unp4k_search_placeholder": MessageLookupByLibrary.simpleMessage(
|
||||
"Search files (supports regex)...",
|
||||
),
|
||||
"tools_unp4k_searching": MessageLookupByLibrary.simpleMessage(
|
||||
"Searching...",
|
||||
),
|
||||
"tools_unp4k_sort_date_asc": MessageLookupByLibrary.simpleMessage(
|
||||
"Older First",
|
||||
),
|
||||
"tools_unp4k_sort_date_desc": MessageLookupByLibrary.simpleMessage(
|
||||
"Newer First",
|
||||
),
|
||||
"tools_unp4k_sort_default": MessageLookupByLibrary.simpleMessage(
|
||||
"Default Sort",
|
||||
),
|
||||
"tools_unp4k_sort_size_asc": MessageLookupByLibrary.simpleMessage(
|
||||
"Smaller First",
|
||||
),
|
||||
"tools_unp4k_sort_size_desc": MessageLookupByLibrary.simpleMessage(
|
||||
"Larger First",
|
||||
),
|
||||
"tools_unp4k_title": m116,
|
||||
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage(
|
||||
"Click file to preview",
|
||||
),
|
||||
|
||||
@ -242,17 +242,17 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
static String m103(v0) => "ランチャー内部バージョン情報:${v0}";
|
||||
|
||||
static String m104(v0) => "ファイルを開く:${v0}";
|
||||
static String m111(v0) => "ファイルを開く:${v0}";
|
||||
|
||||
static String m105(v0, v1) => "読み込み完了:${v0}ファイル、所要時間:${v1} ms";
|
||||
static String m112(v0, v1) => "読み込み完了:${v0}ファイル、所要時間:${v1} ms";
|
||||
|
||||
static String m106(v0) => "ファイルを読み込み中:${v0}...";
|
||||
static String m113(v0) => "ファイルを読み込み中:${v0}...";
|
||||
|
||||
static String m107(v0, v1) => "ファイルを処理中(${v0}/${v1})...";
|
||||
static String m114(v0, v1) => "ファイルを処理中(${v0}/${v1})...";
|
||||
|
||||
static String m108(v0) => "不明なファイルタイプ\n${v0}";
|
||||
static String m115(v0) => "不明なファイルタイプ\n${v0}";
|
||||
|
||||
static String m109(v0) => "P4Kビューア -> ${v0}";
|
||||
static String m116(v0) => "P4Kビューア -> ${v0}";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@ -1971,18 +1971,18 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"この機能を使用するには.NET8ランタイムをインストールする必要があります。下のボタンをクリックしてダウンロードしてインストールし、インストールが成功したらこのページを再度開いて使用を続行してください。",
|
||||
),
|
||||
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage("初期化中..."),
|
||||
"tools_unp4k_msg_open_file": m104,
|
||||
"tools_unp4k_msg_read_completed": m105,
|
||||
"tools_unp4k_msg_read_file": m106,
|
||||
"tools_unp4k_msg_open_file": m111,
|
||||
"tools_unp4k_msg_read_completed": m112,
|
||||
"tools_unp4k_msg_read_file": m113,
|
||||
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
||||
"P4Kファイルを読み込み中...",
|
||||
),
|
||||
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
||||
"ファイルを処理中...",
|
||||
),
|
||||
"tools_unp4k_msg_reading3": m107,
|
||||
"tools_unp4k_msg_unknown_file_type": m108,
|
||||
"tools_unp4k_title": m109,
|
||||
"tools_unp4k_msg_reading3": m114,
|
||||
"tools_unp4k_msg_unknown_file_type": m115,
|
||||
"tools_unp4k_title": m116,
|
||||
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage(
|
||||
"プレビューするファイルをクリック",
|
||||
),
|
||||
|
||||
@ -256,18 +256,18 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
static String m103(v0) => "Внутренняя версия лаунчера: ${v0}";
|
||||
|
||||
static String m104(v0) => "Открытие файла: ${v0}";
|
||||
static String m111(v0) => "Открытие файла: ${v0}";
|
||||
|
||||
static String m105(v0, v1) =>
|
||||
static String m112(v0, v1) =>
|
||||
"Загрузка завершена: ${v0} файлов, время: ${v1} мс";
|
||||
|
||||
static String m106(v0) => "Чтение файла: ${v0}...";
|
||||
static String m113(v0) => "Чтение файла: ${v0}...";
|
||||
|
||||
static String m107(v0, v1) => "Обработка файлов (${v0}/${v1})...";
|
||||
static String m114(v0, v1) => "Обработка файлов (${v0}/${v1})...";
|
||||
|
||||
static String m108(v0) => "Неизвестный тип файла\n${v0}";
|
||||
static String m115(v0) => "Неизвестный тип файла\n${v0}";
|
||||
|
||||
static String m109(v0) => "Просмотрщик P4K -> ${v0}";
|
||||
static String m116(v0) => "Просмотрщик P4K -> ${v0}";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@ -2224,18 +2224,18 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage(
|
||||
"Инициализация...",
|
||||
),
|
||||
"tools_unp4k_msg_open_file": m104,
|
||||
"tools_unp4k_msg_read_completed": m105,
|
||||
"tools_unp4k_msg_read_file": m106,
|
||||
"tools_unp4k_msg_open_file": m111,
|
||||
"tools_unp4k_msg_read_completed": m112,
|
||||
"tools_unp4k_msg_read_file": m113,
|
||||
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
||||
"Чтение файла P4K...",
|
||||
),
|
||||
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
||||
"Обработка файлов...",
|
||||
),
|
||||
"tools_unp4k_msg_reading3": m107,
|
||||
"tools_unp4k_msg_unknown_file_type": m108,
|
||||
"tools_unp4k_title": m109,
|
||||
"tools_unp4k_msg_reading3": m114,
|
||||
"tools_unp4k_msg_unknown_file_type": m115,
|
||||
"tools_unp4k_title": m116,
|
||||
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage(
|
||||
"Нажмите на файл для предварительного просмотра",
|
||||
),
|
||||
|
||||
@ -240,17 +240,31 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
static String m103(v0) => "启动器内部版本信息:${v0}";
|
||||
|
||||
static String m104(v0) => "打开文件:${v0}";
|
||||
static String m104(v0) => "导出选中项 (${v0})";
|
||||
|
||||
static String m105(v0, v1) => "加载完毕:${v0} 个文件,用时:${v1} ms";
|
||||
static String m105(v0) => "提取失败:${v0}";
|
||||
|
||||
static String m106(v0) => "读取文件:${v0} ...";
|
||||
static String m106(v0) => "提取完成:${v0}";
|
||||
|
||||
static String m107(v0, v1) => "正在处理文件 (${v0}/${v1}) ...";
|
||||
static String m107(v0) => "正在提取:${v0}";
|
||||
|
||||
static String m108(v0) => "未知文件类型\n${v0}";
|
||||
static String m108(v0) => "提取完成,共 ${v0} 个文件";
|
||||
|
||||
static String m109(v0) => "P4K 查看器 -> ${v0}";
|
||||
static String m109(v0) => "当前文件:${v0}";
|
||||
|
||||
static String m110(v0, v1) => "正在提取 (${v0}/${v1})";
|
||||
|
||||
static String m111(v0) => "打开文件:${v0}";
|
||||
|
||||
static String m112(v0, v1) => "加载完毕:${v0} 个文件,用时:${v1} ms";
|
||||
|
||||
static String m113(v0) => "读取文件:${v0} ...";
|
||||
|
||||
static String m114(v0, v1) => "正在处理文件 (${v0}/${v1}) ...";
|
||||
|
||||
static String m115(v0) => "未知文件类型\n${v0}";
|
||||
|
||||
static String m116(v0) => "P4K 查看器 -> ${v0}";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@ -1849,6 +1863,31 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("生成补丁 ..."),
|
||||
"tools_rsi_launcher_enhance_working_msg2":
|
||||
MessageLookupByLibrary.simpleMessage("安装补丁,这需要一点时间,取决于您的计算机性能 ..."),
|
||||
"tools_unp4k_action_cancel_multi_select":
|
||||
MessageLookupByLibrary.simpleMessage("取消多选"),
|
||||
"tools_unp4k_action_deselect_all": MessageLookupByLibrary.simpleMessage(
|
||||
"取消全选",
|
||||
),
|
||||
"tools_unp4k_action_export_selected": m104,
|
||||
"tools_unp4k_action_extract_failed": m105,
|
||||
"tools_unp4k_action_extract_success": m106,
|
||||
"tools_unp4k_action_extracting": m107,
|
||||
"tools_unp4k_action_multi_select": MessageLookupByLibrary.simpleMessage(
|
||||
"多选",
|
||||
),
|
||||
"tools_unp4k_action_save_as": MessageLookupByLibrary.simpleMessage(
|
||||
"另存为...",
|
||||
),
|
||||
"tools_unp4k_action_select_all": MessageLookupByLibrary.simpleMessage("全选"),
|
||||
"tools_unp4k_extract_cancelled": MessageLookupByLibrary.simpleMessage(
|
||||
"提取已取消",
|
||||
),
|
||||
"tools_unp4k_extract_completed": m108,
|
||||
"tools_unp4k_extract_current_file": m109,
|
||||
"tools_unp4k_extract_dialog_title": MessageLookupByLibrary.simpleMessage(
|
||||
"提取文件",
|
||||
),
|
||||
"tools_unp4k_extract_progress": m110,
|
||||
"tools_unp4k_missing_runtime": MessageLookupByLibrary.simpleMessage(
|
||||
"缺少运行库",
|
||||
),
|
||||
@ -1858,18 +1897,30 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"使用此功能需安装 .NET8 运行库,请点击下方按钮下载安装,安装成功后重新打开此页面即可继续使用。",
|
||||
),
|
||||
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage("初始化中..."),
|
||||
"tools_unp4k_msg_open_file": m104,
|
||||
"tools_unp4k_msg_read_completed": m105,
|
||||
"tools_unp4k_msg_read_file": m106,
|
||||
"tools_unp4k_msg_open_file": m111,
|
||||
"tools_unp4k_msg_read_completed": m112,
|
||||
"tools_unp4k_msg_read_file": m113,
|
||||
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
||||
"正在读取P4K 文件 ...",
|
||||
),
|
||||
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
||||
"正在处理文件 ...",
|
||||
),
|
||||
"tools_unp4k_msg_reading3": m107,
|
||||
"tools_unp4k_msg_unknown_file_type": m108,
|
||||
"tools_unp4k_title": m109,
|
||||
"tools_unp4k_msg_reading3": m114,
|
||||
"tools_unp4k_msg_unknown_file_type": m115,
|
||||
"tools_unp4k_search_no_result": MessageLookupByLibrary.simpleMessage(
|
||||
"未找到匹配文件",
|
||||
),
|
||||
"tools_unp4k_search_placeholder": MessageLookupByLibrary.simpleMessage(
|
||||
"搜索文件(支持正则)...",
|
||||
),
|
||||
"tools_unp4k_searching": MessageLookupByLibrary.simpleMessage("正在搜索..."),
|
||||
"tools_unp4k_sort_date_asc": MessageLookupByLibrary.simpleMessage("旧文件优先"),
|
||||
"tools_unp4k_sort_date_desc": MessageLookupByLibrary.simpleMessage("新文件优先"),
|
||||
"tools_unp4k_sort_default": MessageLookupByLibrary.simpleMessage("默认排序"),
|
||||
"tools_unp4k_sort_size_asc": MessageLookupByLibrary.simpleMessage("小文件优先"),
|
||||
"tools_unp4k_sort_size_desc": MessageLookupByLibrary.simpleMessage("大文件优先"),
|
||||
"tools_unp4k_title": m116,
|
||||
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage("单击文件以预览"),
|
||||
"tools_vehicle_sorting_info": MessageLookupByLibrary.simpleMessage(
|
||||
"将左侧载具拖动到右侧列表中,这将会为载具名称增加 001、002 .. 等前缀,方便您在游戏内 UI 快速定位载具。在右侧列表上下拖动可以调整载具的顺序。",
|
||||
|
||||
@ -236,17 +236,17 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
static String m103(v0) => "啟動器內部版本資訊:${v0}";
|
||||
|
||||
static String m104(v0) => "打開文件:${v0}";
|
||||
static String m111(v0) => "打開文件:${v0}";
|
||||
|
||||
static String m105(v0, v1) => "載入完畢:${v0} 個文件,用時:${v1} ms";
|
||||
static String m112(v0, v1) => "載入完畢:${v0} 個文件,用時:${v1} ms";
|
||||
|
||||
static String m106(v0) => "讀取文件:${v0} ...";
|
||||
static String m113(v0) => "讀取文件:${v0} ...";
|
||||
|
||||
static String m107(v0, v1) => "正在處理文件 (${v0}/${v1}) ...";
|
||||
static String m114(v0, v1) => "正在處理文件 (${v0}/${v1}) ...";
|
||||
|
||||
static String m108(v0) => "未知文件類型\n${v0}";
|
||||
static String m115(v0) => "未知文件類型\n${v0}";
|
||||
|
||||
static String m109(v0) => "P4K 查看器 -> ${v0}";
|
||||
static String m116(v0) => "P4K 查看器 -> ${v0}";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@ -1849,18 +1849,18 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"使用此功能需安裝 .NET8 運行庫,請點擊下方按鈕下載安裝,安裝成功後重新打開此頁面即可繼續使用。",
|
||||
),
|
||||
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage("初始化中..."),
|
||||
"tools_unp4k_msg_open_file": m104,
|
||||
"tools_unp4k_msg_read_completed": m105,
|
||||
"tools_unp4k_msg_read_file": m106,
|
||||
"tools_unp4k_msg_open_file": m111,
|
||||
"tools_unp4k_msg_read_completed": m112,
|
||||
"tools_unp4k_msg_read_file": m113,
|
||||
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
||||
"正在讀取P4K 文件 ...",
|
||||
),
|
||||
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
||||
"正在處理文件 ...",
|
||||
),
|
||||
"tools_unp4k_msg_reading3": m107,
|
||||
"tools_unp4k_msg_unknown_file_type": m108,
|
||||
"tools_unp4k_title": m109,
|
||||
"tools_unp4k_msg_reading3": m114,
|
||||
"tools_unp4k_msg_unknown_file_type": m115,
|
||||
"tools_unp4k_title": m116,
|
||||
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage("單擊文件以預覽"),
|
||||
"tools_vehicle_sorting_info": MessageLookupByLibrary.simpleMessage(
|
||||
"將左側載具拖動到右側列表中,這將會為載具名稱增加 001、002 .. 等前綴,方便您在遊戲內 UI 快速定位載具。在右側列表上下拖動可以調整載具的順序。",
|
||||
|
||||
@ -28,9 +28,10 @@ class S {
|
||||
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
|
||||
|
||||
static Future<S> load(Locale locale) {
|
||||
final name = (locale.countryCode?.isEmpty ?? false)
|
||||
? locale.languageCode
|
||||
: locale.toString();
|
||||
final name =
|
||||
(locale.countryCode?.isEmpty ?? false)
|
||||
? locale.languageCode
|
||||
: locale.toString();
|
||||
final localeName = Intl.canonicalizedLocale(name);
|
||||
return initializeMessages(localeName).then((_) {
|
||||
Intl.defaultLocale = localeName;
|
||||
@ -7901,6 +7902,226 @@ class S {
|
||||
args: [v0],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Search files (supports regex)...`
|
||||
String get tools_unp4k_search_placeholder {
|
||||
return Intl.message(
|
||||
'Search files (supports regex)...',
|
||||
name: 'tools_unp4k_search_placeholder',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Default Sort`
|
||||
String get tools_unp4k_sort_default {
|
||||
return Intl.message(
|
||||
'Default Sort',
|
||||
name: 'tools_unp4k_sort_default',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Smaller First`
|
||||
String get tools_unp4k_sort_size_asc {
|
||||
return Intl.message(
|
||||
'Smaller First',
|
||||
name: 'tools_unp4k_sort_size_asc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Larger First`
|
||||
String get tools_unp4k_sort_size_desc {
|
||||
return Intl.message(
|
||||
'Larger First',
|
||||
name: 'tools_unp4k_sort_size_desc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Older First`
|
||||
String get tools_unp4k_sort_date_asc {
|
||||
return Intl.message(
|
||||
'Older First',
|
||||
name: 'tools_unp4k_sort_date_asc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Newer First`
|
||||
String get tools_unp4k_sort_date_desc {
|
||||
return Intl.message(
|
||||
'Newer First',
|
||||
name: 'tools_unp4k_sort_date_desc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Save As...`
|
||||
String get tools_unp4k_action_save_as {
|
||||
return Intl.message(
|
||||
'Save As...',
|
||||
name: 'tools_unp4k_action_save_as',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Extracting: {v0}`
|
||||
String tools_unp4k_action_extracting(Object v0) {
|
||||
return Intl.message(
|
||||
'Extracting: $v0',
|
||||
name: 'tools_unp4k_action_extracting',
|
||||
desc: '',
|
||||
args: [v0],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Extraction complete: {v0}`
|
||||
String tools_unp4k_action_extract_success(Object v0) {
|
||||
return Intl.message(
|
||||
'Extraction complete: $v0',
|
||||
name: 'tools_unp4k_action_extract_success',
|
||||
desc: '',
|
||||
args: [v0],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Extraction failed: {v0}`
|
||||
String tools_unp4k_action_extract_failed(Object v0) {
|
||||
return Intl.message(
|
||||
'Extraction failed: $v0',
|
||||
name: 'tools_unp4k_action_extract_failed',
|
||||
desc: '',
|
||||
args: [v0],
|
||||
);
|
||||
}
|
||||
|
||||
/// `No matching files found`
|
||||
String get tools_unp4k_search_no_result {
|
||||
return Intl.message(
|
||||
'No matching files found',
|
||||
name: 'tools_unp4k_search_no_result',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Searching...`
|
||||
String get tools_unp4k_searching {
|
||||
return Intl.message(
|
||||
'Searching...',
|
||||
name: 'tools_unp4k_searching',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Extract Files`
|
||||
String get tools_unp4k_extract_dialog_title {
|
||||
return Intl.message(
|
||||
'Extract Files',
|
||||
name: 'tools_unp4k_extract_dialog_title',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Extracting ({v0}/{v1})`
|
||||
String tools_unp4k_extract_progress(Object v0, Object v1) {
|
||||
return Intl.message(
|
||||
'Extracting ($v0/$v1)',
|
||||
name: 'tools_unp4k_extract_progress',
|
||||
desc: '',
|
||||
args: [v0, v1],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Current file: {v0}`
|
||||
String tools_unp4k_extract_current_file(Object v0) {
|
||||
return Intl.message(
|
||||
'Current file: $v0',
|
||||
name: 'tools_unp4k_extract_current_file',
|
||||
desc: '',
|
||||
args: [v0],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Extraction cancelled`
|
||||
String get tools_unp4k_extract_cancelled {
|
||||
return Intl.message(
|
||||
'Extraction cancelled',
|
||||
name: 'tools_unp4k_extract_cancelled',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Extraction completed, {v0} files total`
|
||||
String tools_unp4k_extract_completed(Object v0) {
|
||||
return Intl.message(
|
||||
'Extraction completed, $v0 files total',
|
||||
name: 'tools_unp4k_extract_completed',
|
||||
desc: '',
|
||||
args: [v0],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Multi-Select`
|
||||
String get tools_unp4k_action_multi_select {
|
||||
return Intl.message(
|
||||
'Multi-Select',
|
||||
name: 'tools_unp4k_action_multi_select',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Export Selected ({v0})`
|
||||
String tools_unp4k_action_export_selected(Object v0) {
|
||||
return Intl.message(
|
||||
'Export Selected ($v0)',
|
||||
name: 'tools_unp4k_action_export_selected',
|
||||
desc: '',
|
||||
args: [v0],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Cancel Multi-Select`
|
||||
String get tools_unp4k_action_cancel_multi_select {
|
||||
return Intl.message(
|
||||
'Cancel Multi-Select',
|
||||
name: 'tools_unp4k_action_cancel_multi_select',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Select All`
|
||||
String get tools_unp4k_action_select_all {
|
||||
return Intl.message(
|
||||
'Select All',
|
||||
name: 'tools_unp4k_action_select_all',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Deselect All`
|
||||
String get tools_unp4k_action_deselect_all {
|
||||
return Intl.message(
|
||||
'Deselect All',
|
||||
name: 'tools_unp4k_action_deselect_all',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||
|
||||
@ -1424,5 +1424,27 @@
|
||||
"splash_db_reset_done": "[Diagnostic] Database reset complete, preparing to exit application",
|
||||
"splash_db_reset_msg": "Database has been reset, application will exit. Please restart the application.",
|
||||
"splash_reset_db_failed": "[Diagnostic] Failed to reset database: {v0}",
|
||||
"@splash_reset_db_failed": {}
|
||||
}
|
||||
"@splash_reset_db_failed": {},
|
||||
"tools_unp4k_search_placeholder": "Search files (supports regex)...",
|
||||
"tools_unp4k_sort_default": "Default Sort",
|
||||
"tools_unp4k_sort_size_asc": "Smaller First",
|
||||
"tools_unp4k_sort_size_desc": "Larger First",
|
||||
"tools_unp4k_sort_date_asc": "Older First",
|
||||
"tools_unp4k_sort_date_desc": "Newer First",
|
||||
"tools_unp4k_action_save_as": "Save As...",
|
||||
"tools_unp4k_action_extracting": "Extracting: {v0}",
|
||||
"tools_unp4k_action_extract_success": "Extraction complete: {v0}",
|
||||
"tools_unp4k_action_extract_failed": "Extraction failed: {v0}",
|
||||
"tools_unp4k_search_no_result": "No matching files found",
|
||||
"tools_unp4k_searching": "Searching...",
|
||||
"tools_unp4k_extract_dialog_title": "Extract Files",
|
||||
"tools_unp4k_extract_progress": "Extracting ({v0}/{v1})",
|
||||
"tools_unp4k_extract_current_file": "Current file: {v0}",
|
||||
"tools_unp4k_extract_cancelled": "Extraction cancelled",
|
||||
"tools_unp4k_extract_completed": "Extraction completed, {v0} files total",
|
||||
"tools_unp4k_action_multi_select": "Multi-Select",
|
||||
"tools_unp4k_action_export_selected": "Export Selected ({v0})",
|
||||
"tools_unp4k_action_cancel_multi_select": "Cancel Multi-Select",
|
||||
"tools_unp4k_action_select_all": "Select All",
|
||||
"tools_unp4k_action_deselect_all": "Deselect All"
|
||||
}
|
||||
@ -1139,5 +1139,27 @@
|
||||
"splash_db_reset_done": "[诊断] 数据库重置完成,准备退出应用",
|
||||
"splash_db_reset_msg": "数据库已重置,应用将退出。请重新启动应用。",
|
||||
"splash_reset_db_failed": "[诊断] 重置数据库失败: {v0}",
|
||||
"@splash_reset_db_failed": {}
|
||||
"@splash_reset_db_failed": {},
|
||||
"tools_unp4k_search_placeholder": "搜索文件(支持正则)...",
|
||||
"tools_unp4k_sort_default": "默认排序",
|
||||
"tools_unp4k_sort_size_asc": "小文件优先",
|
||||
"tools_unp4k_sort_size_desc": "大文件优先",
|
||||
"tools_unp4k_sort_date_asc": "旧文件优先",
|
||||
"tools_unp4k_sort_date_desc": "新文件优先",
|
||||
"tools_unp4k_action_save_as": "另存为...",
|
||||
"tools_unp4k_action_extracting": "正在提取:{v0}",
|
||||
"tools_unp4k_action_extract_success": "提取完成:{v0}",
|
||||
"tools_unp4k_action_extract_failed": "提取失败:{v0}",
|
||||
"tools_unp4k_search_no_result": "未找到匹配文件",
|
||||
"tools_unp4k_searching": "正在搜索...",
|
||||
"tools_unp4k_extract_dialog_title": "提取文件",
|
||||
"tools_unp4k_extract_progress": "正在提取 ({v0}/{v1})",
|
||||
"tools_unp4k_extract_current_file": "当前文件:{v0}",
|
||||
"tools_unp4k_extract_cancelled": "提取已取消",
|
||||
"tools_unp4k_extract_completed": "提取完成,共 {v0} 个文件",
|
||||
"tools_unp4k_action_multi_select": "多选",
|
||||
"tools_unp4k_action_export_selected": "导出选中项 ({v0})",
|
||||
"tools_unp4k_action_cancel_multi_select": "取消多选",
|
||||
"tools_unp4k_action_select_all": "全选",
|
||||
"tools_unp4k_action_deselect_all": "取消全选"
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
@ -7,20 +6,34 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/binary_conf.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/rs_process.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/data/app_unp4k_p4k_item_data.dart';
|
||||
import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/rs_process.dart'
|
||||
as rs_process;
|
||||
import 'package:starcitizen_doctor/common/rust/api/unp4k_api.dart' as unp4k_api;
|
||||
|
||||
part 'unp4kc.freezed.dart';
|
||||
|
||||
part 'unp4kc.g.dart';
|
||||
|
||||
/// 排序类型枚举
|
||||
enum Unp4kSortType {
|
||||
/// 默认排序(文件夹优先,按名称)
|
||||
defaultSort,
|
||||
|
||||
/// 文件大小升序
|
||||
sizeAsc,
|
||||
|
||||
/// 文件大小降序
|
||||
sizeDesc,
|
||||
|
||||
/// 修改时间升序
|
||||
dateAsc,
|
||||
|
||||
/// 修改时间降序
|
||||
dateDesc,
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class Unp4kcState with _$Unp4kcState {
|
||||
const factory Unp4kcState({
|
||||
@ -31,19 +44,29 @@ abstract class Unp4kcState with _$Unp4kcState {
|
||||
String? endMessage,
|
||||
MapEntry<String, String>? tempOpenFile,
|
||||
@Default("") String errorMessage,
|
||||
@Default("") String searchQuery,
|
||||
@Default(false) bool isSearching,
|
||||
|
||||
/// 搜索结果的虚拟文件系统(支持分级展示)
|
||||
MemoryFileSystem? searchFs,
|
||||
|
||||
/// 搜索匹配的文件路径集合
|
||||
Set<String>? searchMatchedFiles,
|
||||
@Default(Unp4kSortType.defaultSort) Unp4kSortType sortType,
|
||||
|
||||
/// 是否处于多选模式
|
||||
@Default(false) bool isMultiSelectMode,
|
||||
|
||||
/// 多选模式下选中的文件路径集合
|
||||
@Default({}) Set<String> selectedItems,
|
||||
}) = _Unp4kcState;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class Unp4kCModel extends _$Unp4kCModel {
|
||||
int? _rsPid;
|
||||
|
||||
@override
|
||||
Unp4kcState build() {
|
||||
state = Unp4kcState(
|
||||
startUp: false,
|
||||
curPath: '\\',
|
||||
endMessage: S.current.tools_unp4k_msg_init);
|
||||
state = Unp4kcState(startUp: false, curPath: '\\', endMessage: S.current.tools_unp4k_msg_init);
|
||||
_init();
|
||||
return state;
|
||||
}
|
||||
@ -52,108 +75,333 @@ class Unp4kCModel extends _$Unp4kCModel {
|
||||
|
||||
String getGamePath() => _toolsState.scInstalledPath;
|
||||
|
||||
bool _hasUnp4kRunTimeError = false;
|
||||
|
||||
void _init() async {
|
||||
final execDir = "${appGlobalState.applicationBinaryModuleDir}\\unp4kc";
|
||||
await BinaryModuleConf.extractModule(
|
||||
["unp4kc"], appGlobalState.applicationBinaryModuleDir!);
|
||||
final exec = "$execDir\\unp4kc.exe";
|
||||
final gamePath = getGamePath();
|
||||
final gameP4kPath = "$gamePath\\Data.p4k";
|
||||
|
||||
final stream = rs_process.start(
|
||||
executable: exec, arguments: [], workingDirectory: execDir);
|
||||
try {
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading);
|
||||
|
||||
stream.listen((event) async {
|
||||
switch (event.dataType) {
|
||||
case RsProcessStreamDataType.output:
|
||||
_rsPid = event.rsPid;
|
||||
try {
|
||||
final eventJson = await compute(json.decode, event.data);
|
||||
_handleMessage(eventJson, event.rsPid);
|
||||
} catch (e) {
|
||||
dPrint("[unp4kc] json error: $e");
|
||||
}
|
||||
break;
|
||||
case RsProcessStreamDataType.error:
|
||||
dPrint("[unp4kc] stderr: ${event.data}");
|
||||
if (state.errorMessage.isEmpty) {
|
||||
state = state.copyWith(errorMessage: event.data);
|
||||
} else {
|
||||
state = state.copyWith(
|
||||
errorMessage: "${state.errorMessage}\n${event.data}");
|
||||
}
|
||||
if (!_hasUnp4kRunTimeError) {
|
||||
if (checkRunTimeError(state.errorMessage)) {
|
||||
_hasUnp4kRunTimeError = true;
|
||||
AnalyticsApi.touch("unp4k_no_runtime");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RsProcessStreamDataType.exit:
|
||||
dPrint("[unp4kc] exit: ${event.data}");
|
||||
break;
|
||||
final loadStartTime = DateTime.now();
|
||||
|
||||
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>{};
|
||||
final fs = MemoryFileSystem(style: FileSystemStyle.posix);
|
||||
|
||||
var nextAwait = 0;
|
||||
for (var i = 0; i < p4kFiles.length; i++) {
|
||||
final item = p4kFiles[i];
|
||||
final fileData = AppUnp4kP4kItemData(
|
||||
name: item.name,
|
||||
isDirectory: item.isDirectory,
|
||||
size: item.size.toInt(),
|
||||
compressedSize: item.compressedSize.toInt(),
|
||||
dateModified: item.dateModified,
|
||||
);
|
||||
|
||||
files[item.name] = fileData;
|
||||
|
||||
if (!item.isDirectory) {
|
||||
await fs.file(item.name.replaceAll("\\", "/")).create(recursive: true);
|
||||
}
|
||||
|
||||
if (i == nextAwait) {
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading3(i, p4kFiles.length));
|
||||
await Future.delayed(Duration.zero);
|
||||
nextAwait += 20000;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ref.onDispose(() {
|
||||
state = state.copyWith(fs: null);
|
||||
if (_rsPid != null) {
|
||||
Process.killPid(_rsPid!);
|
||||
dPrint("[unp4kc] kill ...");
|
||||
final endTime = DateTime.now();
|
||||
state = state.copyWith(
|
||||
files: files,
|
||||
fs: fs,
|
||||
endMessage: S.current.tools_unp4k_msg_read_completed(
|
||||
files.length,
|
||||
endTime.difference(loadStartTime).inMilliseconds,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
dPrint("[unp4k] error: $e");
|
||||
state = state.copyWith(errorMessage: e.toString());
|
||||
AnalyticsApi.touch("unp4k_error");
|
||||
}
|
||||
|
||||
ref.onDispose(() async {
|
||||
try {
|
||||
await unp4k_api.p4KClose();
|
||||
} catch (e) {
|
||||
dPrint("[unp4k] close error: $e");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
DateTime? _loadStartTime;
|
||||
List<AppUnp4kP4kItemData>? getFiles() {
|
||||
final path = state.curPath.replaceAll("\\", "/");
|
||||
|
||||
void _handleMessage(Map<String, dynamic> eventJson, int rsPid) async {
|
||||
final action = eventJson["action"];
|
||||
final data = eventJson["data"];
|
||||
final gamePath = getGamePath();
|
||||
final gameP4kPath = "$gamePath\\Data.p4k";
|
||||
switch (action.toString().trim()) {
|
||||
case "info: startup":
|
||||
rs_process.write(rsPid: rsPid, data: "$gameP4kPath\n");
|
||||
break;
|
||||
case "info: Reading_p4k_file":
|
||||
_loadStartTime = DateTime.now();
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading);
|
||||
break;
|
||||
case "info: All Ready":
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_msg_reading2);
|
||||
break;
|
||||
case "data: P4K_Files":
|
||||
final p4kFiles = (data as List<dynamic>);
|
||||
final files = <String, AppUnp4kP4kItemData>{};
|
||||
final fs = MemoryFileSystem(style: FileSystemStyle.posix);
|
||||
// 如果有搜索结果,使用搜索的虚拟文件系统
|
||||
final fs = state.searchFs ?? state.fs;
|
||||
if (fs == null) return null;
|
||||
|
||||
var nextAwait = 0;
|
||||
for (var i = 0; i < p4kFiles.length; i++) {
|
||||
final item = AppUnp4kP4kItemData.fromJson(p4kFiles[i]);
|
||||
item.name = "${item.name}";
|
||||
files["\\${item.name}"] = item;
|
||||
await fs
|
||||
.file(item.name?.replaceAll("\\", "/") ?? "")
|
||||
.create(recursive: true);
|
||||
if (i == nextAwait) {
|
||||
state = state.copyWith(
|
||||
endMessage:
|
||||
S.current.tools_unp4k_msg_reading3(i, p4kFiles.length));
|
||||
await Future.delayed(Duration.zero);
|
||||
nextAwait += 20000;
|
||||
final dir = fs.directory(path);
|
||||
if (!dir.existsSync()) return [];
|
||||
final files = dir.listSync(recursive: false, followLinks: false);
|
||||
|
||||
final result = <AppUnp4kP4kItemData>[];
|
||||
for (var file in files) {
|
||||
if (file is File) {
|
||||
final f = state.files?[file.path.replaceAll("/", "\\")];
|
||||
if (f != null) {
|
||||
if (!(f.name?.startsWith("\\") ?? true)) {
|
||||
f.name = "\\${f.name}";
|
||||
}
|
||||
result.add(f);
|
||||
}
|
||||
final endTime = DateTime.now();
|
||||
state = state.copyWith(
|
||||
files: files,
|
||||
fs: fs,
|
||||
endMessage: S.current.tools_unp4k_msg_read_completed(files.length,
|
||||
endTime.difference(_loadStartTime!).inMilliseconds));
|
||||
_loadStartTime = null;
|
||||
} else {
|
||||
result.add(AppUnp4kP4kItemData(name: file.path.replaceAll("/", "\\"), isDirectory: true));
|
||||
}
|
||||
}
|
||||
|
||||
// 应用排序
|
||||
_sortFiles(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 对文件列表进行排序
|
||||
void _sortFiles(List<AppUnp4kP4kItemData> files) {
|
||||
switch (state.sortType) {
|
||||
case Unp4kSortType.defaultSort:
|
||||
// 默认排序:文件夹优先,按名称排序
|
||||
files.sort((a, b) {
|
||||
if ((a.isDirectory ?? false) && !(b.isDirectory ?? false)) {
|
||||
return -1;
|
||||
} else if (!(a.isDirectory ?? false) && (b.isDirectory ?? false)) {
|
||||
return 1;
|
||||
} else {
|
||||
return (a.name ?? "").compareTo(b.name ?? "");
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "info: Extracted_Open":
|
||||
final filePath = data.toString();
|
||||
dPrint("[unp4kc] Extracted_Open file: $filePath");
|
||||
case Unp4kSortType.sizeAsc:
|
||||
// 文件大小升序(文件夹大小视为0)
|
||||
files.sort((a, b) {
|
||||
if ((a.isDirectory ?? false) && !(b.isDirectory ?? false)) {
|
||||
return -1;
|
||||
} else if (!(a.isDirectory ?? false) && (b.isDirectory ?? false)) {
|
||||
return 1;
|
||||
}
|
||||
final sizeA = (a.isDirectory ?? false) ? 0 : (a.size ?? 0);
|
||||
final sizeB = (b.isDirectory ?? false) ? 0 : (b.size ?? 0);
|
||||
return sizeA.compareTo(sizeB);
|
||||
});
|
||||
break;
|
||||
case Unp4kSortType.sizeDesc:
|
||||
// 文件大小降序
|
||||
files.sort((a, b) {
|
||||
if ((a.isDirectory ?? false) && !(b.isDirectory ?? false)) {
|
||||
return -1;
|
||||
} else if (!(a.isDirectory ?? false) && (b.isDirectory ?? false)) {
|
||||
return 1;
|
||||
}
|
||||
final sizeA = (a.isDirectory ?? false) ? 0 : (a.size ?? 0);
|
||||
final sizeB = (b.isDirectory ?? false) ? 0 : (b.size ?? 0);
|
||||
return sizeB.compareTo(sizeA);
|
||||
});
|
||||
break;
|
||||
case Unp4kSortType.dateAsc:
|
||||
// 修改时间升序
|
||||
files.sort((a, b) {
|
||||
if ((a.isDirectory ?? false) && !(b.isDirectory ?? false)) {
|
||||
return -1;
|
||||
} else if (!(a.isDirectory ?? false) && (b.isDirectory ?? false)) {
|
||||
return 1;
|
||||
}
|
||||
final dateA = a.dateModified ?? 0;
|
||||
final dateB = b.dateModified ?? 0;
|
||||
return dateA.compareTo(dateB);
|
||||
});
|
||||
break;
|
||||
case Unp4kSortType.dateDesc:
|
||||
// 修改时间降序
|
||||
files.sort((a, b) {
|
||||
if ((a.isDirectory ?? false) && !(b.isDirectory ?? false)) {
|
||||
return -1;
|
||||
} else if (!(a.isDirectory ?? false) && (b.isDirectory ?? false)) {
|
||||
return 1;
|
||||
}
|
||||
final dateA = a.dateModified ?? 0;
|
||||
final dateB = b.dateModified ?? 0;
|
||||
return dateB.compareTo(dateA);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置排序类型
|
||||
void setSortType(Unp4kSortType sortType) {
|
||||
state = state.copyWith(sortType: sortType);
|
||||
}
|
||||
|
||||
/// 执行搜索(异步)
|
||||
Future<void> search(String query) async {
|
||||
if (query.isEmpty) {
|
||||
// 清除搜索,返回根目录
|
||||
state = state.copyWith(
|
||||
searchQuery: "",
|
||||
searchFs: null,
|
||||
searchMatchedFiles: null,
|
||||
isSearching: false,
|
||||
curPath: "\\",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存当前路径,用于搜索后尝试保持
|
||||
final currentPath = state.curPath;
|
||||
|
||||
state = state.copyWith(searchQuery: query, isSearching: true, endMessage: S.current.tools_unp4k_searching);
|
||||
|
||||
// 使用 compute 在后台线程执行搜索
|
||||
final allFiles = state.files;
|
||||
if (allFiles == null) {
|
||||
state = state.copyWith(isSearching: false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final searchResult = await compute(_searchFiles, _SearchParams(allFiles, query));
|
||||
final matchedFiles = searchResult.matchedFiles;
|
||||
|
||||
// 构建搜索结果的虚拟文件系统
|
||||
final searchFs = MemoryFileSystem(style: FileSystemStyle.posix);
|
||||
for (var filePath in matchedFiles) {
|
||||
await searchFs.file(filePath.replaceAll("\\", "/")).create(recursive: true);
|
||||
}
|
||||
|
||||
// 检查当前路径是否有搜索结果
|
||||
String targetPath = "\\";
|
||||
if (currentPath != "\\") {
|
||||
final checkPath = currentPath.replaceAll("\\", "/");
|
||||
final dir = searchFs.directory(checkPath);
|
||||
if (dir.existsSync() && dir.listSync().isNotEmpty) {
|
||||
// 当前目录有结果,保持当前路径
|
||||
targetPath = currentPath;
|
||||
}
|
||||
}
|
||||
|
||||
state = state.copyWith(
|
||||
searchFs: searchFs,
|
||||
searchMatchedFiles: matchedFiles,
|
||||
isSearching: false,
|
||||
curPath: targetPath,
|
||||
endMessage: matchedFiles.isEmpty
|
||||
? S.current.tools_unp4k_search_no_result
|
||||
: S.current.tools_unp4k_msg_read_completed(matchedFiles.length, 0),
|
||||
);
|
||||
} catch (e) {
|
||||
dPrint("[unp4k] search error: $e");
|
||||
state = state.copyWith(isSearching: false, endMessage: e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// 清除搜索
|
||||
void clearSearch() {
|
||||
state = state.copyWith(
|
||||
searchQuery: "",
|
||||
searchFs: null,
|
||||
searchMatchedFiles: null,
|
||||
isSearching: false,
|
||||
curPath: "\\",
|
||||
);
|
||||
}
|
||||
|
||||
/// 进入多选模式
|
||||
void enterMultiSelectMode() {
|
||||
state = state.copyWith(isMultiSelectMode: true, selectedItems: {});
|
||||
}
|
||||
|
||||
/// 退出多选模式
|
||||
void exitMultiSelectMode() {
|
||||
state = state.copyWith(isMultiSelectMode: false, selectedItems: {});
|
||||
}
|
||||
|
||||
/// 切换选中状态
|
||||
void toggleSelectItem(String itemPath) {
|
||||
final currentSelected = Set<String>.from(state.selectedItems);
|
||||
if (currentSelected.contains(itemPath)) {
|
||||
currentSelected.remove(itemPath);
|
||||
} else {
|
||||
currentSelected.add(itemPath);
|
||||
}
|
||||
state = state.copyWith(selectedItems: currentSelected);
|
||||
}
|
||||
|
||||
/// 全选当前目录的文件
|
||||
void selectAll(List<AppUnp4kP4kItemData>? files) {
|
||||
if (files == null) return;
|
||||
final currentSelected = Set<String>.from(state.selectedItems);
|
||||
for (var file in files) {
|
||||
final path = file.name ?? "";
|
||||
if (path.isNotEmpty) {
|
||||
currentSelected.add(path);
|
||||
}
|
||||
}
|
||||
state = state.copyWith(selectedItems: currentSelected);
|
||||
}
|
||||
|
||||
/// 取消全选当前目录的文件
|
||||
void deselectAll(List<AppUnp4kP4kItemData>? files) {
|
||||
if (files == null) return;
|
||||
final currentSelected = Set<String>.from(state.selectedItems);
|
||||
for (var file in files) {
|
||||
final path = file.name ?? "";
|
||||
currentSelected.remove(path);
|
||||
}
|
||||
state = state.copyWith(selectedItems: currentSelected);
|
||||
}
|
||||
|
||||
void changeDir(String name, {bool fullPath = false}) {
|
||||
// 切换目录时退出多选模式
|
||||
if (state.isMultiSelectMode) {
|
||||
state = state.copyWith(isMultiSelectMode: false, selectedItems: {});
|
||||
}
|
||||
// 切换目录时不清除搜索,只改变当前路径
|
||||
if (fullPath) {
|
||||
state = state.copyWith(curPath: name);
|
||||
} else {
|
||||
state = state.copyWith(curPath: "${state.curPath}$name\\");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> openFile(String filePath) async {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final tempPath = "${tempDir.absolute.path}\\SCToolbox_unp4kc\\${SCLoggerHelper.getGameChannelID(getGamePath())}\\";
|
||||
state = state.copyWith(
|
||||
tempOpenFile: const MapEntry("loading", ""),
|
||||
endMessage: S.current.tools_unp4k_msg_open_file(filePath),
|
||||
);
|
||||
await extractFile(filePath, tempPath, mode: "extract_open");
|
||||
}
|
||||
|
||||
Future<void> extractFile(String filePath, String outputPath, {String mode = "extract"}) async {
|
||||
try {
|
||||
// remove first \\
|
||||
if (filePath.startsWith("\\")) {
|
||||
filePath = filePath.substring(1);
|
||||
}
|
||||
|
||||
final fullOutputPath = "$outputPath$filePath";
|
||||
dPrint("extractFile .... $filePath -> $fullOutputPath");
|
||||
|
||||
// 使用 Rust API 提取到磁盘
|
||||
await unp4k_api.p4KExtractToDisk(filePath: filePath, outputPath: fullOutputPath);
|
||||
|
||||
if (mode == "extract_open") {
|
||||
const textExt = [".txt", ".xml", ".json", ".lua", ".cfg", ".ini"];
|
||||
const imgExt = [".png"];
|
||||
String openType = "unknown";
|
||||
@ -168,111 +416,274 @@ class Unp4kCModel extends _$Unp4kCModel {
|
||||
}
|
||||
}
|
||||
state = state.copyWith(
|
||||
tempOpenFile: MapEntry(openType, filePath),
|
||||
endMessage: S.current.tools_unp4k_msg_open_file(filePath));
|
||||
break;
|
||||
default:
|
||||
dPrint("[unp4kc] unknown action: $action");
|
||||
break;
|
||||
tempOpenFile: MapEntry(openType, fullOutputPath),
|
||||
endMessage: S.current.tools_unp4k_msg_open_file(filePath),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("[unp4k] extractFile error: $e");
|
||||
state = state.copyWith(errorMessage: e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
List<AppUnp4kP4kItemData>? getFiles() {
|
||||
final path = state.curPath.replaceAll("\\", "/");
|
||||
final fs = state.fs;
|
||||
if (fs == null) return null;
|
||||
final dir = fs.directory(path);
|
||||
if (!dir.existsSync()) return [];
|
||||
final files = dir.listSync(recursive: false, followLinks: false);
|
||||
files.sort((a, b) {
|
||||
if (a is Directory && b is File) {
|
||||
return -1;
|
||||
} else if (a is File && b is Directory) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.path.compareTo(b.path);
|
||||
/// 提取文件或文件夹到指定目录(带进度回调和取消支持)
|
||||
/// [item] 要提取的文件或文件夹
|
||||
/// [outputDir] 输出目录
|
||||
/// [onProgress] 进度回调 (当前文件索引, 总文件数, 当前文件名)
|
||||
/// [isCancelled] 取消检查函数,返回 true 表示取消
|
||||
/// 返回值:(是否成功, 已提取文件数, 错误信息)
|
||||
Future<(bool, int, String?)> extractToDirectoryWithProgress(
|
||||
AppUnp4kP4kItemData item,
|
||||
String outputDir, {
|
||||
void Function(int current, int total, String currentFile)? onProgress,
|
||||
bool Function()? isCancelled,
|
||||
}) async {
|
||||
try {
|
||||
final itemPath = item.name ?? "";
|
||||
var filePath = itemPath;
|
||||
if (filePath.startsWith("\\")) {
|
||||
filePath = filePath.substring(1);
|
||||
}
|
||||
});
|
||||
final result = <AppUnp4kP4kItemData>[];
|
||||
for (var file in files) {
|
||||
if (file is File) {
|
||||
final f = state.files?[file.path.replaceAll("/", "\\")];
|
||||
if (f != null) {
|
||||
if (!(f.name?.startsWith("\\") ?? true)) {
|
||||
f.name = "\\${f.name}";
|
||||
|
||||
if (item.isDirectory ?? false) {
|
||||
// 提取文件夹:遍历所有以该路径为前缀的文件
|
||||
final allFiles = state.files;
|
||||
if (allFiles != null) {
|
||||
final prefix = itemPath.endsWith("\\") ? itemPath : "$itemPath\\";
|
||||
|
||||
// 收集所有需要提取的文件
|
||||
final filesToExtract = <MapEntry<String, AppUnp4kP4kItemData>>[];
|
||||
for (var entry in allFiles.entries) {
|
||||
if (entry.key.startsWith(prefix) && !(entry.value.isDirectory ?? false)) {
|
||||
filesToExtract.add(entry);
|
||||
}
|
||||
}
|
||||
result.add(f);
|
||||
|
||||
final total = filesToExtract.length;
|
||||
var current = 0;
|
||||
|
||||
for (var entry in filesToExtract) {
|
||||
// 检查是否取消
|
||||
if (isCancelled?.call() == true) {
|
||||
return (false, current, S.current.tools_unp4k_extract_cancelled);
|
||||
}
|
||||
|
||||
var entryPath = entry.key;
|
||||
if (entryPath.startsWith("\\")) {
|
||||
entryPath = entryPath.substring(1);
|
||||
}
|
||||
|
||||
current++;
|
||||
onProgress?.call(current, total, entryPath);
|
||||
|
||||
final fullOutputPath = "$outputDir\\$entryPath";
|
||||
await unp4k_api.p4KExtractToDisk(filePath: entryPath, outputPath: fullOutputPath);
|
||||
}
|
||||
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_extract_completed(current));
|
||||
return (true, current, null);
|
||||
}
|
||||
return (true, 0, null);
|
||||
} else {
|
||||
// 提取单个文件
|
||||
onProgress?.call(1, 1, filePath);
|
||||
|
||||
// 检查是否取消
|
||||
if (isCancelled?.call() == true) {
|
||||
return (false, 0, S.current.tools_unp4k_extract_cancelled);
|
||||
}
|
||||
|
||||
final fullOutputPath = "$outputDir\\$filePath";
|
||||
await unp4k_api.p4KExtractToDisk(filePath: filePath, outputPath: fullOutputPath);
|
||||
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_extract_completed(1));
|
||||
return (true, 1, null);
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("[unp4k] extractToDirectoryWithProgress error: $e");
|
||||
return (false, 0, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取文件夹中需要提取的文件数量
|
||||
int getFileCountInDirectory(AppUnp4kP4kItemData item) {
|
||||
if (!(item.isDirectory ?? false)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
final itemPath = item.name ?? "";
|
||||
final prefix = itemPath.endsWith("\\") ? itemPath : "$itemPath\\";
|
||||
final allFiles = state.files;
|
||||
|
||||
if (allFiles == null) return 0;
|
||||
|
||||
int count = 0;
|
||||
for (var entry in allFiles.entries) {
|
||||
if (entry.key.startsWith(prefix) && !(entry.value.isDirectory ?? false)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// 获取多选项的总文件数量
|
||||
int getFileCountForSelectedItems(Set<String> selectedItems) {
|
||||
int count = 0;
|
||||
final allFiles = state.files;
|
||||
if (allFiles == null) return 0;
|
||||
|
||||
for (var itemPath in selectedItems) {
|
||||
final item = allFiles[itemPath];
|
||||
if (item != null) {
|
||||
if (item.isDirectory ?? false) {
|
||||
count += getFileCountInDirectory(item);
|
||||
} else {
|
||||
count += 1;
|
||||
}
|
||||
} else {
|
||||
result.add(AppUnp4kP4kItemData(
|
||||
name: file.path.replaceAll("/", "\\"), isDirectory: true));
|
||||
// 可能是文件夹(虚拟路径)
|
||||
final prefix = itemPath.endsWith("\\") ? itemPath : "$itemPath\\";
|
||||
for (var entry in allFiles.entries) {
|
||||
if (entry.key.startsWith(prefix) && !(entry.value.isDirectory ?? false)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return count;
|
||||
}
|
||||
|
||||
void changeDir(String name, {bool fullPath = false}) {
|
||||
if (fullPath) {
|
||||
state = state.copyWith(curPath: name);
|
||||
} else {
|
||||
state = state.copyWith(curPath: "${state.curPath}$name\\");
|
||||
/// 批量提取多个选中项到指定目录(带进度回调和取消支持)
|
||||
Future<(bool, int, String?)> extractSelectedItemsWithProgress(
|
||||
Set<String> selectedItems,
|
||||
String outputDir, {
|
||||
void Function(int current, int total, String currentFile)? onProgress,
|
||||
bool Function()? isCancelled,
|
||||
}) async {
|
||||
try {
|
||||
final allFiles = state.files;
|
||||
if (allFiles == null) return (true, 0, null);
|
||||
|
||||
// 收集所有需要提取的文件
|
||||
final filesToExtract = <String>[];
|
||||
|
||||
for (var itemPath in selectedItems) {
|
||||
final item = allFiles[itemPath];
|
||||
if (item != null) {
|
||||
if (item.isDirectory ?? false) {
|
||||
// 文件夹:收集所有子文件
|
||||
final prefix = itemPath.endsWith("\\") ? itemPath : "$itemPath\\";
|
||||
for (var entry in allFiles.entries) {
|
||||
if (entry.key.startsWith(prefix) && !(entry.value.isDirectory ?? false)) {
|
||||
filesToExtract.add(entry.key);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 单个文件
|
||||
filesToExtract.add(itemPath);
|
||||
}
|
||||
} else {
|
||||
// 可能是虚拟文件夹路径
|
||||
final prefix = itemPath.endsWith("\\") ? itemPath : "$itemPath\\";
|
||||
for (var entry in allFiles.entries) {
|
||||
if (entry.key.startsWith(prefix) && !(entry.value.isDirectory ?? false)) {
|
||||
filesToExtract.add(entry.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final total = filesToExtract.length;
|
||||
var current = 0;
|
||||
|
||||
for (var filePath in filesToExtract) {
|
||||
// 检查是否取消
|
||||
if (isCancelled?.call() == true) {
|
||||
return (false, current, S.current.tools_unp4k_extract_cancelled);
|
||||
}
|
||||
|
||||
var extractPath = filePath;
|
||||
if (extractPath.startsWith("\\")) {
|
||||
extractPath = extractPath.substring(1);
|
||||
}
|
||||
|
||||
current++;
|
||||
onProgress?.call(current, total, extractPath);
|
||||
|
||||
final fullOutputPath = "$outputDir\\$extractPath";
|
||||
await unp4k_api.p4KExtractToDisk(filePath: extractPath, outputPath: fullOutputPath);
|
||||
}
|
||||
|
||||
state = state.copyWith(endMessage: S.current.tools_unp4k_extract_completed(current));
|
||||
return (true, current, null);
|
||||
} catch (e) {
|
||||
dPrint("[unp4k] extractSelectedItemsWithProgress error: $e");
|
||||
return (false, 0, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> openFile(String filePath) async {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final tempPath =
|
||||
"${tempDir.absolute.path}\\SCToolbox_unp4kc\\${SCLoggerHelper.getGameChannelID(getGamePath())}\\";
|
||||
state = state.copyWith(
|
||||
tempOpenFile: const MapEntry("loading", ""),
|
||||
endMessage: S.current.tools_unp4k_msg_open_file(filePath));
|
||||
extractFile(filePath, tempPath, mode: "extract_open");
|
||||
}
|
||||
|
||||
Future<void> extractFile(String filePath, String outputPath,
|
||||
{String mode = "extract"}) async {
|
||||
// remove first \\
|
||||
if (filePath.startsWith("\\")) {
|
||||
filePath = filePath.substring(1);
|
||||
/// 从 P4K 文件中提取指定文件到内存
|
||||
/// [p4kPath] P4K 文件路径
|
||||
/// [filePath] 要提取的文件路径(P4K 内部路径)
|
||||
static Future<Uint8List> extractP4kFileToMemory(String p4kPath, String filePath) async {
|
||||
try {
|
||||
await unp4k_api.p4KOpen(p4KPath: p4kPath);
|
||||
final data = await unp4k_api.p4KExtractToMemory(filePath: filePath);
|
||||
await unp4k_api.p4KClose();
|
||||
return Uint8List.fromList(data);
|
||||
} catch (e) {
|
||||
throw Exception("extractP4kFileToMemory error: $e");
|
||||
}
|
||||
outputPath = "$outputPath$filePath";
|
||||
dPrint("extractFile .... $filePath");
|
||||
if (_rsPid != null) {
|
||||
rs_process.write(
|
||||
rsPid: _rsPid!, data: "$mode<:,:>$filePath<:,:>$outputPath\n");
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkRunTimeError(String errorMessage) {
|
||||
if (errorMessage
|
||||
.contains("You must install .NET to run this application") ||
|
||||
errorMessage.contains(
|
||||
"You must install or update .NET to run this application") ||
|
||||
errorMessage.contains(
|
||||
"It was not possible to find any compatible framework version")) {
|
||||
AnalyticsApi.touch("unp4k_no_runtime");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Future<Uint8List> unp4kTools(
|
||||
String applicationBinaryModuleDir, List<String> args) async {
|
||||
await BinaryModuleConf.extractModule(
|
||||
["unp4kc"], applicationBinaryModuleDir);
|
||||
final execDir = "$applicationBinaryModuleDir\\unp4kc";
|
||||
final exec = "$execDir\\unp4kc.exe";
|
||||
final r = await Process.run(exec, args);
|
||||
if (r.exitCode != 0) {
|
||||
Process.killPid(r.pid);
|
||||
throw Exception(
|
||||
"error: ${r.exitCode} , info= ${r.stdout} , err= ${r.stderr}");
|
||||
}
|
||||
final eventJson = await compute(json.decode, r.stdout.toString());
|
||||
if (eventJson["action"] == "data: Uint8List") {
|
||||
final data = eventJson["data"];
|
||||
return Uint8List.fromList((data as List).cast<int>());
|
||||
}
|
||||
throw Exception("error: data error");
|
||||
}
|
||||
}
|
||||
|
||||
/// 搜索参数类
|
||||
class _SearchParams {
|
||||
final Map<String, AppUnp4kP4kItemData> files;
|
||||
final String query;
|
||||
|
||||
_SearchParams(this.files, this.query);
|
||||
}
|
||||
|
||||
/// 搜索结果类
|
||||
class _SearchResult {
|
||||
final Set<String> matchedFiles;
|
||||
|
||||
_SearchResult(this.matchedFiles);
|
||||
}
|
||||
|
||||
/// 在后台线程执行搜索
|
||||
_SearchResult _searchFiles(_SearchParams params) {
|
||||
final matchedFiles = <String>{};
|
||||
|
||||
// 尝试编译正则表达式,如果失败则使用普通字符串匹配
|
||||
RegExp? regex;
|
||||
try {
|
||||
regex = RegExp(params.query, caseSensitive: false);
|
||||
} catch (e) {
|
||||
// 正则无效,回退到普通字符串匹配
|
||||
regex = null;
|
||||
}
|
||||
|
||||
for (var entry in params.files.entries) {
|
||||
final item = entry.value;
|
||||
final name = item.name ?? "";
|
||||
|
||||
// 跳过文件夹本身
|
||||
if (item.isDirectory ?? false) continue;
|
||||
|
||||
bool matches = false;
|
||||
if (regex != null) {
|
||||
matches = regex.hasMatch(name);
|
||||
} else {
|
||||
matches = name.toLowerCase().contains(params.query.toLowerCase());
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
// 添加匹配的文件路径
|
||||
matchedFiles.add(name.startsWith("\\") ? name : "\\$name");
|
||||
}
|
||||
}
|
||||
|
||||
return _SearchResult(matchedFiles);
|
||||
}
|
||||
|
||||
@ -14,7 +14,11 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$Unp4kcState implements DiagnosticableTreeMixin {
|
||||
|
||||
bool get startUp; Map<String, AppUnp4kP4kItemData>? get files; MemoryFileSystem? get fs; String get curPath; String? get endMessage; MapEntry<String, String>? get tempOpenFile; String get errorMessage;
|
||||
bool get startUp; Map<String, AppUnp4kP4kItemData>? get files; MemoryFileSystem? get fs; String get curPath; String? get endMessage; MapEntry<String, String>? get tempOpenFile; String get errorMessage; String get searchQuery; bool get isSearching;/// 搜索结果的虚拟文件系统(支持分级展示)
|
||||
MemoryFileSystem? get searchFs;/// 搜索匹配的文件路径集合
|
||||
Set<String>? get searchMatchedFiles; Unp4kSortType get sortType;/// 是否处于多选模式
|
||||
bool get isMultiSelectMode;/// 多选模式下选中的文件路径集合
|
||||
Set<String> get selectedItems;
|
||||
/// Create a copy of Unp4kcState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -26,21 +30,21 @@ $Unp4kcStateCopyWith<Unp4kcState> get copyWith => _$Unp4kcStateCopyWithImpl<Unp4
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'Unp4kcState'))
|
||||
..add(DiagnosticsProperty('startUp', startUp))..add(DiagnosticsProperty('files', files))..add(DiagnosticsProperty('fs', fs))..add(DiagnosticsProperty('curPath', curPath))..add(DiagnosticsProperty('endMessage', endMessage))..add(DiagnosticsProperty('tempOpenFile', tempOpenFile))..add(DiagnosticsProperty('errorMessage', errorMessage));
|
||||
..add(DiagnosticsProperty('startUp', startUp))..add(DiagnosticsProperty('files', files))..add(DiagnosticsProperty('fs', fs))..add(DiagnosticsProperty('curPath', curPath))..add(DiagnosticsProperty('endMessage', endMessage))..add(DiagnosticsProperty('tempOpenFile', tempOpenFile))..add(DiagnosticsProperty('errorMessage', errorMessage))..add(DiagnosticsProperty('searchQuery', searchQuery))..add(DiagnosticsProperty('isSearching', isSearching))..add(DiagnosticsProperty('searchFs', searchFs))..add(DiagnosticsProperty('searchMatchedFiles', searchMatchedFiles))..add(DiagnosticsProperty('sortType', sortType))..add(DiagnosticsProperty('isMultiSelectMode', isMultiSelectMode))..add(DiagnosticsProperty('selectedItems', selectedItems));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is Unp4kcState&&(identical(other.startUp, startUp) || other.startUp == startUp)&&const DeepCollectionEquality().equals(other.files, files)&&(identical(other.fs, fs) || other.fs == fs)&&(identical(other.curPath, curPath) || other.curPath == curPath)&&(identical(other.endMessage, endMessage) || other.endMessage == endMessage)&&(identical(other.tempOpenFile, tempOpenFile) || other.tempOpenFile == tempOpenFile)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is Unp4kcState&&(identical(other.startUp, startUp) || other.startUp == startUp)&&const DeepCollectionEquality().equals(other.files, files)&&(identical(other.fs, fs) || other.fs == fs)&&(identical(other.curPath, curPath) || other.curPath == curPath)&&(identical(other.endMessage, endMessage) || other.endMessage == endMessage)&&(identical(other.tempOpenFile, tempOpenFile) || other.tempOpenFile == tempOpenFile)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.searchQuery, searchQuery) || other.searchQuery == searchQuery)&&(identical(other.isSearching, isSearching) || other.isSearching == isSearching)&&(identical(other.searchFs, searchFs) || other.searchFs == searchFs)&&const DeepCollectionEquality().equals(other.searchMatchedFiles, searchMatchedFiles)&&(identical(other.sortType, sortType) || other.sortType == sortType)&&(identical(other.isMultiSelectMode, isMultiSelectMode) || other.isMultiSelectMode == isMultiSelectMode)&&const DeepCollectionEquality().equals(other.selectedItems, selectedItems));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,startUp,const DeepCollectionEquality().hash(files),fs,curPath,endMessage,tempOpenFile,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,startUp,const DeepCollectionEquality().hash(files),fs,curPath,endMessage,tempOpenFile,errorMessage,searchQuery,isSearching,searchFs,const DeepCollectionEquality().hash(searchMatchedFiles),sortType,isMultiSelectMode,const DeepCollectionEquality().hash(selectedItems));
|
||||
|
||||
@override
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'Unp4kcState(startUp: $startUp, files: $files, fs: $fs, curPath: $curPath, endMessage: $endMessage, tempOpenFile: $tempOpenFile, errorMessage: $errorMessage)';
|
||||
return 'Unp4kcState(startUp: $startUp, files: $files, fs: $fs, curPath: $curPath, endMessage: $endMessage, tempOpenFile: $tempOpenFile, errorMessage: $errorMessage, searchQuery: $searchQuery, isSearching: $isSearching, searchFs: $searchFs, searchMatchedFiles: $searchMatchedFiles, sortType: $sortType, isMultiSelectMode: $isMultiSelectMode, selectedItems: $selectedItems)';
|
||||
}
|
||||
|
||||
|
||||
@ -51,7 +55,7 @@ abstract mixin class $Unp4kcStateCopyWith<$Res> {
|
||||
factory $Unp4kcStateCopyWith(Unp4kcState value, $Res Function(Unp4kcState) _then) = _$Unp4kcStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage
|
||||
bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage, String searchQuery, bool isSearching, MemoryFileSystem? searchFs, Set<String>? searchMatchedFiles, Unp4kSortType sortType, bool isMultiSelectMode, Set<String> selectedItems
|
||||
});
|
||||
|
||||
|
||||
@ -68,7 +72,7 @@ class _$Unp4kcStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of Unp4kcState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? startUp = null,Object? files = freezed,Object? fs = freezed,Object? curPath = null,Object? endMessage = freezed,Object? tempOpenFile = freezed,Object? errorMessage = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? startUp = null,Object? files = freezed,Object? fs = freezed,Object? curPath = null,Object? endMessage = freezed,Object? tempOpenFile = freezed,Object? errorMessage = null,Object? searchQuery = null,Object? isSearching = null,Object? searchFs = freezed,Object? searchMatchedFiles = freezed,Object? sortType = null,Object? isMultiSelectMode = null,Object? selectedItems = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
startUp: null == startUp ? _self.startUp : startUp // ignore: cast_nullable_to_non_nullable
|
||||
as bool,files: freezed == files ? _self.files : files // ignore: cast_nullable_to_non_nullable
|
||||
@ -77,7 +81,14 @@ as MemoryFileSystem?,curPath: null == curPath ? _self.curPath : curPath // ignor
|
||||
as String,endMessage: freezed == endMessage ? _self.endMessage : endMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String?,tempOpenFile: freezed == tempOpenFile ? _self.tempOpenFile : tempOpenFile // ignore: cast_nullable_to_non_nullable
|
||||
as MapEntry<String, String>?,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as String,searchQuery: null == searchQuery ? _self.searchQuery : searchQuery // ignore: cast_nullable_to_non_nullable
|
||||
as String,isSearching: null == isSearching ? _self.isSearching : isSearching // ignore: cast_nullable_to_non_nullable
|
||||
as bool,searchFs: freezed == searchFs ? _self.searchFs : searchFs // ignore: cast_nullable_to_non_nullable
|
||||
as MemoryFileSystem?,searchMatchedFiles: freezed == searchMatchedFiles ? _self.searchMatchedFiles : searchMatchedFiles // ignore: cast_nullable_to_non_nullable
|
||||
as Set<String>?,sortType: null == sortType ? _self.sortType : sortType // ignore: cast_nullable_to_non_nullable
|
||||
as Unp4kSortType,isMultiSelectMode: null == isMultiSelectMode ? _self.isMultiSelectMode : isMultiSelectMode // ignore: cast_nullable_to_non_nullable
|
||||
as bool,selectedItems: null == selectedItems ? _self.selectedItems : selectedItems // ignore: cast_nullable_to_non_nullable
|
||||
as Set<String>,
|
||||
));
|
||||
}
|
||||
|
||||
@ -162,10 +173,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage, String searchQuery, bool isSearching, MemoryFileSystem? searchFs, Set<String>? searchMatchedFiles, Unp4kSortType sortType, bool isMultiSelectMode, Set<String> selectedItems)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Unp4kcState() when $default != null:
|
||||
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage);case _:
|
||||
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage,_that.searchQuery,_that.isSearching,_that.searchFs,_that.searchMatchedFiles,_that.sortType,_that.isMultiSelectMode,_that.selectedItems);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@ -183,10 +194,10 @@ return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessag
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage, String searchQuery, bool isSearching, MemoryFileSystem? searchFs, Set<String>? searchMatchedFiles, Unp4kSortType sortType, bool isMultiSelectMode, Set<String> selectedItems) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Unp4kcState():
|
||||
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage);case _:
|
||||
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage,_that.searchQuery,_that.isSearching,_that.searchFs,_that.searchMatchedFiles,_that.sortType,_that.isMultiSelectMode,_that.selectedItems);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@ -203,10 +214,10 @@ return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessag
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage, String searchQuery, bool isSearching, MemoryFileSystem? searchFs, Set<String>? searchMatchedFiles, Unp4kSortType sortType, bool isMultiSelectMode, Set<String> selectedItems)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Unp4kcState() when $default != null:
|
||||
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage);case _:
|
||||
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage,_that.searchQuery,_that.isSearching,_that.searchFs,_that.searchMatchedFiles,_that.sortType,_that.isMultiSelectMode,_that.selectedItems);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@ -218,7 +229,7 @@ return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessag
|
||||
|
||||
|
||||
class _Unp4kcState with DiagnosticableTreeMixin implements Unp4kcState {
|
||||
const _Unp4kcState({required this.startUp, final Map<String, AppUnp4kP4kItemData>? files, this.fs, required this.curPath, this.endMessage, this.tempOpenFile, this.errorMessage = ""}): _files = files;
|
||||
const _Unp4kcState({required this.startUp, final Map<String, AppUnp4kP4kItemData>? files, this.fs, required this.curPath, this.endMessage, this.tempOpenFile, this.errorMessage = "", this.searchQuery = "", this.isSearching = false, this.searchFs, final Set<String>? searchMatchedFiles, this.sortType = Unp4kSortType.defaultSort, this.isMultiSelectMode = false, final Set<String> selectedItems = const {}}): _files = files,_searchMatchedFiles = searchMatchedFiles,_selectedItems = selectedItems;
|
||||
|
||||
|
||||
@override final bool startUp;
|
||||
@ -236,6 +247,33 @@ class _Unp4kcState with DiagnosticableTreeMixin implements Unp4kcState {
|
||||
@override final String? endMessage;
|
||||
@override final MapEntry<String, String>? tempOpenFile;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
@override@JsonKey() final String searchQuery;
|
||||
@override@JsonKey() final bool isSearching;
|
||||
/// 搜索结果的虚拟文件系统(支持分级展示)
|
||||
@override final MemoryFileSystem? searchFs;
|
||||
/// 搜索匹配的文件路径集合
|
||||
final Set<String>? _searchMatchedFiles;
|
||||
/// 搜索匹配的文件路径集合
|
||||
@override Set<String>? get searchMatchedFiles {
|
||||
final value = _searchMatchedFiles;
|
||||
if (value == null) return null;
|
||||
if (_searchMatchedFiles is EqualUnmodifiableSetView) return _searchMatchedFiles;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableSetView(value);
|
||||
}
|
||||
|
||||
@override@JsonKey() final Unp4kSortType sortType;
|
||||
/// 是否处于多选模式
|
||||
@override@JsonKey() final bool isMultiSelectMode;
|
||||
/// 多选模式下选中的文件路径集合
|
||||
final Set<String> _selectedItems;
|
||||
/// 多选模式下选中的文件路径集合
|
||||
@override@JsonKey() Set<String> get selectedItems {
|
||||
if (_selectedItems is EqualUnmodifiableSetView) return _selectedItems;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableSetView(_selectedItems);
|
||||
}
|
||||
|
||||
|
||||
/// Create a copy of Unp4kcState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -248,21 +286,21 @@ _$Unp4kcStateCopyWith<_Unp4kcState> get copyWith => __$Unp4kcStateCopyWithImpl<_
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'Unp4kcState'))
|
||||
..add(DiagnosticsProperty('startUp', startUp))..add(DiagnosticsProperty('files', files))..add(DiagnosticsProperty('fs', fs))..add(DiagnosticsProperty('curPath', curPath))..add(DiagnosticsProperty('endMessage', endMessage))..add(DiagnosticsProperty('tempOpenFile', tempOpenFile))..add(DiagnosticsProperty('errorMessage', errorMessage));
|
||||
..add(DiagnosticsProperty('startUp', startUp))..add(DiagnosticsProperty('files', files))..add(DiagnosticsProperty('fs', fs))..add(DiagnosticsProperty('curPath', curPath))..add(DiagnosticsProperty('endMessage', endMessage))..add(DiagnosticsProperty('tempOpenFile', tempOpenFile))..add(DiagnosticsProperty('errorMessage', errorMessage))..add(DiagnosticsProperty('searchQuery', searchQuery))..add(DiagnosticsProperty('isSearching', isSearching))..add(DiagnosticsProperty('searchFs', searchFs))..add(DiagnosticsProperty('searchMatchedFiles', searchMatchedFiles))..add(DiagnosticsProperty('sortType', sortType))..add(DiagnosticsProperty('isMultiSelectMode', isMultiSelectMode))..add(DiagnosticsProperty('selectedItems', selectedItems));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Unp4kcState&&(identical(other.startUp, startUp) || other.startUp == startUp)&&const DeepCollectionEquality().equals(other._files, _files)&&(identical(other.fs, fs) || other.fs == fs)&&(identical(other.curPath, curPath) || other.curPath == curPath)&&(identical(other.endMessage, endMessage) || other.endMessage == endMessage)&&(identical(other.tempOpenFile, tempOpenFile) || other.tempOpenFile == tempOpenFile)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Unp4kcState&&(identical(other.startUp, startUp) || other.startUp == startUp)&&const DeepCollectionEquality().equals(other._files, _files)&&(identical(other.fs, fs) || other.fs == fs)&&(identical(other.curPath, curPath) || other.curPath == curPath)&&(identical(other.endMessage, endMessage) || other.endMessage == endMessage)&&(identical(other.tempOpenFile, tempOpenFile) || other.tempOpenFile == tempOpenFile)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.searchQuery, searchQuery) || other.searchQuery == searchQuery)&&(identical(other.isSearching, isSearching) || other.isSearching == isSearching)&&(identical(other.searchFs, searchFs) || other.searchFs == searchFs)&&const DeepCollectionEquality().equals(other._searchMatchedFiles, _searchMatchedFiles)&&(identical(other.sortType, sortType) || other.sortType == sortType)&&(identical(other.isMultiSelectMode, isMultiSelectMode) || other.isMultiSelectMode == isMultiSelectMode)&&const DeepCollectionEquality().equals(other._selectedItems, _selectedItems));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,startUp,const DeepCollectionEquality().hash(_files),fs,curPath,endMessage,tempOpenFile,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,startUp,const DeepCollectionEquality().hash(_files),fs,curPath,endMessage,tempOpenFile,errorMessage,searchQuery,isSearching,searchFs,const DeepCollectionEquality().hash(_searchMatchedFiles),sortType,isMultiSelectMode,const DeepCollectionEquality().hash(_selectedItems));
|
||||
|
||||
@override
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'Unp4kcState(startUp: $startUp, files: $files, fs: $fs, curPath: $curPath, endMessage: $endMessage, tempOpenFile: $tempOpenFile, errorMessage: $errorMessage)';
|
||||
return 'Unp4kcState(startUp: $startUp, files: $files, fs: $fs, curPath: $curPath, endMessage: $endMessage, tempOpenFile: $tempOpenFile, errorMessage: $errorMessage, searchQuery: $searchQuery, isSearching: $isSearching, searchFs: $searchFs, searchMatchedFiles: $searchMatchedFiles, sortType: $sortType, isMultiSelectMode: $isMultiSelectMode, selectedItems: $selectedItems)';
|
||||
}
|
||||
|
||||
|
||||
@ -273,7 +311,7 @@ abstract mixin class _$Unp4kcStateCopyWith<$Res> implements $Unp4kcStateCopyWith
|
||||
factory _$Unp4kcStateCopyWith(_Unp4kcState value, $Res Function(_Unp4kcState) _then) = __$Unp4kcStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage
|
||||
bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage, String searchQuery, bool isSearching, MemoryFileSystem? searchFs, Set<String>? searchMatchedFiles, Unp4kSortType sortType, bool isMultiSelectMode, Set<String> selectedItems
|
||||
});
|
||||
|
||||
|
||||
@ -290,7 +328,7 @@ class __$Unp4kcStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of Unp4kcState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? startUp = null,Object? files = freezed,Object? fs = freezed,Object? curPath = null,Object? endMessage = freezed,Object? tempOpenFile = freezed,Object? errorMessage = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? startUp = null,Object? files = freezed,Object? fs = freezed,Object? curPath = null,Object? endMessage = freezed,Object? tempOpenFile = freezed,Object? errorMessage = null,Object? searchQuery = null,Object? isSearching = null,Object? searchFs = freezed,Object? searchMatchedFiles = freezed,Object? sortType = null,Object? isMultiSelectMode = null,Object? selectedItems = null,}) {
|
||||
return _then(_Unp4kcState(
|
||||
startUp: null == startUp ? _self.startUp : startUp // ignore: cast_nullable_to_non_nullable
|
||||
as bool,files: freezed == files ? _self._files : files // ignore: cast_nullable_to_non_nullable
|
||||
@ -299,7 +337,14 @@ as MemoryFileSystem?,curPath: null == curPath ? _self.curPath : curPath // ignor
|
||||
as String,endMessage: freezed == endMessage ? _self.endMessage : endMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String?,tempOpenFile: freezed == tempOpenFile ? _self.tempOpenFile : tempOpenFile // ignore: cast_nullable_to_non_nullable
|
||||
as MapEntry<String, String>?,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as String,searchQuery: null == searchQuery ? _self.searchQuery : searchQuery // ignore: cast_nullable_to_non_nullable
|
||||
as String,isSearching: null == isSearching ? _self.isSearching : isSearching // ignore: cast_nullable_to_non_nullable
|
||||
as bool,searchFs: freezed == searchFs ? _self.searchFs : searchFs // ignore: cast_nullable_to_non_nullable
|
||||
as MemoryFileSystem?,searchMatchedFiles: freezed == searchMatchedFiles ? _self._searchMatchedFiles : searchMatchedFiles // ignore: cast_nullable_to_non_nullable
|
||||
as Set<String>?,sortType: null == sortType ? _self.sortType : sortType // ignore: cast_nullable_to_non_nullable
|
||||
as Unp4kSortType,isMultiSelectMode: null == isMultiSelectMode ? _self.isMultiSelectMode : isMultiSelectMode // ignore: cast_nullable_to_non_nullable
|
||||
as bool,selectedItems: null == selectedItems ? _self._selectedItems : selectedItems // ignore: cast_nullable_to_non_nullable
|
||||
as Set<String>,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ final class Unp4kCModelProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$unp4kCModelHash() => r'410461980f6173fdbb5d92cbaa3f4c2f57c1ad8d';
|
||||
String _$unp4kCModelHash() => r'b46274b1409dc904db2d96acf692869edf034b9f';
|
||||
|
||||
abstract class _$Unp4kCModel extends $Notifier<Unp4kcState> {
|
||||
Unp4kcState build();
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
@ -13,7 +14,6 @@ import 'package:re_highlight/styles/vs2015.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/data/app_advanced_localization_data.dart';
|
||||
import 'package:starcitizen_doctor/data/sc_localization_data.dart';
|
||||
import 'package:starcitizen_doctor/provider/unp4kc.dart';
|
||||
@ -218,22 +218,19 @@ class AdvancedLocalizationUIModel extends _$AdvancedLocalizationUIModel {
|
||||
|
||||
Future<String> readEnglishInI(String gameDir) async {
|
||||
try {
|
||||
var data = await Unp4kCModel.unp4kTools(appGlobalState.applicationBinaryModuleDir!, [
|
||||
"extract_memory",
|
||||
var data = await Unp4kCModel.extractP4kFileToMemory(
|
||||
"$gameDir\\Data.p4k",
|
||||
"Data\\Localization\\english\\global.ini",
|
||||
]);
|
||||
);
|
||||
// remove bom
|
||||
if (data.length > 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF) {
|
||||
data = data.sublist(3);
|
||||
}
|
||||
final iniData = String.fromCharCodes(data);
|
||||
final iniData = utf8.decode(data, allowMalformed: true);
|
||||
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'4527ea29b07d4e525367d380d2aeb3ece4f99f4f';
|
||||
|
||||
abstract class _$AdvancedLocalizationUIModel
|
||||
extends $Notifier<AdvancedLocalizationUIState> {
|
||||
|
||||
@ -41,7 +41,7 @@ final class PartyRoomUIModelProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$partyRoomUIModelHash() => r'add4703c9129465718a7850ea09025aa1ff35358';
|
||||
String _$partyRoomUIModelHash() => r'b22ad79b6d4a877876b2534f35fb0448b34d4ad5';
|
||||
|
||||
abstract class _$PartyRoomUIModel extends $Notifier<PartyRoomUIState> {
|
||||
PartyRoomUIState build();
|
||||
|
||||
@ -66,7 +66,7 @@ final class PartyRoomGameLogTrackerProviderProvider
|
||||
}
|
||||
|
||||
String _$partyRoomGameLogTrackerProviderHash() =>
|
||||
r'3e1560b2fffc5461a41bece57b43e27f4112ad0c';
|
||||
r'7c9413736b0a3357075ab5309f0e746f0d6e3fc3';
|
||||
|
||||
final class PartyRoomGameLogTrackerProviderFamily extends $Family
|
||||
with
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:file_sizes/file_sizes.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
@ -11,7 +13,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 +29,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,215 +55,133 @@ 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)),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
: Stack(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context)
|
||||
.cardColor
|
||||
.withValues(alpha: .06)),
|
||||
height: 36,
|
||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||
child: SuperListView.builder(
|
||||
itemCount: paths.length - 1,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
var path = paths[index];
|
||||
if (path.isEmpty) {
|
||||
path = "\\";
|
||||
}
|
||||
final fullPath =
|
||||
"${paths.sublist(0, index + 1).join("\\")}\\";
|
||||
return Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Text(path),
|
||||
onPressed: () {
|
||||
model.changeDir(fullPath, fullPath: true);
|
||||
},
|
||||
),
|
||||
const Icon(
|
||||
FluentIcons.chevron_right,
|
||||
size: 12,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Row(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
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(
|
||||
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor.withValues(alpha: .06)),
|
||||
height: 36,
|
||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
// 搜索模式下显示返回按钮
|
||||
if (state.searchFs != null) ...[
|
||||
IconButton(
|
||||
icon: const Icon(FluentIcons.back, size: 14),
|
||||
onPressed: () {
|
||||
if (item.isDirectory ?? false) {
|
||||
model.changeDir(fileName);
|
||||
} else {
|
||||
model.openFile(item.name ?? "");
|
||||
}
|
||||
model.clearSearch();
|
||||
},
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(left: 4, right: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
if (item.isDirectory ?? false)
|
||||
const Icon(
|
||||
FluentIcons.folder_fill,
|
||||
color: Color.fromRGBO(255, 224, 138, 1),
|
||||
)
|
||||
else if (fileName.endsWith(".xml"))
|
||||
const Icon(
|
||||
FluentIcons.file_code,
|
||||
)
|
||||
else
|
||||
const Icon(
|
||||
FluentIcons.open_file,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
fileName,
|
||||
style: const TextStyle(
|
||||
fontSize: 13),
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!(item.isDirectory ?? true)) ...[
|
||||
const SizedBox(height: 1),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
FileSize.getSize(item.size),
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white
|
||||
.withValues(alpha: .6)),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
"${item.dateTime}",
|
||||
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),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: files?.length ?? 0,
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
"[${S.current.tools_unp4k_searching.replaceAll('...', '')}]",
|
||||
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .7)),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
Expanded(
|
||||
child: SuperListView.builder(
|
||||
itemCount: paths.length - 1,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
var path = paths[index];
|
||||
if (path.isEmpty) {
|
||||
path = "\\";
|
||||
}
|
||||
final fullPath = "${paths.sublist(0, index + 1).join("\\")}\\";
|
||||
return Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Text(path),
|
||||
onPressed: () {
|
||||
model.changeDir(fullPath, fullPath: true);
|
||||
},
|
||||
),
|
||||
const Icon(FluentIcons.chevron_right, size: 12),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
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 ?? ""))
|
||||
else
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(S.current
|
||||
.tools_unp4k_msg_unknown_file_type(
|
||||
state.tempOpenFile
|
||||
?.value ??
|
||||
"")),
|
||||
const SizedBox(height: 32),
|
||||
FilledButton(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * .3,
|
||||
child: _FileListPanel(state: state, model: model, files: files),
|
||||
),
|
||||
Expanded(
|
||||
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 ?? ""))
|
||||
else
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
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),
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(S.current.action_open_folder),
|
||||
),
|
||||
onPressed: () {
|
||||
SystemHelper.openDir(state
|
||||
.tempOpenFile
|
||||
?.value ??
|
||||
"");
|
||||
})
|
||||
],
|
||||
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)),
|
||||
),
|
||||
],
|
||||
)),
|
||||
if (state.endMessage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"${state.endMessage}",
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
// 搜索加载遮罩
|
||||
if (state.isSearching)
|
||||
Container(
|
||||
color: Colors.black.withValues(alpha: .7),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 16),
|
||||
Text(S.current.tools_unp4k_searching, style: const TextStyle(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -263,6 +189,528 @@ class UnP4kcUI extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 文件列表面板组件
|
||||
class _FileListPanel extends HookConsumerWidget {
|
||||
final Unp4kcState state;
|
||||
final Unp4kCModel model;
|
||||
final List<AppUnp4kP4kItemData>? files;
|
||||
|
||||
const _FileListPanel({required this.state, required this.model, required this.files});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final searchController = useTextEditingController(text: state.searchQuery);
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor.withValues(alpha: .01)),
|
||||
child: Column(
|
||||
children: [
|
||||
// 搜索栏和排序选择器
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextBox(
|
||||
controller: searchController,
|
||||
placeholder: S.current.tools_unp4k_search_placeholder,
|
||||
prefix: const Padding(padding: EdgeInsets.only(left: 8), child: Icon(FluentIcons.search, size: 14)),
|
||||
suffix: searchController.text.isNotEmpty || state.searchFs != null
|
||||
? IconButton(
|
||||
icon: const Icon(FluentIcons.clear, size: 12),
|
||||
onPressed: () {
|
||||
searchController.clear();
|
||||
model.clearSearch();
|
||||
},
|
||||
)
|
||||
: null,
|
||||
onSubmitted: (value) {
|
||||
model.search(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ComboBox<Unp4kSortType>(
|
||||
value: state.sortType,
|
||||
items: [
|
||||
ComboBoxItem(value: Unp4kSortType.defaultSort, child: Text(S.current.tools_unp4k_sort_default)),
|
||||
ComboBoxItem(value: Unp4kSortType.sizeAsc, child: Text(S.current.tools_unp4k_sort_size_asc)),
|
||||
ComboBoxItem(value: Unp4kSortType.sizeDesc, child: Text(S.current.tools_unp4k_sort_size_desc)),
|
||||
ComboBoxItem(value: Unp4kSortType.dateAsc, child: Text(S.current.tools_unp4k_sort_date_asc)),
|
||||
ComboBoxItem(value: Unp4kSortType.dateDesc, child: Text(S.current.tools_unp4k_sort_date_desc)),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
model.setSortType(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 多选模式工具栏
|
||||
if (state.isMultiSelectMode)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context).accentColor.withValues(alpha: .1),
|
||||
border: Border(
|
||||
bottom: BorderSide(color: FluentTheme.of(context).accentColor.withValues(alpha: .3), width: 1),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
S.current.tools_unp4k_action_export_selected(state.selectedItems.length),
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
const Spacer(),
|
||||
Button(child: Text(S.current.tools_unp4k_action_select_all), onPressed: () => model.selectAll(files)),
|
||||
const SizedBox(width: 4),
|
||||
Button(
|
||||
child: Text(S.current.tools_unp4k_action_deselect_all),
|
||||
onPressed: () => model.deselectAll(files),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
FilledButton(
|
||||
onPressed: state.selectedItems.isNotEmpty ? () => _exportSelected(context) : null,
|
||||
child: Text(S.current.tools_unp4k_action_save_as),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
IconButton(
|
||||
icon: const Icon(FluentIcons.cancel, size: 14),
|
||||
onPressed: () => model.exitMultiSelectMode(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 文件列表
|
||||
Expanded(
|
||||
child: files == null || files!.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
state.searchFs != null ? S.current.tools_unp4k_search_no_result : '',
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: .6)),
|
||||
),
|
||||
)
|
||||
: SuperListView.builder(
|
||||
padding: const EdgeInsets.only(top: 6, bottom: 6, left: 3, right: 12),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = files![index];
|
||||
return _FileListItem(item: item, state: state, model: model);
|
||||
},
|
||||
itemCount: files?.length ?? 0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _exportSelected(BuildContext context) async {
|
||||
final outputDir = await FilePicker.platform.getDirectoryPath(dialogTitle: S.current.tools_unp4k_action_save_as);
|
||||
if (outputDir != null && context.mounted) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (dialogContext) {
|
||||
return _MultiExtractProgressDialog(selectedItems: state.selectedItems, outputDir: outputDir, model: model);
|
||||
},
|
||||
);
|
||||
// 提取完成后退出多选模式
|
||||
model.exitMultiSelectMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 文件列表项组件
|
||||
class _FileListItem extends HookWidget {
|
||||
final AppUnp4kP4kItemData item;
|
||||
final Unp4kcState state;
|
||||
final Unp4kCModel model;
|
||||
|
||||
const _FileListItem({required this.item, required this.state, required this.model});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final flyoutController = useMemoized(() => FlyoutController());
|
||||
// 显示相对于当前路径的文件名
|
||||
final fileName = item.name?.replaceAll(state.curPath.trim(), "") ?? "?";
|
||||
final itemPath = item.name ?? "";
|
||||
final isSelected = state.selectedItems.contains(itemPath);
|
||||
|
||||
return FlyoutTarget(
|
||||
controller: flyoutController,
|
||||
child: GestureDetector(
|
||||
onSecondaryTapUp: (details) => _showContextMenu(context, flyoutController),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 4, bottom: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? FluentTheme.of(context).accentColor.withValues(alpha: .2)
|
||||
: FluentTheme.of(context).cardColor.withValues(alpha: .05),
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
if (state.isMultiSelectMode) {
|
||||
// 多选模式下点击切换选中状态
|
||||
model.toggleSelectItem(itemPath);
|
||||
} else if (item.isDirectory ?? false) {
|
||||
final dirName = item.name?.replaceAll(state.curPath.trim(), "") ?? "";
|
||||
model.changeDir(dirName);
|
||||
} else {
|
||||
model.openFile(item.name ?? "");
|
||||
}
|
||||
},
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(left: 4, right: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
// 多选模式下显示复选框
|
||||
if (state.isMultiSelectMode) ...[
|
||||
Checkbox(
|
||||
checked: isSelected,
|
||||
onChanged: (value) {
|
||||
model.toggleSelectItem(itemPath);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
if (item.isDirectory ?? false)
|
||||
const Icon(FluentIcons.folder_fill, color: Color.fromRGBO(255, 224, 138, 1))
|
||||
else if (fileName.endsWith(".xml"))
|
||||
const Icon(FluentIcons.file_code)
|
||||
else
|
||||
const Icon(FluentIcons.open_file),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(fileName, style: const TextStyle(fontSize: 13), textAlign: TextAlign.start),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!(item.isDirectory ?? true)) ...[
|
||||
const SizedBox(height: 1),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
FileSize.getSize(item.size),
|
||||
style: TextStyle(fontSize: 10, color: Colors.white.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)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showContextMenu(BuildContext context, FlyoutController controller) {
|
||||
// 保存外部 context,因为 flyout 的 context 在关闭后会失效
|
||||
final outerContext = context;
|
||||
controller.showFlyout(
|
||||
autoModeConfiguration: FlyoutAutoConfiguration(preferredMode: FlyoutPlacementMode.bottomCenter),
|
||||
barrierColor: Colors.transparent,
|
||||
builder: (flyoutContext) {
|
||||
return MenuFlyout(
|
||||
items: [
|
||||
MenuFlyoutItem(
|
||||
leading: const Icon(FluentIcons.save_as, size: 16),
|
||||
text: Text(S.current.tools_unp4k_action_save_as),
|
||||
onPressed: () async {
|
||||
Navigator.of(flyoutContext).pop();
|
||||
await _saveAs(outerContext);
|
||||
},
|
||||
),
|
||||
// 多选模式切换
|
||||
if (!state.isMultiSelectMode)
|
||||
MenuFlyoutItem(
|
||||
leading: const Icon(FluentIcons.checkbox_composite, size: 16),
|
||||
text: Text(S.current.tools_unp4k_action_multi_select),
|
||||
onPressed: () {
|
||||
Navigator.of(flyoutContext).pop();
|
||||
model.enterMultiSelectMode();
|
||||
// 自动选中当前项
|
||||
model.toggleSelectItem(item.name ?? "");
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _saveAs(BuildContext context) async {
|
||||
final outputDir = await FilePicker.platform.getDirectoryPath(dialogTitle: S.current.tools_unp4k_action_save_as);
|
||||
if (outputDir != null && context.mounted) {
|
||||
await _showExtractProgressDialog(context, outputDir);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showExtractProgressDialog(BuildContext context, String outputDir) async {
|
||||
await showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (dialogContext) {
|
||||
return _ExtractProgressDialog(item: item, outputDir: outputDir, model: model);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 提取进度对话框
|
||||
class _ExtractProgressDialog extends HookWidget {
|
||||
final AppUnp4kP4kItemData item;
|
||||
final String outputDir;
|
||||
final Unp4kCModel model;
|
||||
|
||||
const _ExtractProgressDialog({required this.item, required this.outputDir, required this.model});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isCancelled = useState(false);
|
||||
final currentFile = useState("");
|
||||
final currentIndex = useState(0);
|
||||
final totalFiles = useState(0);
|
||||
final isCompleted = useState(false);
|
||||
final errorMessage = useState<String?>(null);
|
||||
final extractedCount = useState(0);
|
||||
|
||||
useEffect(() {
|
||||
// 获取文件数量
|
||||
totalFiles.value = model.getFileCountInDirectory(item);
|
||||
|
||||
// 开始提取
|
||||
_startExtraction(isCancelled, currentFile, currentIndex, totalFiles, isCompleted, errorMessage, extractedCount);
|
||||
return null;
|
||||
}, const []);
|
||||
|
||||
final progress = totalFiles.value > 0 ? currentIndex.value / totalFiles.value : 0.0;
|
||||
|
||||
return ContentDialog(
|
||||
title: Text(S.current.tools_unp4k_extract_dialog_title),
|
||||
constraints: const BoxConstraints(maxWidth: 500, maxHeight: 300),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!isCompleted.value && errorMessage.value == null) ...[
|
||||
// 进度条
|
||||
ProgressBar(value: progress * 100),
|
||||
const SizedBox(height: 12),
|
||||
// 进度文本
|
||||
Text(
|
||||
S.current.tools_unp4k_extract_progress(currentIndex.value, totalFiles.value),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// 当前文件
|
||||
Text(
|
||||
S.current.tools_unp4k_extract_current_file(
|
||||
currentFile.value.length > 60
|
||||
? "...${currentFile.value.substring(currentFile.value.length - 60)}"
|
||||
: currentFile.value,
|
||||
),
|
||||
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .7)),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
] else if (errorMessage.value != null) ...[
|
||||
// 错误信息
|
||||
const Icon(FluentIcons.error_badge, size: 48, color: Color(0xFFE81123)),
|
||||
const SizedBox(height: 12),
|
||||
Text(errorMessage.value!, style: const TextStyle(fontSize: 14)),
|
||||
] else ...[
|
||||
// 完成
|
||||
const Icon(FluentIcons.completed_solid, size: 48, color: Color(0xFF107C10)),
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.tools_unp4k_extract_completed(extractedCount.value), style: const TextStyle(fontSize: 14)),
|
||||
],
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
if (!isCompleted.value && errorMessage.value == null)
|
||||
Button(
|
||||
onPressed: () {
|
||||
isCancelled.value = true;
|
||||
},
|
||||
child: Text(S.current.home_action_cancel),
|
||||
)
|
||||
else
|
||||
FilledButton(onPressed: () => Navigator.of(context).pop(), child: Text(S.current.action_close)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _startExtraction(
|
||||
ValueNotifier<bool> isCancelled,
|
||||
ValueNotifier<String> currentFile,
|
||||
ValueNotifier<int> currentIndex,
|
||||
ValueNotifier<int> totalFiles,
|
||||
ValueNotifier<bool> isCompleted,
|
||||
ValueNotifier<String?> errorMessage,
|
||||
ValueNotifier<int> extractedCount,
|
||||
) async {
|
||||
final result = await model.extractToDirectoryWithProgress(
|
||||
item,
|
||||
outputDir,
|
||||
onProgress: (current, total, file) {
|
||||
currentIndex.value = current;
|
||||
totalFiles.value = total;
|
||||
currentFile.value = file;
|
||||
},
|
||||
isCancelled: () => isCancelled.value,
|
||||
);
|
||||
|
||||
final (success, count, error) = result;
|
||||
extractedCount.value = count;
|
||||
|
||||
if (!success && error != null) {
|
||||
errorMessage.value = error;
|
||||
} else {
|
||||
isCompleted.value = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 批量提取进度对话框
|
||||
class _MultiExtractProgressDialog extends HookWidget {
|
||||
final Set<String> selectedItems;
|
||||
final String outputDir;
|
||||
final Unp4kCModel model;
|
||||
|
||||
const _MultiExtractProgressDialog({required this.selectedItems, required this.outputDir, required this.model});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isCancelled = useState(false);
|
||||
final currentFile = useState("");
|
||||
final currentIndex = useState(0);
|
||||
final totalFiles = useState(0);
|
||||
final isCompleted = useState(false);
|
||||
final errorMessage = useState<String?>(null);
|
||||
final extractedCount = useState(0);
|
||||
|
||||
useEffect(() {
|
||||
// 获取文件数量
|
||||
totalFiles.value = model.getFileCountForSelectedItems(selectedItems);
|
||||
|
||||
// 开始提取
|
||||
_startExtraction(isCancelled, currentFile, currentIndex, totalFiles, isCompleted, errorMessage, extractedCount);
|
||||
return null;
|
||||
}, const []);
|
||||
|
||||
final progress = totalFiles.value > 0 ? currentIndex.value / totalFiles.value : 0.0;
|
||||
|
||||
return ContentDialog(
|
||||
title: Text(S.current.tools_unp4k_extract_dialog_title),
|
||||
constraints: const BoxConstraints(maxWidth: 500, maxHeight: 300),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!isCompleted.value && errorMessage.value == null) ...[
|
||||
// 进度条
|
||||
ProgressBar(value: progress * 100),
|
||||
const SizedBox(height: 12),
|
||||
// 进度文本
|
||||
Text(
|
||||
S.current.tools_unp4k_extract_progress(currentIndex.value, totalFiles.value),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// 当前文件
|
||||
Text(
|
||||
S.current.tools_unp4k_extract_current_file(
|
||||
currentFile.value.length > 60
|
||||
? "...${currentFile.value.substring(currentFile.value.length - 60)}"
|
||||
: currentFile.value,
|
||||
),
|
||||
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .7)),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
] else if (errorMessage.value != null) ...[
|
||||
// 错误信息
|
||||
const Icon(FluentIcons.error_badge, size: 48, color: Color(0xFFE81123)),
|
||||
const SizedBox(height: 12),
|
||||
Text(errorMessage.value!, style: const TextStyle(fontSize: 14)),
|
||||
] else ...[
|
||||
// 完成
|
||||
const Icon(FluentIcons.completed_solid, size: 48, color: Color(0xFF107C10)),
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.tools_unp4k_extract_completed(extractedCount.value), style: const TextStyle(fontSize: 14)),
|
||||
],
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
if (!isCompleted.value && errorMessage.value == null)
|
||||
Button(
|
||||
onPressed: () {
|
||||
isCancelled.value = true;
|
||||
},
|
||||
child: Text(S.current.home_action_cancel),
|
||||
)
|
||||
else
|
||||
FilledButton(onPressed: () => Navigator.of(context).pop(), child: Text(S.current.action_close)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _startExtraction(
|
||||
ValueNotifier<bool> isCancelled,
|
||||
ValueNotifier<String> currentFile,
|
||||
ValueNotifier<int> currentIndex,
|
||||
ValueNotifier<int> totalFiles,
|
||||
ValueNotifier<bool> isCompleted,
|
||||
ValueNotifier<String?> errorMessage,
|
||||
ValueNotifier<int> extractedCount,
|
||||
) async {
|
||||
final result = await model.extractSelectedItemsWithProgress(
|
||||
selectedItems,
|
||||
outputDir,
|
||||
onProgress: (current, total, file) {
|
||||
currentIndex.value = current;
|
||||
totalFiles.value = total;
|
||||
currentFile.value = file;
|
||||
},
|
||||
isCancelled: () => isCancelled.value,
|
||||
);
|
||||
|
||||
final (success, count, error) = result;
|
||||
extractedCount.value = count;
|
||||
|
||||
if (!success && error != null) {
|
||||
errorMessage.value = error;
|
||||
} else {
|
||||
isCompleted.value = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _TextTempWidget extends HookConsumerWidget {
|
||||
final String filePath;
|
||||
|
||||
@ -273,20 +721,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).readAsBytes().then((data) {
|
||||
// 处理可能的 BOM
|
||||
if (data.length > 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF) {
|
||||
data = data.sublist(3);
|
||||
}
|
||||
final text = utf8.decode(data, allowMalformed: true);
|
||||
textData.value = text;
|
||||
});
|
||||
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 +743,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)]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
545
rust/Cargo.lock
generated
545
rust/Cargo.lock
generated
@ -17,6 +17,17 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.12"
|
||||
@ -133,6 +144,15 @@ version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asar"
|
||||
version = "0.3.0"
|
||||
@ -179,9 +199,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.33"
|
||||
version = "0.4.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93c1f86859c1af3d514fa19e8323147ff10ea98684e6c7b307912509f50e67b2"
|
||||
checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473"
|
||||
dependencies = [
|
||||
"compression-codecs",
|
||||
"compression-core",
|
||||
@ -363,6 +383,15 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.6.2"
|
||||
@ -385,6 +414,16 @@ dependencies = [
|
||||
"piper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "build-target"
|
||||
version = "0.4.0"
|
||||
@ -415,6 +454,15 @@ version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c"
|
||||
dependencies = [
|
||||
"libbz2-rs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.4"
|
||||
@ -425,12 +473,23 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.46"
|
||||
name = "cbc"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36"
|
||||
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
@ -459,10 +518,20 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.52"
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -470,9 +539,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.52"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -548,9 +617,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "compression-codecs"
|
||||
version = "0.4.32"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "680dc087785c5230f8e8843e2e57ac7c1c90488b6a91b88caa265410568f441b"
|
||||
checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad"
|
||||
dependencies = [
|
||||
"compression-core",
|
||||
"flate2",
|
||||
@ -559,9 +628,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "compression-core"
|
||||
version = "0.4.30"
|
||||
version = "0.4.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a9b614a5787ef0c8802a55766480563cb3a93b435898c422ed2a359cf811582"
|
||||
checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
@ -572,6 +641,19 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
@ -602,6 +684,12 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.18.1"
|
||||
@ -656,6 +744,21 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
@ -822,6 +925,12 @@ version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
||||
|
||||
[[package]]
|
||||
name = "deflate64"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204"
|
||||
|
||||
[[package]]
|
||||
name = "delegate-attr"
|
||||
version = "0.3.0"
|
||||
@ -853,6 +962,17 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.2"
|
||||
@ -892,6 +1012,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -936,6 +1057,12 @@ version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
@ -947,9 +1074,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "endi"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
|
||||
checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
|
||||
|
||||
[[package]]
|
||||
name = "enum-as-inner"
|
||||
@ -1078,6 +1205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"libz-rs-sys",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
@ -1298,6 +1426,25 @@ version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
"log",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.12"
|
||||
@ -1310,7 +1457,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http",
|
||||
"indexmap 2.12.0",
|
||||
"indexmap 2.12.1",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@ -1331,9 +1478,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@ -1400,13 +1547,21 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.3.1"
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
@ -1496,9 +1651,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.18"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56"
|
||||
checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
@ -1671,16 +1826,39 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.0"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.18.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88"
|
||||
dependencies = [
|
||||
"console",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
"unit-prefix",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipconfig"
|
||||
version = "0.3.2"
|
||||
@ -1749,10 +1927,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.82"
|
||||
name = "jobserver"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
|
||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
@ -1765,10 +1953,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
name = "libbz2-rs-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.178"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@ -1781,6 +1975,15 @@ dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-rs-sys"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b484ba8d4f775eeca644c452a56650e544bf7e617f1d170fe7298122ead5222"
|
||||
dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
@ -1810,9 +2013,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
@ -1821,10 +2024,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "mac-notification-sys"
|
||||
version = "0.6.8"
|
||||
name = "lzma-rust2"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ee70bb2bba058d58e252d2944582d634fc884fc9c489a966d428dedcf653e97"
|
||||
checksum = "c60a23ffb90d527e23192f1246b14746e2f7f071cb84476dd879071696c18a4a"
|
||||
dependencies = [
|
||||
"crc",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mac-notification-sys"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65fd3f75411f4725061682ed91f131946e912859d0044d39c4ec0aac818d7621"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"objc2",
|
||||
@ -2300,6 +2513,16 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
@ -2397,6 +2620,12 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppmd-rust"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d558c559f0450f16f2a27a1f017ef38468c1090c9ce63c8e51366232d53717b4"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@ -2449,6 +2678,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.38.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.9"
|
||||
@ -2731,6 +2969,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tokenizers",
|
||||
"tokio",
|
||||
"unp4k_rs",
|
||||
"url",
|
||||
"walkdir",
|
||||
"win32job",
|
||||
@ -2787,9 +3026,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a"
|
||||
checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c"
|
||||
dependencies = [
|
||||
"web-time",
|
||||
"zeroize",
|
||||
@ -2963,15 +3202,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.16.0"
|
||||
version = "3.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1"
|
||||
checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.12.0",
|
||||
"indexmap 2.12.1",
|
||||
"schemars 0.9.0",
|
||||
"schemars 1.1.0",
|
||||
"serde_core",
|
||||
@ -2982,9 +3221,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.16.0"
|
||||
version = "3.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b"
|
||||
checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c"
|
||||
dependencies = [
|
||||
"darling 0.21.3",
|
||||
"proc-macro2",
|
||||
@ -2992,6 +3231,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
@ -3020,9 +3270,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.6"
|
||||
version = "1.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@ -3120,9 +3370,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.110"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3193,7 +3443,7 @@ version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9"
|
||||
dependencies = [
|
||||
"quick-xml",
|
||||
"quick-xml 0.37.5",
|
||||
"thiserror 2.0.17",
|
||||
"windows 0.61.3",
|
||||
"windows-version",
|
||||
@ -3328,9 +3578,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokenizers"
|
||||
version = "0.22.1"
|
||||
version = "0.22.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6475a27088c98ea96d00b39a9ddfb63780d1ad4cceb6f48374349a96ab2b7842"
|
||||
checksum = "b238e22d44a15349529690fb07bd645cf58149a1b1e44d6cb5bd1641ff1a6223"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"aho-corasick",
|
||||
@ -3434,7 +3684,7 @@ version = "0.23.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d"
|
||||
dependencies = [
|
||||
"indexmap 2.12.0",
|
||||
"indexmap 2.12.1",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"winnow",
|
||||
@ -3466,9 +3716,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
@ -3496,9 +3746,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
@ -3507,9 +3757,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.30"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3518,9 +3768,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.34"
|
||||
version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@ -3538,9 +3788,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.20"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||
dependencies = [
|
||||
"sharded-slab",
|
||||
"thread_local",
|
||||
@ -3591,6 +3841,12 @@ version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
@ -3603,6 +3859,34 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "unit-prefix"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
|
||||
|
||||
[[package]]
|
||||
name = "unp4k_rs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/StarCitizenToolBox/unp4k_rs?tag=V0.0.2#02867472dda1c18e81b0f635b8653fa86bd145cb"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"cbc",
|
||||
"clap",
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
"glob",
|
||||
"globset",
|
||||
"indicatif",
|
||||
"quick-xml 0.38.4",
|
||||
"rayon",
|
||||
"thiserror 2.0.17",
|
||||
"zip",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
@ -3629,9 +3913,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ureq-proto"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2"
|
||||
checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"http",
|
||||
@ -3671,13 +3955,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.18.1"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
|
||||
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"js-sys",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@ -3735,9 +4019,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.105"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
|
||||
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@ -3748,9 +4032,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.55"
|
||||
version = "0.4.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0"
|
||||
checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@ -3761,9 +4045,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.105"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
|
||||
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@ -3771,9 +4055,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.105"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
|
||||
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
@ -3784,9 +4068,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.105"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
|
||||
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -3821,9 +4105,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.82"
|
||||
version = "0.3.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1"
|
||||
checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@ -4347,9 +4631,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.13"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -4472,18 +4756,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.27"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
||||
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.27"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -4516,6 +4800,20 @@ name = "zeroize"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
@ -4550,6 +4848,79 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"arbitrary",
|
||||
"bzip2",
|
||||
"constant_time_eq",
|
||||
"crc32fast",
|
||||
"deflate64",
|
||||
"flate2",
|
||||
"getrandom 0.3.4",
|
||||
"hmac",
|
||||
"indexmap 2.12.1",
|
||||
"lzma-rust2",
|
||||
"memchr",
|
||||
"pbkdf2",
|
||||
"ppmd-rust",
|
||||
"sha1",
|
||||
"time",
|
||||
"zeroize",
|
||||
"zopfli",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36134c44663532e6519d7a6dfdbbe06f6f8192bde8ae9ed076e9b213f0e31df7"
|
||||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"crc32fast",
|
||||
"log",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.16+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.8.0"
|
||||
|
||||
@ -30,6 +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.2" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = { version = "0.62.2", features = [
|
||||
|
||||
@ -6,3 +6,4 @@ pub mod rs_process;
|
||||
pub mod win32_api;
|
||||
pub mod asar_api;
|
||||
pub mod ort_api;
|
||||
pub mod unp4k_api;
|
||||
|
||||
192
rust/src/api/unp4k_api.rs
Normal file
192
rust/src/api/unp4k_api.rs
Normal file
@ -0,0 +1,192 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use flutter_rust_bridge::frb;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use unp4k::{P4kEntry, P4kFile};
|
||||
|
||||
/// P4K 文件项信息
|
||||
#[frb(dart_metadata=("freezed"))]
|
||||
pub struct P4kFileItem {
|
||||
/// 文件名/路径
|
||||
pub name: String,
|
||||
/// 是否为目录
|
||||
pub is_directory: bool,
|
||||
/// 文件大小(字节)
|
||||
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 读取器实例(用于保持状态)
|
||||
static GLOBAL_P4K_READER: once_cell::sync::Lazy<Arc<Mutex<Option<P4kFile>>>> =
|
||||
once_cell::sync::Lazy::new(|| Arc::new(Mutex::new(None)));
|
||||
|
||||
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<()> {
|
||||
let path = PathBuf::from(&p4k_path);
|
||||
if !path.exists() {
|
||||
return Err(anyhow!("P4K file not found: {}", p4k_path));
|
||||
}
|
||||
|
||||
// 在后台线程执行阻塞操作
|
||||
let reader = tokio::task::spawn_blocking(move || {
|
||||
let reader = P4kFile::open(&path)?;
|
||||
Ok::<_, anyhow::Error>(reader)
|
||||
})
|
||||
.await??;
|
||||
|
||||
*GLOBAL_P4K_READER.lock().unwrap() = Some(reader);
|
||||
// 清空之前的文件列表缓存
|
||||
GLOBAL_P4K_FILES.lock().unwrap().clear();
|
||||
|
||||
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>> {
|
||||
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,
|
||||
date_modified: dos_datetime_to_millis(entry.mod_date, entry.mod_time),
|
||||
});
|
||||
}
|
||||
|
||||
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()
|
||||
} else {
|
||||
format!("\\{}", file_path)
|
||||
};
|
||||
|
||||
// 获取文件 entry 的克隆
|
||||
let entry = {
|
||||
let files = GLOBAL_P4K_FILES.lock().unwrap();
|
||||
files
|
||||
.get(&normalized_path)
|
||||
.ok_or_else(|| anyhow!("File not found: {}", file_path))?
|
||||
.clone()
|
||||
};
|
||||
|
||||
// 在后台线程执行阻塞的提取操作
|
||||
let data = tokio::task::spawn_blocking(move || {
|
||||
let mut reader = GLOBAL_P4K_READER.lock().unwrap();
|
||||
if reader.is_none() {
|
||||
return Err(anyhow!("P4K reader not initialized"));
|
||||
}
|
||||
let data = reader.as_mut().unwrap().extract_entry(&entry)?;
|
||||
Ok::<_, anyhow::Error>(data)
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// 提取文件到磁盘
|
||||
pub async fn p4k_extract_to_disk(file_path: String, output_path: String) -> Result<()> {
|
||||
let data = p4k_extract_to_memory(file_path).await?;
|
||||
|
||||
// 在后台线程执行阻塞的文件写入操作
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let output = PathBuf::from(&output_path);
|
||||
|
||||
// 创建父目录
|
||||
if let Some(parent) = output.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
std::fs::write(output, data)?;
|
||||
Ok::<_, anyhow::Error>(())
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 关闭 P4K 读取器
|
||||
pub async fn p4k_close() -> Result<()> {
|
||||
*GLOBAL_P4K_READER.lock().unwrap() = None;
|
||||
GLOBAL_P4K_FILES.lock().unwrap().clear();
|
||||
Ok(())
|
||||
}
|
||||
@ -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 = -518970253;
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1801517256;
|
||||
|
||||
// Section: executor
|
||||
|
||||
@ -291,6 +291,151 @@ fn wire__crate__api__ort_api__load_translation_model_impl(
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__unp4k_api__p4k_close_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "p4k_close",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
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_close().await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__unp4k_api__p4k_extract_to_disk_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
file_path: impl CstDecode<String>,
|
||||
output_path: impl CstDecode<String>,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "p4k_extract_to_disk",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let api_file_path = file_path.cst_decode();
|
||||
let api_output_path = output_path.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_extract_to_disk(
|
||||
api_file_path,
|
||||
api_output_path,
|
||||
)
|
||||
.await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__unp4k_api__p4k_extract_to_memory_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
file_path: impl CstDecode<String>,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "p4k_extract_to_memory",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let api_file_path = file_path.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_extract_to_memory(api_file_path).await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__unp4k_api__p4k_get_all_files_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "p4k_get_all_files",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
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_all_files().await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__unp4k_api__p4k_get_file_count_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "p4k_get_file_count",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
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_file_count().await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__unp4k_api__p4k_open_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
p4k_path: impl CstDecode<String>,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::DcoCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "p4k_open",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let api_p4k_path = p4k_path.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_open(api_p4k_path).await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
that: impl CstDecode<crate::api::asar_api::RsiLauncherAsarData>,
|
||||
@ -564,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 {
|
||||
@ -630,6 +781,12 @@ impl CstDecode<u8> for u8 {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl CstDecode<usize> for usize {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> usize {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl SseDecode for flutter_rust_bridge::for_generated::anyhow::Error {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
@ -681,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 {
|
||||
@ -693,6 +857,20 @@ impl SseDecode for Vec<String> {
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for Vec<crate::api::unp4k_api::P4kFileItem> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
let mut len_ = <i32>::sse_decode(deserializer);
|
||||
let mut ans_ = vec![];
|
||||
for idx_ in 0..len_ {
|
||||
ans_.push(<crate::api::unp4k_api::P4kFileItem>::sse_decode(
|
||||
deserializer,
|
||||
));
|
||||
}
|
||||
return ans_;
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for Vec<u8> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
@ -823,6 +1001,24 @@ impl SseDecode for Option<Vec<u8>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for crate::api::unp4k_api::P4kFileItem {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
let mut var_name = <String>::sse_decode(deserializer);
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for crate::api::win32_api::ProcessInfo {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
@ -943,6 +1139,13 @@ impl SseDecode for () {
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {}
|
||||
}
|
||||
|
||||
impl SseDecode for usize {
|
||||
// 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_u64::<NativeEndian>().unwrap() as _
|
||||
}
|
||||
}
|
||||
|
||||
fn pde_ffi_dispatcher_primary_impl(
|
||||
func_id: i32,
|
||||
port: flutter_rust_bridge::for_generated::MessagePort,
|
||||
@ -1024,6 +1227,30 @@ impl flutter_rust_bridge::IntoIntoDart<crate::api::http_api::MyMethod>
|
||||
}
|
||||
}
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
impl flutter_rust_bridge::IntoDart for crate::api::unp4k_api::P4kFileItem {
|
||||
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
|
||||
[
|
||||
self.name.into_into_dart().into_dart(),
|
||||
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()
|
||||
}
|
||||
}
|
||||
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive
|
||||
for crate::api::unp4k_api::P4kFileItem
|
||||
{
|
||||
}
|
||||
impl flutter_rust_bridge::IntoIntoDart<crate::api::unp4k_api::P4kFileItem>
|
||||
for crate::api::unp4k_api::P4kFileItem
|
||||
{
|
||||
fn into_into_dart(self) -> crate::api::unp4k_api::P4kFileItem {
|
||||
self
|
||||
}
|
||||
}
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
impl flutter_rust_bridge::IntoDart for crate::api::win32_api::ProcessInfo {
|
||||
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
|
||||
[
|
||||
@ -1185,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) {
|
||||
@ -1195,6 +1429,16 @@ impl SseEncode for Vec<String> {
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for Vec<crate::api::unp4k_api::P4kFileItem> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<i32>::sse_encode(self.len() as _, serializer);
|
||||
for item in self {
|
||||
<crate::api::unp4k_api::P4kFileItem>::sse_encode(item, serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for Vec<u8> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
@ -1318,6 +1562,17 @@ impl SseEncode for Option<Vec<u8>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for crate::api::unp4k_api::P4kFileItem {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<String>::sse_encode(self.name, serializer);
|
||||
<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);
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for crate::api::win32_api::ProcessInfo {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
@ -1416,6 +1671,16 @@ impl SseEncode for () {
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {}
|
||||
}
|
||||
|
||||
impl SseEncode for usize {
|
||||
// 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_u64::<NativeEndian>(self as _)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
mod io {
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
@ -1508,6 +1773,16 @@ mod io {
|
||||
vec.into_iter().map(CstDecode::cst_decode).collect()
|
||||
}
|
||||
}
|
||||
impl CstDecode<Vec<crate::api::unp4k_api::P4kFileItem>> for *mut wire_cst_list_p_4_k_file_item {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> Vec<crate::api::unp4k_api::P4kFileItem> {
|
||||
let vec = unsafe {
|
||||
let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self);
|
||||
flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len)
|
||||
};
|
||||
vec.into_iter().map(CstDecode::cst_decode).collect()
|
||||
}
|
||||
}
|
||||
impl CstDecode<Vec<u8>> for *mut wire_cst_list_prim_u_8_loose {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> Vec<u8> {
|
||||
@ -1546,6 +1821,18 @@ mod io {
|
||||
vec.into_iter().map(CstDecode::cst_decode).collect()
|
||||
}
|
||||
}
|
||||
impl CstDecode<crate::api::unp4k_api::P4kFileItem> for wire_cst_p_4_k_file_item {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> crate::api::unp4k_api::P4kFileItem {
|
||||
crate::api::unp4k_api::P4kFileItem {
|
||||
name: self.name.cst_decode(),
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl CstDecode<crate::api::win32_api::ProcessInfo> for wire_cst_process_info {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> crate::api::win32_api::ProcessInfo {
|
||||
@ -1596,6 +1883,22 @@ mod io {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl NewWithNullPtr for wire_cst_p_4_k_file_item {
|
||||
fn new_with_null_ptr() -> Self {
|
||||
Self {
|
||||
name: core::ptr::null_mut(),
|
||||
is_directory: Default::default(),
|
||||
size: Default::default(),
|
||||
compressed_size: Default::default(),
|
||||
date_modified: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Default for wire_cst_p_4_k_file_item {
|
||||
fn default() -> Self {
|
||||
Self::new_with_null_ptr()
|
||||
}
|
||||
}
|
||||
impl NewWithNullPtr for wire_cst_process_info {
|
||||
fn new_with_null_ptr() -> Self {
|
||||
Self {
|
||||
@ -1764,6 +2067,50 @@ mod io {
|
||||
)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_close(port_: i64) {
|
||||
wire__crate__api__unp4k_api__p4k_close_impl(port_)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_extract_to_disk(
|
||||
port_: i64,
|
||||
file_path: *mut wire_cst_list_prim_u_8_strict,
|
||||
output_path: *mut wire_cst_list_prim_u_8_strict,
|
||||
) {
|
||||
wire__crate__api__unp4k_api__p4k_extract_to_disk_impl(port_, file_path, output_path)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_extract_to_memory(
|
||||
port_: i64,
|
||||
file_path: *mut wire_cst_list_prim_u_8_strict,
|
||||
) {
|
||||
wire__crate__api__unp4k_api__p4k_extract_to_memory_impl(port_, file_path)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_get_all_files(
|
||||
port_: i64,
|
||||
) {
|
||||
wire__crate__api__unp4k_api__p4k_get_all_files_impl(port_)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_get_file_count(
|
||||
port_: i64,
|
||||
) {
|
||||
wire__crate__api__unp4k_api__p4k_get_file_count_impl(port_)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__unp4k_api__p4k_open(
|
||||
port_: i64,
|
||||
p4k_path: *mut wire_cst_list_prim_u_8_strict,
|
||||
) {
|
||||
wire__crate__api__unp4k_api__p4k_open_impl(port_, p4k_path)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js(
|
||||
port_: i64,
|
||||
@ -1884,6 +2231,20 @@ mod io {
|
||||
flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_cst_new_list_p_4_k_file_item(
|
||||
len: i32,
|
||||
) -> *mut wire_cst_list_p_4_k_file_item {
|
||||
let wrap = wire_cst_list_p_4_k_file_item {
|
||||
ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(
|
||||
<wire_cst_p_4_k_file_item>::new_with_null_ptr(),
|
||||
len,
|
||||
),
|
||||
len,
|
||||
};
|
||||
flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_cst_new_list_prim_u_8_loose(
|
||||
len: i32,
|
||||
@ -1942,6 +2303,12 @@ mod io {
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct wire_cst_list_p_4_k_file_item {
|
||||
ptr: *mut wire_cst_p_4_k_file_item,
|
||||
len: i32,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct wire_cst_list_prim_u_8_loose {
|
||||
ptr: *mut u8,
|
||||
len: i32,
|
||||
@ -1966,6 +2333,15 @@ mod io {
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct wire_cst_p_4_k_file_item {
|
||||
name: *mut wire_cst_list_prim_u_8_strict,
|
||||
is_directory: bool,
|
||||
size: u64,
|
||||
compressed_size: u64,
|
||||
date_modified: i64,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct wire_cst_process_info {
|
||||
pid: u32,
|
||||
name: *mut wire_cst_list_prim_u_8_strict,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user