using Microsoft.UI.Xaml.Media.Imaging; using Microsoft.Win32; using Sandbox.Definitions; using SpaceEngineersBlueprintEditor.Model; using SpaceEngineersBlueprintEditor.SpaceEngineersCore; using System.Collections; using System.Reflection; using VRage.Collections; using VRage.Game; using XFEExtension.NetCore.FileExtension; namespace SpaceEngineersBlueprintEditor.Utilities.Helpers; /// /// 太空工程师帮助类 /// public static class SpaceEngineersHelper { /// /// 加载完成事件 /// public static event EventHandler? LoadComplete; /// /// 普通块 /// public static string[] NormalBlock { get; } = [ "LargeBlockArmorBlock", "LargeBlockArmorRoundSlope" , "LargeBlockArmorRoundCornerInv", "LargeBlockArmorRoundCorner", "LargeBlockArmorSlope2Base", "LargeBlockArmorHalfSlopedCorner", "LargeBlockArmorHalfCorner", "LargeBlockArmorHalfSlopeCorner", "LargeHalfSlopeArmorBlock", "LargeHalfArmorBlock", "LargeBlockArmorSlope2Tip", "LargeBlockArmorBlock", "LargeBlockArmorCornerSquareInverted", "LargeBlockArmorCornerSquare", "LargeBlockArmorCornerInv", "LargeBlockArmorCorner", "LargeBlockArmorSlope", "LargeBlockArmorCorner2Base", "LargeBlockArmorHalfSlopeInverted", "LargeBlockArmorHalfSlopeCornerInverted", "LargeBlockArmorInvCorner2Tip", "LargeBlockArmorInvCorner2Base", "LargeBlockArmorCorner2Tip", "LargeBlockArmorSlopeTransitionBase", "LargeBlockArmorSlopeTransitionTipMirrored", "LargeBlockArmorSlopeTransitionMirrored", "LargeBlockArmorSlopeTransitionBaseMirrored", "LargeBlockArmorSlopeTransitionTip", "LargeArmorPanelLight", "LargeArmorQuarterPanelLight", "LargeArmorHalfPanelLight", "LargeArmor2x1HalfSlopedTipPanelLightLeft", "LargeArmor2x1HalfSlopedPanelLightLeft", "LargeArmor2x1HalfSlopedTipPanelLightRight", "LargeArmor2x1HalfSlopedPanelLightRight", "LargeArmorHalfSlopedPanelLight", "LargeArmor2x1SlopedSideTipPanelLightInv", "LargeArmor2x1SlopedSideBasePanelLightInv", "LargeArmor2x1SlopedSideTipPanelLight", "LargeArmor2x1SlopedSideBasePanelLight", "LargeArmor2x1SlopedPanelTipLight", "LargeArmor2x1SlopedPanelLight", "LargeArmorSlopedSidePanelLight", "LargeArmorSlopedPanelLight", "LargeBlockArmorSlopeTransition", "SmallBlockArmorBlock", "SmallBlockArmorRoundSlope" , "SmallBlockArmorRoundCornerInv", "SmallBlockArmorRoundCorner", "SmallBlockArmorSlope2Base", "SmallBlockArmorHalfSlopedCorner", "SmallBlockArmorHalfCorner", "SmallBlockArmorHalfSlopeCorner", "HalfSlopeArmorBlock", "HalfArmorBlock", "SmallBlockArmorSlope2Tip", "SmallBlockArmorBlock", "SmallBlockArmorCornerSquareInverted", "SmallBlockArmorCornerSquare", "SmallBlockArmorCornerInv", "SmallBlockArmorCorner", "SmallBlockArmorSlope", "SmallBlockArmorCorner2Base", "SmallBlockArmorHalfSlopeInverted", "SmallBlockArmorHalfSlopeCornerInverted", "SmallBlockArmorInvCorner2Tip", "SmallBlockArmorInvCorner2Base", "SmallBlockArmorCorner2Tip", "SmallBlockArmorSlopeTransitionBase", "SmallBlockArmorSlopeTransitionTipMirrored", "SmallBlockArmorSlopeTransitionMirrored", "SmallBlockArmorSlopeTransitionBaseMirrored", "SmallBlockArmorSlopeTransitionTip", "SmallArmorPanelLight", "SmallArmorQuarterPanelLight", "SmallArmorHalfPanelLight", "SmallArmor2x1HalfSlopedTipPanelLightLeft", "SmallArmor2x1HalfSlopedPanelLightLeft", "SmallArmor2x1HalfSlopedTipPanelLightRight", "SmallArmor2x1HalfSlopedPanelLightRight", "SmallArmorHalfSlopedPanelLight", "SmallArmor2x1SlopedSideTipPanelLightInv", "SmallArmor2x1SlopedSideBasePanelLightInv", "SmallArmor2x1SlopedSideTipPanelLight", "SmallArmor2x1SlopedSideBasePanelLight", "SmallArmor2x1SlopedPanelTip", "SmallArmor2x1SlopedPanelLight", "SmallArmorSlopedSidePanelLight", "SmallArmorSlopedPanelLight", "SmallBlockArmorSlopeTransition", ]; /// /// 重甲块 /// public static string[] HeavyBlock { get; } = [ "LargeHeavyBlockArmorBlock", "LargeHeavyBlockArmorRoundSlope" , "LargeHeavyBlockArmorRoundCornerInv", "LargeHeavyBlockArmorRoundCorner", "LargeHeavyBlockArmorSlope2Base", "LargeBlockHeavyArmorHalfSlopedCorner", "LargeBlockHeavyArmorHalfCorner", "LargeBlockHeavyArmorHalfSlopeCorner", "LargeHeavyHalfSlopeArmorBlock", "LargeHeavyHalfArmorBlock", "LargeHeavyBlockArmorSlope2Tip", "LargeHeavyBlockArmorBlock", "LargeBlockHeavyArmorCornerSquareInverted", "LargeBlockHeavyArmorCornerSquare", "LargeHeavyBlockArmorCornerInv", "LargeHeavyBlockArmorCorner", "LargeHeavyBlockArmorSlope", "LargeHeavyBlockArmorCorner2Base", "LargeBlockHeavyArmorHalfSlopeInverted", "LargeBlockHeavyArmorHalfSlopeCornerInverted", "LargeHeavyBlockArmorInvCorner2Tip", "LargeHeavyBlockArmorInvCorner2Base", "LargeHeavyBlockArmorCorner2Tip", "LargeBlockHeavyArmorSlopeTransitionBase", "LargeBlockHeavyArmorSlopeTransitionTipMirrored", "LargeBlockHeavyArmorSlopeTransitionMirrored", "LargeBlockHeavyArmorSlopeTransitionBaseMirrored", "LargeBlockHeavyArmorSlopeTransitionTip", "LargeArmorPanelHeavy", "LargeArmorQuarterPanelHeavy", "LargeArmorHalfPanelHeavy", "LargeArmor2x1HalfSlopedTipPanelHeavyLeft", "LargeArmor2x1HalfSlopedPanelHeavyLeft", "LargeArmor2x1HalfSlopedTipPanelHeavyRight", "LargeArmor2x1HalfSlopedPanelHeavyRight", "LargeArmorHalfSlopedPanelHeavy", "LargeArmor2x1SlopedSideTipPanelHeavyInv", "LargeArmor2x1SlopedSideBasePanelHeavyInv", "LargeArmor2x1SlopedSideTipPanelHeavy", "LargeArmor2x1SlopedSideBasePanelHeavy", "LargeArmor2x1SlopedPanelTipHeavy", "LargeArmor2x1SlopedPanelHeavy", "LargeArmorSlopedSidePanelHeavy", "LargeArmorSlopedPanelHeavy", "LargeBlockHeavyArmorSlopeTransition", "SmallHeavyBlockArmorBlock", "SmallHeavyBlockArmorRoundSlope" , "SmallHeavyBlockArmorRoundCornerInv", "SmallHeavyBlockArmorRoundCorner", "SmallHeavyBlockArmorSlope2Base", "SmallBlockHeavyArmorHalfSlopedCorner", "SmallBlockHeavyArmorHalfCorner", "SmallBlockHeavyArmorHalfSlopeCorner", "HeavyHalfSlopeArmorBlock", "HeavyHalfArmorBlock", "SmallHeavyBlockArmorSlope2Tip", "SmallHeavyBlockArmorBlock", "SmallBlockHeavyArmorCornerSquareInverted", "SmallBlockHeavyArmorCornerSquare", "SmallHeavyBlockArmorCornerInv", "SmallHeavyBlockArmorCorner", "SmallHeavyBlockArmorSlope", "SmallHeavyBlockArmorCorner2Base", "SmallBlockHeavyArmorHalfSlopeInverted", "SmallBlockHeavyArmorHalfSlopeCornerInverted", "SmallHeavyBlockArmorInvCorner2Tip", "SmallHeavyBlockArmorInvCorner2Base", "SmallHeavyBlockArmorCorner2Tip", "SmallBlockHeavyArmorSlopeTransitionBase", "SmallBlockHeavyArmorSlopeTransitionTipMirrored", "SmallBlockHeavyArmorSlopeTransitionMirrored", "SmallBlockHeavyArmorSlopeTransitionBaseMirrored", "SmallBlockHeavyArmorSlopeTransitionTip", "SmallArmorPanelHeavy", "SmallArmorQuarterPanelHeavy", "SmallArmorHalfPanelHeavy", "SmallArmor2x1HalfSlopedTipPanelHeavyLeft", "SmallArmor2x1HalfSlopedPanelHeavyLeft", "SmallArmor2x1HalfSlopedTipPanelHeavyRight", "SmallArmor2x1HalfSlopedPanelHeavyRight", "SmallArmorHalfSlopedPanelHeavy", "SmallArmor2x1SlopedSideTipPanelHeavyInv", "SmallArmor2x1SlopedSideBasePanelHeavyInv", "SmallArmor2x1SlopedSideTipPanelHeavy", "SmallArmor2x1SlopedSideBasePanelHeavy", "SmallArmor2x1SlopedPanelTipHeavy", "SmallArmor2x1SlopedPanelHeavy", "SmallArmorSlopedSidePanelHeavy", "SmallArmorSlopedPanelHeavy", "SmallBlockHeavyArmorSlopeTransition", ]; /// /// 小型块 /// public static string[] SmallBlock { get; } = [ "SmallBlockArmorBlock", "SmallBlockArmorRoundSlope" , "SmallBlockArmorRoundCornerInv", "SmallBlockArmorRoundCorner", "SmallBlockArmorSlope2Base", "SmallBlockArmorHalfSlopedCorner", "SmallBlockArmorHalfCorner", "SmallBlockArmorHalfSlopeCorner", "HalfSlopeArmorBlock", "HalfArmorBlock", "SmallBlockArmorSlope2Tip", "SmallBlockArmorBlock", "SmallBlockArmorCornerSquareInverted", "SmallBlockArmorCornerSquare", "SmallBlockArmorCornerInv", "SmallBlockArmorCorner", "SmallBlockArmorSlope", "SmallBlockArmorCorner2Base", "SmallBlockArmorHalfSlopeInverted", "SmallBlockArmorHalfSlopeCornerInverted", "SmallBlockArmorInvCorner2Tip", "SmallBlockArmorInvCorner2Base", "SmallBlockArmorCorner2Tip", "SmallBlockArmorSlopeTransitionBase", "SmallBlockArmorSlopeTransitionTipMirrored", "SmallBlockArmorSlopeTransitionMirrored", "SmallBlockArmorSlopeTransitionBaseMirrored", "SmallBlockArmorSlopeTransitionTip", "SmallArmorPanelLight", "SmallArmorQuarterPanelLight", "SmallArmorHalfPanelLight", "SmallArmor2x1HalfSlopedTipPanelLightLeft", "SmallArmor2x1HalfSlopedPanelLightLeft", "SmallArmor2x1HalfSlopedTipPanelLightRight", "SmallArmor2x1HalfSlopedPanelLightRight", "SmallArmorHalfSlopedPanelLight", "SmallArmor2x1SlopedSideTipPanelLightInv", "SmallArmor2x1SlopedSideBasePanelLightInv", "SmallArmor2x1SlopedSideTipPanelLight", "SmallArmor2x1SlopedSideBasePanelLight", "SmallArmor2x1SlopedPanelTip", "SmallArmor2x1SlopedPanelLight", "SmallArmorSlopedSidePanelLight", "SmallArmorSlopedPanelLight", "SmallBlockArmorSlopeTransition", "SmallHeavyBlockArmorBlock", "SmallHeavyBlockArmorRoundSlope" , "SmallHeavyBlockArmorRoundCornerInv", "SmallHeavyBlockArmorRoundCorner", "SmallHeavyBlockArmorSlope2Base", "SmallBlockHeavyArmorHalfSlopedCorner", "SmallBlockHeavyArmorHalfCorner", "SmallBlockHeavyArmorHalfSlopeCorner", "HeavyHalfSlopeArmorBlock", "HeavyHalfArmorBlock", "SmallHeavyBlockArmorSlope2Tip", "SmallHeavyBlockArmorBlock", "SmallBlockHeavyArmorCornerSquareInverted", "SmallBlockHeavyArmorCornerSquare", "SmallHeavyBlockArmorCornerInv", "SmallHeavyBlockArmorCorner", "SmallHeavyBlockArmorSlope", "SmallHeavyBlockArmorCorner2Base", "SmallBlockHeavyArmorHalfSlopeInverted", "SmallBlockHeavyArmorHalfSlopeCornerInverted", "SmallHeavyBlockArmorInvCorner2Tip", "SmallHeavyBlockArmorInvCorner2Base", "SmallHeavyBlockArmorCorner2Tip", "SmallBlockHeavyArmorSlopeTransitionBase", "SmallBlockHeavyArmorSlopeTransitionTipMirrored", "SmallBlockHeavyArmorSlopeTransitionMirrored", "SmallBlockHeavyArmorSlopeTransitionBaseMirrored", "SmallBlockHeavyArmorSlopeTransitionTip", "SmallArmorPanelHeavy", "SmallArmorQuarterPanelHeavy", "SmallArmorHalfPanelHeavy", "SmallArmor2x1HalfSlopedTipPanelHeavyLeft", "SmallArmor2x1HalfSlopedPanelHeavyLeft", "SmallArmor2x1HalfSlopedTipPanelHeavyRight", "SmallArmor2x1HalfSlopedPanelHeavyRight", "SmallArmorHalfSlopedPanelHeavy", "SmallArmor2x1SlopedSideTipPanelHeavyInv", "SmallArmor2x1SlopedSideBasePanelHeavyInv", "SmallArmor2x1SlopedSideTipPanelHeavy", "SmallArmor2x1SlopedSideBasePanelHeavy", "SmallArmor2x1SlopedPanelTipHeavy", "SmallArmor2x1SlopedPanelHeavy", "SmallArmorSlopedSidePanelHeavy", "SmallArmorSlopedPanelHeavy", "SmallBlockHeavyArmorSlopeTransition", "Small", ]; /// /// 大型块 /// public static string[] LargeBlock { get; } = [ "LargeBlockArmorBlock", "LargeBlockArmorRoundSlope" , "LargeBlockArmorRoundCornerInv", "LargeBlockArmorRoundCorner", "LargeBlockArmorSlope2Base", "LargeBlockArmorHalfSlopedCorner", "LargeBlockArmorHalfCorner", "LargeBlockArmorHalfSlopeCorner", "LargeHalfSlopeArmorBlock", "LargeHalfArmorBlock", "LargeBlockArmorSlope2Tip", "LargeBlockArmorBlock", "LargeBlockArmorCornerSquareInverted", "LargeBlockArmorCornerSquare", "LargeBlockArmorCornerInv", "LargeBlockArmorCorner", "LargeBlockArmorSlope", "LargeBlockArmorCorner2Base", "LargeBlockArmorHalfSlopeInverted", "LargeBlockArmorHalfSlopeCornerInverted", "LargeBlockArmorInvCorner2Tip", "LargeBlockArmorInvCorner2Base", "LargeBlockArmorCorner2Tip", "LargeBlockArmorSlopeTransitionBase", "LargeBlockArmorSlopeTransitionTipMirrored", "LargeBlockArmorSlopeTransitionMirrored", "LargeBlockArmorSlopeTransitionBaseMirrored", "LargeBlockArmorSlopeTransitionTip", "LargeArmorPanelLight", "LargeArmorQuarterPanelLight", "LargeArmorHalfPanelLight", "LargeArmor2x1HalfSlopedTipPanelLightLeft", "LargeArmor2x1HalfSlopedPanelLightLeft", "LargeArmor2x1HalfSlopedTipPanelLightRight", "LargeArmor2x1HalfSlopedPanelLightRight", "LargeArmorHalfSlopedPanelLight", "LargeArmor2x1SlopedSideTipPanelLightInv", "LargeArmor2x1SlopedSideBasePanelLightInv", "LargeArmor2x1SlopedSideTipPanelLight", "LargeArmor2x1SlopedSideBasePanelLight", "LargeArmor2x1SlopedPanelTipLight", "LargeArmor2x1SlopedPanelLight", "LargeArmorSlopedSidePanelLight", "LargeArmorSlopedPanelLight", "LargeBlockArmorSlopeTransition", "LargeHeavyBlockArmorBlock", "LargeHeavyBlockArmorRoundSlope" , "LargeHeavyBlockArmorRoundCornerInv", "LargeHeavyBlockArmorRoundCorner", "LargeHeavyBlockArmorSlope2Base", "LargeBlockHeavyArmorHalfSlopedCorner", "LargeBlockHeavyArmorHalfCorner", "LargeBlockHeavyArmorHalfSlopeCorner", "LargeHeavyHalfSlopeArmorBlock", "LargeHeavyHalfArmorBlock", "LargeHeavyBlockArmorSlope2Tip", "LargeHeavyBlockArmorBlock", "LargeBlockHeavyArmorCornerSquareInverted", "LargeBlockHeavyArmorCornerSquare", "LargeHeavyBlockArmorCornerInv", "LargeHeavyBlockArmorCorner", "LargeHeavyBlockArmorSlope", "LargeHeavyBlockArmorCorner2Base", "LargeBlockHeavyArmorHalfSlopeInverted", "LargeBlockHeavyArmorHalfSlopeCornerInverted", "LargeHeavyBlockArmorInvCorner2Tip", "LargeHeavyBlockArmorInvCorner2Base", "LargeHeavyBlockArmorCorner2Tip", "LargeBlockHeavyArmorSlopeTransitionBase", "LargeBlockHeavyArmorSlopeTransitionTipMirrored", "LargeBlockHeavyArmorSlopeTransitionMirrored", "LargeBlockHeavyArmorSlopeTransitionBaseMirrored", "LargeBlockHeavyArmorSlopeTransitionTip", "LargeArmorPanelHeavy", "LargeArmorQuarterPanelHeavy", "LargeArmorHalfPanelHeavy", "LargeArmor2x1HalfSlopedTipPanelHeavyLeft", "LargeArmor2x1HalfSlopedPanelHeavyLeft", "LargeArmor2x1HalfSlopedTipPanelHeavyRight", "LargeArmor2x1HalfSlopedPanelHeavyRight", "LargeArmorHalfSlopedPanelHeavy", "LargeArmor2x1SlopedSideTipPanelHeavyInv", "LargeArmor2x1SlopedSideBasePanelHeavyInv", "LargeArmor2x1SlopedSideTipPanelHeavy", "LargeArmor2x1SlopedSideBasePanelHeavy", "LargeArmor2x1SlopedPanelTipHeavy", "LargeArmor2x1SlopedPanelHeavy", "LargeArmorSlopedSidePanelHeavy", "LargeArmorSlopedPanelHeavy", "LargeBlockHeavyArmorSlopeTransition", "Large", ]; /// /// 是否已经加载完成 /// public static bool IsLoadComplete { get; private set; } = false; /// /// 所有类型定义集 /// public static DictionaryValuesReader AllDefinitions => MyDefinitionManager.Static.GetAllDefinitions(); /// /// 方块定义集 /// public static IEnumerable CubeBlockDefinitions => AllDefinitions.Where(definition => definition is MyCubeBlockDefinition).Cast(); /// /// 组件定义集 /// public static IEnumerable ComponentDefinitions => AllDefinitions.Where(definition => definition is MyComponentDefinition).Cast(); /// /// 物品定义集 /// public static IEnumerable ItemDefinitions => AllDefinitions.Where(definition => definition is MyPhysicalItemDefinition).Cast(); /// /// 场景定义集 /// public static IEnumerable ScenarioDefinition => AllDefinitions.Where(definition => definition is MyScenarioDefinition).Cast(); /// /// 加载蓝图模型 /// /// 蓝图文件路径 /// 蓝图模型(异步) public static async Task LoadBlueprintModel(string blueprintFile) => new() { ViewData = LoadBlueprintInfo(blueprintFile)?.ToBlueprintInfoViewData(), BlueprintDefinitions = await LoadBlueprintAsync(blueprintFile) }; /// /// 异步加载蓝图定义 /// /// 蓝图文件路径 /// 蓝图定义(异步) public static async Task LoadBlueprintAsync(string blueprintFile) => await Task.Run(() => SpaceEngineerDefinitions.Load(blueprintFile)); /// /// 加载蓝图信息 /// /// 蓝图文件路径 /// 蓝图信息 public static BlueprintInfo? LoadBlueprintInfo(string blueprintFile) { var rootPath = Path.GetDirectoryName(blueprintFile); var imagePath = $@"{rootPath}\thumb.png"; return new(imagePath, File.Exists(blueprintFile), File.Exists(imagePath), Path.GetFileName(rootPath)!, new FileInfo(blueprintFile).Length.FileSize(), blueprintFile); } /// /// 分析蓝图定义 /// /// 蓝图属性视图数据 /// 不希望分析的属性名称列表 public static void AnalyzeBlueprint(BlueprintPropertyViewData blueprintPropertyViewData, params string[] exceptedName) { if (blueprintPropertyViewData.IsAnalyzed) return; blueprintPropertyViewData.IsAnalyzed = true; blueprintPropertyViewData.Children.Clear(); if (CheckIfNull(blueprintPropertyViewData)) return; if (IsCyclicReference(blueprintPropertyViewData)) return; if (IsSpecialObject(blueprintPropertyViewData)) return; var canMutateParent = !blueprintPropertyViewData.Type!.IsValueType || blueprintPropertyViewData.ValueSetter is not null; foreach (var member in GetBrowsableMembers(blueprintPropertyViewData.Type, exceptedName)) { try { if (member is FieldInfo fieldInfo) { var value = fieldInfo.GetValue(blueprintPropertyViewData.Value); var child = new BlueprintPropertyViewData() { Type = fieldInfo.FieldType, Name = fieldInfo.Name, Value = value, Parent = blueprintPropertyViewData, ValueChanged = blueprintPropertyViewData.ValueChanged, ValueSetter = canMutateParent && !fieldInfo.IsInitOnly && !fieldInfo.IsLiteral ? targetValue => fieldInfo.SetValue(blueprintPropertyViewData.Value, targetValue) : null }; SetDetail(child, value); blueprintPropertyViewData.Children.Add(child); } else if (member is PropertyInfo propertyInfo) { var value = propertyInfo.GetValue(blueprintPropertyViewData.Value); var child = new BlueprintPropertyViewData() { Type = propertyInfo.PropertyType, Name = propertyInfo.Name, Value = value, Parent = blueprintPropertyViewData, ValueChanged = blueprintPropertyViewData.ValueChanged, ValueSetter = canMutateParent && propertyInfo.SetMethod?.IsPublic == true ? targetValue => propertyInfo.SetValue(blueprintPropertyViewData.Value, targetValue) : null }; SetDetail(child, value); blueprintPropertyViewData.Children.Add(child); } } catch (Exception ex) { blueprintPropertyViewData.Children.Add(new() { Type = member is FieldInfo field ? field.FieldType : member is PropertyInfo property ? property.PropertyType : null, Name = member.Name, Parent = blueprintPropertyViewData, ValueChanged = blueprintPropertyViewData.ValueChanged, HasError = true, ErrorMessage = ex.GetBaseException().Message, CustomData = ex.GetBaseException().Message }); } } } private static IEnumerable GetBrowsableMembers(Type type, string[] exceptedNames) { const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public; var fields = type.GetFields(flags) .Where(field => !field.IsStatic) .Cast(); var properties = type.GetProperties(flags) .Where(property => property.GetMethod?.IsPublic == true && property.GetIndexParameters().Length == 0) .Cast(); return fields .Concat(properties) .Where(member => !exceptedNames.Contains(member.Name, StringComparer.Ordinal)) .GroupBy(member => member.Name, StringComparer.Ordinal) .Select(group => group.First()) .OrderBy(member => member.Name, StringComparer.OrdinalIgnoreCase); } /// /// 检测是否为Null /// /// 蓝图属性视图数据 /// 是否为Null private static bool CheckIfNull(BlueprintPropertyViewData blueprintPropertyViewData) { if (blueprintPropertyViewData.Value is null || blueprintPropertyViewData.Type is null) { blueprintPropertyViewData.CustomData = "NullObject".GetLocalized(); return true; } return false; } private static bool IsCyclicReference(BlueprintPropertyViewData blueprintPropertyViewData) { if (blueprintPropertyViewData.Type?.IsValueType != false) return false; for (var ancestor = blueprintPropertyViewData.Parent; ancestor is not null; ancestor = ancestor.Parent) { if (!ReferenceEquals(ancestor.Value, blueprintPropertyViewData.Value)) continue; blueprintPropertyViewData.CustomData = "Cyclic reference"; return true; } return false; } /// /// 是否是特殊对象 /// /// 蓝图属性视图数据 /// 是否是特殊对象 private static bool IsSpecialObject(BlueprintPropertyViewData blueprintPropertyViewData) { if (blueprintPropertyViewData.IsEnumerable) { AddArrayOrEnumerableChildren(blueprintPropertyViewData); return true; } return false; } /// /// 添加数组或者枚举类型子数据 /// /// 蓝图属性视图数据 private static void AddArrayOrEnumerableChildren(BlueprintPropertyViewData blueprintPropertyViewData) { var enumerable = (IEnumerable)blueprintPropertyViewData.Value!; var list = enumerable as IList; var index = 0; foreach (var item in enumerable) { var type = item?.GetType() ?? GetEnumerableElementType(blueprintPropertyViewData.Type!) ?? typeof(object); var itemIndex = index; var child = new BlueprintPropertyViewData() { Type = type, Name = $"[{itemIndex}]", DisplayNameOverride = $"[{itemIndex}] {type.Name}", Value = item, Parent = blueprintPropertyViewData, ValueChanged = blueprintPropertyViewData.ValueChanged, ValueSetter = list is not null && !list.IsReadOnly ? targetValue => list[itemIndex] = targetValue : null }; SetDetail(child, item); blueprintPropertyViewData.Children.Add(child); index++; } } private static Type? GetEnumerableElementType(Type enumerableType) { if (enumerableType.IsArray) return enumerableType.GetElementType(); return enumerableType .GetInterfaces() .Append(enumerableType) .FirstOrDefault(type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) ?.GetGenericArguments()[0]; } /// /// 设置定义细节 /// /// 蓝图属性视图数据 /// 目标定义值 public static void SetDetail(BlueprintPropertyViewData blueprintPropertyViewData, object? value) { if (value is MyObjectBuilder_CubeBlock myObjectBuilder_CubeBlock) { try { var cubeBlock = MyDefinitionManager.Static.GetCubeBlockDefinition(myObjectBuilder_CubeBlock); var blockName = cubeBlock?.DisplayNameText ?? myObjectBuilder_CubeBlock.SubtypeName ?? myObjectBuilder_CubeBlock.GetType().Name; if (blueprintPropertyViewData.Name?.StartsWith('[') == true) blueprintPropertyViewData.DisplayNameOverride = $"{blockName} {blueprintPropertyViewData.Name}"; if (cubeBlock?.Icons is { Length: > 0 }) { var imagePath = @$"{AppPath.DefinitionImages}\{FileHelper.ChangeExtension(cubeBlock.Icons[0], "png")}"; if (File.Exists(imagePath)) blueprintPropertyViewData.CubeImage = new BitmapImage(new Uri(imagePath)); } } catch { // Mod blocks can legitimately have no currently loaded definition or icon. } blueprintPropertyViewData.CustomData = value is MyObjectBuilder_TerminalBlock terminalBlock ? terminalBlock.CustomName : myObjectBuilder_CubeBlock.SubtypeName; } else if (value is MyObjectBuilder_CubeGrid myObjectBuilder_CubeGrid) { blueprintPropertyViewData.CustomData = myObjectBuilder_CubeGrid.DisplayName; if (blueprintPropertyViewData.Name?.StartsWith('[') == true) blueprintPropertyViewData.DisplayNameOverride = $"{myObjectBuilder_CubeGrid.DisplayName} {blueprintPropertyViewData.Name}"; } else if (blueprintPropertyViewData.IsEnumerable) { var count = value is ICollection collection ? collection.Count : value is null ? 0 : ((IEnumerable)value).Cast().Count(); blueprintPropertyViewData.CustomData = $"{"Amount".GetLocalized()}: {count}"; } else { blueprintPropertyViewData.CustomData = blueprintPropertyViewData.IsBasicType ? blueprintPropertyViewData.ValueString : null; } } /// /// 异步加载定义集列表 /// /// 异步任务 public static async Task LoadDefinitionViewDataListAsync() { await Helper.Wait(() => Initializer.IsDefinitionsLoadComplete); foreach (var definition in AllDefinitions) if (definition is not null && definition.Icons is not null && definition.Icons.Length > 0) { var originalPath = @$"{SpaceEngineersPath.SpaceEngineerContentPath}\{definition.Icons[0]}"; var targetPath = @$"{AppPath.DefinitionImages}\{FileHelper.ChangeExtension(definition.Icons[0], "png")}"; FileHelper.AutoCreateDirectory(targetPath); if (!File.Exists(targetPath)) FileHelper.ToBitmap(originalPath, targetPath); } IsLoadComplete = true; LoadComplete?.Invoke(null, EventArgs.Empty); } /// /// 获取定义集 /// /// 哈希值 /// 定义集 public static MyDefinitionBase? GetDefinition(int m_hash) => AllDefinitions.First(definition => definition.Id.SubtypeId.m_hash == m_hash); /// /// 获取游戏的根目录路径 /// /// 游戏的根目录路径 public static string? GetGameRootPath() { var path = GetGameRootPath(@"C:\", @"D:\", @"E:\", @"F:\", @"G:\", @"H:\", @"I:\", @"J:\", @"K:\", @"L:\", @"M:\", @"N:\", @"O:\", @"P:\", @"Q:\", @"R:\", @"S:\", @"T:\", @"U:\", @"V:\", @"W:\", @"X:\", @"Y:\", @"Z:\"); if (path is not null) return path; if ((Environment.Is64BitProcess ? Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 244850", false) : Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 244850", false)) is RegistryKey registryKey && registryKey.GetValue("InstallLocation")?.ToString() is string stringValue && !string.IsNullOrEmpty(stringValue)) { return stringValue; } path = GetSteamFilePath(); if (!string.IsNullOrEmpty(path)) return Path.Combine(path, @"SteamApps\common\SpaceEngineers"); return null; } /// /// 获取游戏的根目录路径 /// /// 需要检测的磁盘索引 /// public static string? GetGameRootPath(params string[] roots) { foreach (var root in roots) { var targetPath = $@"{root}SteamLibrary\steamapps\common\SpaceEngineers\Bin64\SpaceEngineers.exe"; if (File.Exists(targetPath)) return $@"{root}SteamLibrary\steamapps\common\SpaceEngineers"; } return null; } /// /// 获取Steam游戏文件的根目录 /// /// public static string? GetSteamFilePath() { if ((Environment.Is64BitProcess ? Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Valve\Steam", false) : Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Valve\Steam", false)) is RegistryKey registryKey) return registryKey.GetValue("InstallPath")?.ToString(); else return null; } /// /// 字符串逐个替换 /// /// /// /// /// public static string ForeachReplace(string targetString, string[] sourceArray, string[] targetArray) { int i = 0; foreach (var sourceString in sourceArray) { targetString = targetString.Replace(sourceString, targetArray[i]); i++; } return targetString; } }