diff --git a/src/StudioCore/AssetBrowser/AssetBrowserScreen.cs b/src/StudioCore/AssetBrowser/AssetBrowserScreen.cs index 3eab16ef5..6cbfd6471 100644 --- a/src/StudioCore/AssetBrowser/AssetBrowserScreen.cs +++ b/src/StudioCore/AssetBrowser/AssetBrowserScreen.cs @@ -4,7 +4,7 @@ using StudioCore.Banks.AliasBank; using StudioCore.Editor; using StudioCore.Gui; -using StudioCore.Interface; +using StudioCore.Editor; using StudioCore.MsbEditor; using StudioCore.Platform; using StudioCore.Scene; @@ -218,7 +218,7 @@ public void OnGui() if (ImGui.Begin($@"Asset Browser: Category##{SourceType}AssetBrowser_CategoryList")) { ImGui.Separator(); - ImguiUtils.WrappedText("Categories:"); + Editor.EditorDecorations.WrappedText("Categories:"); ImGui.Separator(); DisplayCategoryList(); @@ -235,7 +235,7 @@ public void OnGui() DisplayTopSection(); ImGui.Separator(); - ImguiUtils.WrappedText("Assets:"); + Editor.EditorDecorations.WrappedText("Assets:"); ImGui.Separator(); DisplayBrowserList(AssetCategoryType.Character, _characterNameCache, chrReferenceDict); @@ -255,7 +255,7 @@ public void OnGui() ImGui.Indent(10.0f); ImGui.Separator(); - ImguiUtils.WrappedText("Actions:"); + Editor.EditorDecorations.WrappedText("Actions:"); ImGui.Separator(); if (SourceType == AssetBrowserSource.MapEditor) @@ -283,7 +283,7 @@ private void DisplayTopSection() { ImGui.Separator(); ImGui.InputText($"Search", ref _searchInput, 255); - ImguiUtils.ShowHoverTooltip("Separate terms are split via the + character."); + Editor.EditorDecorations.ShowHoverTooltip("Separate terms are split via the + character."); } private void DisplayCategoryList() @@ -327,7 +327,7 @@ private void DisplayCategoryList() if (CFG.Current.AssetBrowser_ShowAliasesInBrowser) { var labelName = AliasUtils.GetMapNameAlias(mapId); - AliasUtils.DisplayAlias(labelName); + EditorDecorations.DisplayAlias(labelName); } } } @@ -393,14 +393,14 @@ private void DisplayBrowserList(AssetCategoryType assetType, List nameCa if (CFG.Current.AssetBrowser_ShowAliasesInBrowser) { var aliasName = referenceDict[lowerName].name; - AliasUtils.DisplayAlias(aliasName); + EditorDecorations.DisplayAlias(aliasName); } // Tags if (CFG.Current.AssetBrowser_ShowTagsInBrowser) { var tagString = string.Join(" ", referenceDict[lowerName].tags); - AliasUtils.DisplayTagAlias(tagString); + EditorDecorations.DisplayTagAlias(tagString); } } } @@ -462,14 +462,14 @@ private void DisplayBrowserList_MapPiece(AssetCategoryType assetType, Dictionary if (CFG.Current.AssetBrowser_ShowAliasesInBrowser) { var aliasName = referenceDict[lowerName].name; - AliasUtils.DisplayAlias(aliasName); + EditorDecorations.DisplayAlias(aliasName); } // Tags if (CFG.Current.AssetBrowser_ShowTagsInBrowser) { var tagString = string.Join(" ", referenceDict[lowerName].tags); - AliasUtils.DisplayTagAlias(tagString); + EditorDecorations.DisplayTagAlias(tagString); } } } @@ -483,41 +483,41 @@ private void DisplayActions_MapEditor() if (_selectedName == null || _selectedName == "") return; - ImguiUtils.WrappedText("Apply the selected asset attributes to your current object selection."); - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.WrappedText("Apply the selected asset attributes to your current object selection."); + Editor.EditorDecorations.WrappedText(""); ImGui.Checkbox("Update Name of Selected Object", ref CFG.Current.AssetBrowser_UpdateName); - ImguiUtils.ShowHoverTooltip("Update the Name property of the selected entity when it is changed to a selected asset."); + Editor.EditorDecorations.ShowHoverTooltip("Update the Name property of the selected entity when it is changed to a selected asset."); if (Locator.AssetLocator.Type is GameType.EldenRing or GameType.ArmoredCoreVI) { ImGui.Checkbox("Update Instance ID of Selected Object", ref CFG.Current.AssetBrowser_UpdateInstanceID); - ImguiUtils.ShowHoverTooltip("Update the Name property of the selected entity when it is changed to a selected asset."); - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.ShowHoverTooltip("Update the Name property of the selected entity when it is changed to a selected asset."); + Editor.EditorDecorations.WrappedText(""); } if (ImGui.Button("Apply##action_Asset_Apply", new Vector2(200, 32))) { ApplyMapAssetSelection(); } - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.WrappedText(""); ImGui.Separator(); - ImguiUtils.WrappedText("Alias:"); + Editor.EditorDecorations.WrappedText("Alias:"); ImGui.Separator(); - ImguiUtils.WrappedText("Update the stored name and tag list for the selected asset here."); - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.WrappedText("Update the stored name and tag list for the selected asset here."); + Editor.EditorDecorations.WrappedText(""); - ImguiUtils.WrappedText("Name:"); + Editor.EditorDecorations.WrappedText("Name:"); ImGui.InputText($"##Name", ref _refUpdateName, 255); - ImguiUtils.ShowHoverTooltip("Alias name given to this asset."); - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.ShowHoverTooltip("Alias name given to this asset."); + Editor.EditorDecorations.WrappedText(""); - ImguiUtils.WrappedText("Tags:"); + Editor.EditorDecorations.WrappedText("Tags:"); ImGui.InputText($"##Tags", ref _refUpdateTags, 255); - ImguiUtils.ShowHoverTooltip("Tags associated with this asset. Tags are separated with the , character."); - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.ShowHoverTooltip("Tags associated with this asset. Tags are separated with the , character."); + Editor.EditorDecorations.WrappedText(""); if (ImGui.Button("Update##action_AssetAlias_Update", new Vector2(200, 32))) { @@ -535,31 +535,31 @@ private void DisplayActions_ModelEditor() if (_selectedName == null || _selectedName == "") return; - ImguiUtils.WrappedText("Load the selected asset."); - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.WrappedText("Load the selected asset."); + Editor.EditorDecorations.WrappedText(""); if (ImGui.Button("Load##action_Asset_Load", new Vector2(200, 32))) { LoadModelAssetSelection(); } - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.WrappedText(""); ImGui.Separator(); - ImguiUtils.WrappedText("Alias:"); + Editor.EditorDecorations.WrappedText("Alias:"); ImGui.Separator(); - ImguiUtils.WrappedText("Update the stored name and tag list for the selected asset here."); - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.WrappedText("Update the stored name and tag list for the selected asset here."); + Editor.EditorDecorations.WrappedText(""); - ImguiUtils.WrappedText("Name:"); + Editor.EditorDecorations.WrappedText("Name:"); ImGui.InputText($"##Name", ref _refUpdateName, 255); - ImguiUtils.ShowHoverTooltip("Alias name given to this asset."); - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.ShowHoverTooltip("Alias name given to this asset."); + Editor.EditorDecorations.WrappedText(""); - ImguiUtils.WrappedText("Tags:"); + Editor.EditorDecorations.WrappedText("Tags:"); ImGui.InputText($"##Tags", ref _refUpdateTags, 255); - ImguiUtils.ShowHoverTooltip("Tags associated with this asset. Tags are separated with the , character."); - ImguiUtils.WrappedText(""); + Editor.EditorDecorations.ShowHoverTooltip("Tags associated with this asset. Tags are separated with the , character."); + Editor.EditorDecorations.WrappedText(""); if (ImGui.Button("Update##action_AssetAlias_Update", new Vector2(200, 32))) { diff --git a/src/StudioCore/Interface/AliasUtils.cs b/src/StudioCore/Banks/AliasBank/AliasUtils.cs similarity index 93% rename from src/StudioCore/Interface/AliasUtils.cs rename to src/StudioCore/Banks/AliasBank/AliasUtils.cs index 01906485d..0bf97c36d 100644 --- a/src/StudioCore/Interface/AliasUtils.cs +++ b/src/StudioCore/Banks/AliasBank/AliasUtils.cs @@ -14,30 +14,9 @@ using System.Threading.Tasks; using static Andre.Native.ImGuiBindings; -namespace StudioCore.Interface; +namespace StudioCore.Banks.AliasBank; public static class AliasUtils { - public static void DisplayAlias(string aliasName) - { - if (aliasName != "") - { - ImGui.SameLine(); - ImGui.PushTextWrapPos(0); - ImGui.TextColored(new Vector4(1.0f, 1.0f, 0.0f, 1.0f), @$"{aliasName}"); - ImGui.PopTextWrapPos(); - } - } - public static void DisplayTagAlias(string aliasName) - { - if (aliasName != "") - { - ImGui.SameLine(); - ImGui.PushTextWrapPos(0); - ImGui.TextColored(new Vector4(0.0f, 1.0f, 0.0f, 1.0f), @$"[{aliasName}]"); - ImGui.PopTextWrapPos(); - } - } - public static string GetTagListString(List refTagList) { var tagListStr = ""; diff --git a/src/StudioCore/CFG.cs b/src/StudioCore/CFG.cs index 820cd00a6..b20235a1b 100644 --- a/src/StudioCore/CFG.cs +++ b/src/StudioCore/CFG.cs @@ -17,7 +17,7 @@ internal partial class CfgSerializerContext : JsonSerializerContext { } -public class CFG +public partial class CFG { public const string FolderName = "DSMapStudio"; public const string Config_FileName = "DSMapStudio_Config.json"; @@ -28,9 +28,6 @@ public class CFG private static readonly object _lock_SaveLoadCFG = new(); - //private string _Param_Export_Array_Delimiter = "|"; - private string _Param_Export_Delimiter = ","; - // JsonExtensionData stores info in config file not present in class in order to retain settings between versions. #pragma warning disable IDE0051 [JsonExtensionData] public IDictionary AdditionalData; @@ -65,140 +62,8 @@ public class CFG public bool MapAliases_ShowTagsInBrowser = true; public bool MapAliases_ShowAliasAddition = false; - // Settings: Map Editor - public bool Viewport_Enable_Selection_Outline = false; - - public bool MapEditor_MapObjectList_ShowMapNames = true; - public bool MapEditor_MapObjectList_ShowCharacterNames = true; - public bool MapEditor_MapObjectList_ShowAssetNames = true; - public bool MapEditor_MapObjectList_ShowMapPieceNames = true; - public bool MapEditor_MapObjectList_ShowPlayerCharacterNames = true; - public bool MapEditor_MapObjectList_ShowSystemCharacterNames = true; - public bool MapEditor_MapObjectList_ShowTreasureNames = true; - - public bool EnableFrustrumCulling = false; - public bool Map_AlwaysListLoadedMaps = true; - public bool EnableEldenRingAutoMapOffset = true; - - public bool Map_EnableViewportGrid = false; - public int Map_ViewportGridType = 0; - public Vector3 GFX_Viewport_Grid_Color = Utils.GetDecimalColor(Color.Red); - public int Map_ViewportGrid_TotalSize = 1000; - public int Map_ViewportGrid_IncrementSize = 10; - - public float Map_ViewportGrid_Offset = 0; - - public float Map_ViewportGrid_ShortcutIncrement = 1; - - public float Map_MoveSelectionToCamera_Radius = 3.0f; - public float GFX_Camera_FOV { get; set; } = 60.0f; - public float GFX_Camera_MoveSpeed_Slow { get; set; } = 1.0f; - public float GFX_Camera_MoveSpeed_Normal { get; set; } = 20.0f; - public float GFX_Camera_MoveSpeed_Fast { get; set; } = 200.0f; - public float GFX_Camera_Sensitivity { get; set; } = 0.0160f; - public float GFX_RenderDistance_Max { get; set; } = 50000.0f; - public float Map_ArbitraryRotation_X_Shift { get; set; } = 90.0f; - public float Map_ArbitraryRotation_Y_Shift { get; set; } = 90.0f; - - public float GFX_Framerate_Limit_Unfocused = 20.0f; - public float GFX_Framerate_Limit = 60.0f; - public uint GFX_Limit_Buffer_Flver_Bone = 65536; - public uint GFX_Limit_Buffer_Indirect_Draw = 50000; - public int GFX_Limit_Renderables = 50000; - - public float GFX_Wireframe_Color_Variance = 0.11f; - - public Vector3 GFX_Renderable_Box_BaseColor = Utils.GetDecimalColor(Color.Blue); - public Vector3 GFX_Renderable_Box_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - public Vector3 GFX_Renderable_Cylinder_BaseColor = Utils.GetDecimalColor(Color.Blue); - public Vector3 GFX_Renderable_Cylinder_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - public Vector3 GFX_Renderable_Sphere_BaseColor = Utils.GetDecimalColor(Color.Blue); - public Vector3 GFX_Renderable_Sphere_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - public Vector3 GFX_Renderable_Point_BaseColor = Utils.GetDecimalColor(Color.Yellow); - public Vector3 GFX_Renderable_Point_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - public Vector3 GFX_Renderable_DummyPoly_BaseColor = Utils.GetDecimalColor(Color.Yellow); - public Vector3 GFX_Renderable_DummyPoly_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - public Vector3 GFX_Renderable_BonePoint_BaseColor = Utils.GetDecimalColor(Color.Blue); - public Vector3 GFX_Renderable_BonePoint_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - public Vector3 GFX_Renderable_ModelMarker_Chr_BaseColor = Utils.GetDecimalColor(Color.Firebrick); - public Vector3 GFX_Renderable_ModelMarker_Chr_HighlightColor = Utils.GetDecimalColor(Color.Tomato); - - public Vector3 GFX_Renderable_ModelMarker_Object_BaseColor = Utils.GetDecimalColor(Color.MediumVioletRed); - public Vector3 GFX_Renderable_ModelMarker_Object_HighlightColor = Utils.GetDecimalColor(Color.DeepPink); - - public Vector3 GFX_Renderable_ModelMarker_Player_BaseColor = Utils.GetDecimalColor(Color.DarkOliveGreen); - public Vector3 GFX_Renderable_ModelMarker_Player_HighlightColor = Utils.GetDecimalColor(Color.OliveDrab); - - public Vector3 GFX_Renderable_ModelMarker_Other_BaseColor = Utils.GetDecimalColor(Color.Wheat); - public Vector3 GFX_Renderable_ModelMarker_Other_HighlightColor = Utils.GetDecimalColor(Color.AntiqueWhite); - - public Vector3 GFX_Renderable_PointLight_BaseColor = Utils.GetDecimalColor(Color.YellowGreen); - public Vector3 GFX_Renderable_PointLight_HighlightColor = Utils.GetDecimalColor(Color.Yellow); - - public Vector3 GFX_Renderable_SpotLight_BaseColor = Utils.GetDecimalColor(Color.Goldenrod); - public Vector3 GFX_Renderable_SpotLight_HighlightColor = Utils.GetDecimalColor(Color.Violet); - - public Vector3 GFX_Renderable_DirectionalLight_BaseColor = Utils.GetDecimalColor(Color.Cyan); - public Vector3 GFX_Renderable_DirectionalLight_HighlightColor = Utils.GetDecimalColor(Color.AliceBlue); - - public Vector3 GFX_Gizmo_X_BaseColor = new(0.952f, 0.211f, 0.325f); - public Vector3 GFX_Gizmo_X_HighlightColor = new(1.0f, 0.4f, 0.513f); - - public Vector3 GFX_Gizmo_Y_BaseColor = new(0.525f, 0.784f, 0.082f); - public Vector3 GFX_Gizmo_Y_HighlightColor = new(0.713f, 0.972f, 0.270f); - - public Vector3 GFX_Gizmo_Z_BaseColor = new(0.219f, 0.564f, 0.929f); - public Vector3 GFX_Gizmo_Z_HighlightColor = new(0.407f, 0.690f, 1.0f); - public RenderFilter LastSceneFilter { get; set; } = RenderFilter.All ^ RenderFilter.Light; - - public RenderFilterPreset SceneFilter_Preset_01 { get; set; } = new("Map", - RenderFilter.MapPiece | RenderFilter.Object | RenderFilter.Character | RenderFilter.Region); - - public RenderFilterPreset SceneFilter_Preset_02 { get; set; } = new("Collision", - RenderFilter.Collision | RenderFilter.Object | RenderFilter.Character | RenderFilter.Region); - - public RenderFilterPreset SceneFilter_Preset_03 { get; set; } = new("Collision & Navmesh", - RenderFilter.Collision | RenderFilter.Navmesh | RenderFilter.Object | RenderFilter.Character | - RenderFilter.Region); - - public RenderFilterPreset SceneFilter_Preset_04 { get; set; } = new("Lighting (Map)", - RenderFilter.MapPiece | RenderFilter.Object | RenderFilter.Character | RenderFilter.Light); - - public RenderFilterPreset SceneFilter_Preset_05 { get; set; } = new("Lighting (Collision)", - RenderFilter.Collision | RenderFilter.Object | RenderFilter.Character | RenderFilter.Light); - - public RenderFilterPreset SceneFilter_Preset_06 { get; set; } = new("All", RenderFilter.All); - // Settings: Model Editor - // Settings: Param Editor - public bool Param_AdvancedMassedit = false; - public bool Param_AllowFieldReorder = true; - public bool Param_AlphabeticalParams = true; - public bool Param_DisableLineWrapping = false; - public bool Param_DisableRowGrouping = false; - public bool Param_HideEnums = false; - public bool Param_HideReferenceRows = false; - public bool Param_MakeMetaNamesPrimary = true; - public bool Param_PasteAfterSelection = false; - public bool Param_PasteThenSelect = true; - public bool Param_ShowFieldOffsets = false; - public bool Param_ShowHotkeysInContextMenu = true; - public bool Param_ShowSecondaryNames = true; - public bool Param_ShowVanillaParams = true; - public bool UI_CompactParams = false; - - // Settings: Text Editor - public bool FMG_NoFmgPatching = false; - public bool FMG_NoGroupedFmgEntries = false; - public bool FMG_ShowOriginalNames = false; - // CFG public static CFG Current { get; private set; } public static CFG Default { get; } = new(); @@ -214,26 +79,6 @@ public class CFG public int GFX_Display_X { get; set; } = 0; public int GFX_Display_Y { get; set; } = 23; - public string Param_Export_Delimiter - { - get - { - if (_Param_Export_Delimiter.Length == 0) - { - _Param_Export_Delimiter = Default.Param_Export_Delimiter; - } - else if (_Param_Export_Delimiter == "|") - { - _Param_Export_Delimiter = - Default - .Param_Export_Delimiter; // Temporary measure to prevent conflicts with byte array delimiters. Will be removed later. - } - - return _Param_Export_Delimiter; - } - set => _Param_Export_Delimiter = value; - } - public static string GetConfigFilePath() { return $@"{GetConfigFolderPath()}\{Config_FileName}"; @@ -428,21 +273,4 @@ public bool IsSameProjectLocation(RecentProject otherProject) return false; } } - - public class RenderFilterPreset - { - [JsonConstructor] - public RenderFilterPreset() - { - } - - public RenderFilterPreset(string name, RenderFilter filters) - { - Name = name; - Filters = filters; - } - - public string Name { get; set; } - public RenderFilter Filters { get; set; } - } } diff --git a/src/StudioCore/DataBank.cs b/src/StudioCore/DataBank.cs new file mode 100644 index 000000000..f11d9adeb --- /dev/null +++ b/src/StudioCore/DataBank.cs @@ -0,0 +1,19 @@ +namespace StudioCore; + +/// +/// Class that stores a collection of game data sourced from a single project +/// +public abstract class DataBank : StudioResource +{ + public Project Project; + + public DataBank(Project project, string nameForUI) : base(project.Settings.GameType, nameForUI) + { + Project = project; + } + public override string GetTaskName() + { + return $@"Resource - Loading {nameForUI} ({Project.Settings.ProjectName})"; + } + public abstract void Save(); +} diff --git a/src/StudioCore/Editor/Action.cs b/src/StudioCore/Editor/Action.cs index 466095945..ed5850544 100644 --- a/src/StudioCore/Editor/Action.cs +++ b/src/StudioCore/Editor/Action.cs @@ -231,7 +231,7 @@ public override ActionEvent Execute() TaskManager.Run(new TaskManager.LiveTask("Param - Check Differences", TaskManager.RequeueType.Repeat, true, TaskLogs.LogPriority.Low, - () => ParamBank.RefreshAllParamDiffCaches(false))); + () => ParamDiffBank.RefreshAllParamDiffCaches(false))); return ActionEvent.NoEvent; } @@ -302,7 +302,7 @@ public override ActionEvent Undo() TaskManager.Run(new TaskManager.LiveTask("Param - Check Differences", TaskManager.RequeueType.Repeat, true, TaskLogs.LogPriority.Low, - () => ParamBank.RefreshAllParamDiffCaches(false))); + () => ParamDiffBank.RefreshAllParamDiffCaches(false))); return ActionEvent.NoEvent; } } diff --git a/src/StudioCore/Editor/EditorDecorations.cs b/src/StudioCore/Editor/EditorDecorations.cs index 5c8c06dd1..a751061d4 100644 --- a/src/StudioCore/Editor/EditorDecorations.cs +++ b/src/StudioCore/Editor/EditorDecorations.cs @@ -28,6 +28,23 @@ public static bool HelpIcon(string id, ref string hint, bool canEdit) return UIHints.AddImGuiHintButton(id, ref hint, canEdit, true); //presently a hack, move code here } + // A second one with brackets and hover instead + public static void ShowHelpMarker(string desc) + { + if (CFG.Current.ShowUITooltips) + { + ImGui.TextDisabled("(?)"); + if (ImGui.IsItemHovered(0)) + { + ImGui.BeginTooltip(); + ImGui.PushTextWrapPos(450.0f); + ImGui.TextUnformatted(desc); + ImGui.PopTextWrapPos(); + ImGui.EndTooltip(); + } + ImGui.SameLine(); + } + } public static void ParamRefText(List paramRefs, Param.Row context) { @@ -924,5 +941,136 @@ public static void ImGui_DisplayPropertyInfo(Type propType, string fieldName, st } ImGui.Separator(); + } + public static unsafe void ShowMenuIcon(string iconStr) + { + ImGui.PushStyleVarVec2(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle()->ItemSpacing.Y)); + ImGui.TextUnformatted(iconStr); + ImGui.PopStyleVar(1); + ImGui.SameLine(); + } + + public static unsafe void ShowActiveStatus(bool isActive) + { + if (isActive) + { + ImGui.SameLine(); + ImGui.PushStyleVarVec2(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle()->ItemSpacing.Y)); + ImGui.TextUnformatted($"{ForkAwesome.CheckSquare}"); + ImGui.PopStyleVar(1); + } + else + { + ImGui.SameLine(); + ImGui.PushStyleVarVec2(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle()->ItemSpacing.Y)); + ImGui.TextUnformatted($"{ForkAwesome.Square}"); + ImGui.PopStyleVar(1); + } + } + + public static void ShowHelpButton(string title, string desc, string id) + { + if (ImGui.Button($"{title}")) + ImGui.OpenPopup($"##{id}HelpPopup"); + + if (ImGui.BeginPopup($"##{id}HelpPopup")) + { + ImGui.Text($"{desc}"); + ImGui.EndPopup(); + } + } + + public static void ShowButtonTooltip(string desc) + { + if (CFG.Current.ShowUITooltips) + { + if (ImGui.IsItemHovered(0)) + { + ImGui.BeginTooltip(); + ImGui.PushTextWrapPos(450.0f); + ImGui.TextUnformatted(desc); + ImGui.PopTextWrapPos(); + ImGui.EndTooltip(); + } + } + } + + public static void ShowHoverTooltip(string desc) + { + if (CFG.Current.ShowUITooltips) + { + if (ImGui.IsItemHovered(0)) + { + ImGui.BeginTooltip(); + ImGui.PushTextWrapPos(450.0f); + ImGui.TextUnformatted(desc); + ImGui.PopTextWrapPos(); + ImGui.EndTooltip(); + } + } } + + public static void ShowWideHoverTooltip(string desc) + { + if (CFG.Current.ShowUITooltips) + { + if (ImGui.IsItemHovered(0)) + { + ImGui.BeginTooltip(); + ImGui.PushTextWrapPos(800.0f); + ImGui.TextUnformatted(desc); + ImGui.PopTextWrapPos(); + ImGui.EndTooltip(); + } + } + } + + public static string GetKeybindHint(string hint) + { + if (hint == "") + return "None"; + else + return hint; + } + + public static void WrappedText(string text) + { + var size = ImGui.GetWindowSize(); + + ImGui.PushTextWrapPos(size.X); + ImGui.TextUnformatted(text); + ImGui.PopTextWrapPos(); + } + + public static void WrappedTextColored(Vector4 color, string text) + { + var size = ImGui.GetWindowSize(); + + ImGui.PushTextWrapPos(size.X); + ImGui.PushStyleColorVec4(ImGuiCol.Text, color); + ImGui.TextUnformatted(text); + ImGui.PopStyleColor(1); + ImGui.PopTextWrapPos(); + } + public static void DisplayAlias(string aliasName) + { + if (aliasName != "") + { + ImGui.SameLine(); + ImGui.PushTextWrapPos(0); + ImGui.TextColored(new Vector4(1.0f, 1.0f, 0.0f, 1.0f), @$"{aliasName}"); + ImGui.PopTextWrapPos(); + } + } + public static void DisplayTagAlias(string aliasName) + { + if (aliasName != "") + { + ImGui.SameLine(); + ImGui.PushTextWrapPos(0); + ImGui.TextColored(new Vector4(0.0f, 1.0f, 0.0f, 1.0f), @$"[{aliasName}]"); + ImGui.PopTextWrapPos(); + } + } + } diff --git a/src/StudioCore/Editor/EditorScreen.cs b/src/StudioCore/Editor/EditorScreen.cs index 8c1d277e0..96014d534 100644 --- a/src/StudioCore/Editor/EditorScreen.cs +++ b/src/StudioCore/Editor/EditorScreen.cs @@ -1,4 +1,7 @@ -using Veldrid; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Veldrid; using Veldrid.Sdl2; namespace StudioCore.Editor; @@ -81,4 +84,17 @@ public void EditorResized(Sdl2Window window, GraphicsDevice device) public void Draw(GraphicsDevice device, CommandList cl) { } + + public bool IsEnabled(Project project) + { + return StudioResource.AreResourcesLoaded(GetDependencies(project)); + } + public void Load(Project project) + { + StudioResource.Load(project, GetDependencies(project)); + } + + protected abstract IEnumerable GetDependencies(Project project); + + public abstract void SettingsMenu(); } diff --git a/src/StudioCore/Interface/HelpWindow.cs b/src/StudioCore/Editor/HelpWindow.cs similarity index 95% rename from src/StudioCore/Interface/HelpWindow.cs rename to src/StudioCore/Editor/HelpWindow.cs index 0f2e01103..e28f346d3 100644 --- a/src/StudioCore/Interface/HelpWindow.cs +++ b/src/StudioCore/Editor/HelpWindow.cs @@ -8,7 +8,7 @@ using System.Text.RegularExpressions; using static Andre.Native.ImGuiBindings; -namespace StudioCore.Interface; +namespace StudioCore.Editor; public class HelpWindow { @@ -300,9 +300,9 @@ private void DisplayHelpSection(List entries, string name, string tab // No selection if (entry == null) { - ImguiUtils.WrappedText($"No {descName} selected"); + EditorDecorations.WrappedText($"No {descName} selected"); ImGui.Separator(); - ImguiUtils.WrappedText(""); + EditorDecorations.WrappedText(""); } // Selection else @@ -341,11 +341,11 @@ private void ProcessText(HelpEntry entry, string textLine) if (entry.HeaderColor != null) { Vector4 color = new Vector4(entry.HeaderColor[0], entry.HeaderColor[1], entry.HeaderColor[2], entry.HeaderColor[3]); - ImguiUtils.WrappedTextColored(color, outputLine); + EditorDecorations.WrappedTextColored(color, outputLine); } else { - ImguiUtils.WrappedText(outputLine); + EditorDecorations.WrappedText(outputLine); } ImGui.Separator(); @@ -364,17 +364,17 @@ private void ProcessText(HelpEntry entry, string textLine) if (entry.HighlightColor != null) { Vector4 color = new Vector4(entry.HighlightColor[0], entry.HighlightColor[1], entry.HighlightColor[2], entry.HighlightColor[3]); - ImguiUtils.WrappedTextColored(color, highlightText); + EditorDecorations.WrappedTextColored(color, highlightText); var offset = highlightText.Length * 8.0f; ImGui.SameLine(offset, 0); - ImguiUtils.WrappedText(otherText); + EditorDecorations.WrappedText(otherText); } else { - ImguiUtils.WrappedText(highlightText); + EditorDecorations.WrappedText(highlightText); ImGui.SameLine(); - ImguiUtils.WrappedText(otherText); + EditorDecorations.WrappedText(otherText); } } } @@ -399,7 +399,7 @@ private void ProcessText(HelpEntry entry, string textLine) // Default else { - ImguiUtils.WrappedText(textLine); + EditorDecorations.WrappedText(textLine); } } } diff --git a/src/StudioCore/Editor/ProjectSettings.cs b/src/StudioCore/Editor/ProjectSettings.cs index 495fe6974..f88adb78c 100644 --- a/src/StudioCore/Editor/ProjectSettings.cs +++ b/src/StudioCore/Editor/ProjectSettings.cs @@ -79,6 +79,15 @@ public static ProjectSettings Deserialize(string path) } } + internal ProjectSettings CopyForGameDir() + { + ProjectSettings newProj = new(); + newProj.ProjectName = "Vanilla"; + newProj.GameRoot = GameRoot; + newProj.GameType = GameType; + newProj.UseLooseParams = false; + return newProj; + } internal ProjectSettings CopyAndAssumeFromModDir(string moddir) { ProjectSettings newProj = new(); diff --git a/src/StudioCore/Editor/TaskManager.cs b/src/StudioCore/Editor/TaskManager.cs index f75663504..718377f84 100644 --- a/src/StudioCore/Editor/TaskManager.cs +++ b/src/StudioCore/Editor/TaskManager.cs @@ -31,9 +31,9 @@ public enum RequeueType /// public static int ActiveTaskNum { get; private set; } - public static void Run(LiveTask liveTask) + public static LiveTask Run(LiveTask liveTask) { - liveTask.Run(); + return liveTask.Run(); } public static void RunPassiveTask(LiveTask liveTask) @@ -138,7 +138,7 @@ public LiveTask(string taskId, RequeueType requeueType, bool silentFail, TaskLog public Task Task { get; private set; } - public void Run() + public LiveTask Run() { if (_liveTasks.TryGetValue(TaskId, out LiveTask oldLiveTask)) { @@ -149,11 +149,11 @@ public void Run() else if (oldLiveTask.RequeueBehavior == RequeueType.Repeat) { oldLiveTask.HasScheduledRequeue = true; - return; + return oldLiveTask; } else { - return; + return oldLiveTask; } } @@ -166,6 +166,7 @@ public void Run() CreateTask(); Task.Start(); + return this; } private void CreateTask() diff --git a/src/StudioCore/Editor/CacheBank.cs b/src/StudioCore/Editor/UICache.cs similarity index 100% rename from src/StudioCore/Editor/CacheBank.cs rename to src/StudioCore/Editor/UICache.cs diff --git a/src/StudioCore/Interface/ImguiUtils.cs b/src/StudioCore/Interface/ImguiUtils.cs deleted file mode 100644 index bf2265992..000000000 --- a/src/StudioCore/Interface/ImguiUtils.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; -using static Andre.Native.ImGuiBindings; - -namespace StudioCore.Interface; -public static class ImguiUtils -{ - public static unsafe void ShowMenuIcon(string iconStr) - { - ImGui.PushStyleVarVec2(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle()->ItemSpacing.Y)); - ImGui.TextUnformatted(iconStr); - ImGui.PopStyleVar(1); - ImGui.SameLine(); - } - - public static unsafe void ShowActiveStatus(bool isActive) - { - if (isActive) - { - ImGui.SameLine(); - ImGui.PushStyleVarVec2(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle()->ItemSpacing.Y)); - ImGui.TextUnformatted($"{ForkAwesome.CheckSquare}"); - ImGui.PopStyleVar(1); - } - else - { - ImGui.SameLine(); - ImGui.PushStyleVarVec2(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle()->ItemSpacing.Y)); - ImGui.TextUnformatted($"{ForkAwesome.Square}"); - ImGui.PopStyleVar(1); - } - } - - public static void ShowHelpButton(string title, string desc, string id) - { - if (ImGui.Button($"{title}")) - ImGui.OpenPopup($"##{id}HelpPopup"); - - if (ImGui.BeginPopup($"##{id}HelpPopup")) - { - ImGui.Text($"{desc}"); - ImGui.EndPopup(); - } - } - - public static void ShowHelpMarker(string desc) - { - if (CFG.Current.ShowUITooltips) - { - ImGui.SameLine(); - ImGui.TextDisabled("(?)"); - if (ImGui.IsItemHovered(0)) - { - ImGui.BeginTooltip(); - ImGui.PushTextWrapPos(450.0f); - ImGui.TextUnformatted(desc); - ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); - } - } - } - - public static void ShowButtonTooltip(string desc) - { - if (CFG.Current.ShowUITooltips) - { - if (ImGui.IsItemHovered(0)) - { - ImGui.BeginTooltip(); - ImGui.PushTextWrapPos(450.0f); - ImGui.TextUnformatted(desc); - ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); - } - } - } - - public static void ShowHoverTooltip(string desc) - { - if (CFG.Current.ShowUITooltips) - { - if (ImGui.IsItemHovered(0)) - { - ImGui.BeginTooltip(); - ImGui.PushTextWrapPos(450.0f); - ImGui.TextUnformatted(desc); - ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); - } - } - } - - public static void ShowWideHoverTooltip(string desc) - { - if (CFG.Current.ShowUITooltips) - { - if (ImGui.IsItemHovered(0)) - { - ImGui.BeginTooltip(); - ImGui.PushTextWrapPos(800.0f); - ImGui.TextUnformatted(desc); - ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); - } - } - } - - public static string GetKeybindHint(string hint) - { - if (hint == "") - return "None"; - else - return hint; - } - - public static void WrappedText(string text) - { - var size = ImGui.GetWindowSize(); - - ImGui.PushTextWrapPos(size.X); - ImGui.TextUnformatted(text); - ImGui.PopTextWrapPos(); - } - - public static void WrappedTextColored(Vector4 color, string text) - { - var size = ImGui.GetWindowSize(); - - ImGui.PushTextWrapPos(size.X); - ImGui.PushStyleColorVec4(ImGuiCol.Text, color); - ImGui.TextUnformatted(text); - ImGui.PopStyleColor(1); - ImGui.PopTextWrapPos(); - } -} diff --git a/src/StudioCore/MapStudioNew.cs b/src/StudioCore/MapStudioNew.cs index b3c86e976..d63b71e9e 100644 --- a/src/StudioCore/MapStudioNew.cs +++ b/src/StudioCore/MapStudioNew.cs @@ -25,7 +25,7 @@ using System.Runtime.InteropServices; using Veldrid; using Veldrid.Sdl2; -using StudioCore.Interface; +using StudioCore.Editor; using StudioCore.Utilities; using Renderer = StudioCore.Scene.Renderer; using Thread = System.Threading.Thread; @@ -413,13 +413,12 @@ private void ChangeProjectSettings(ProjectSettings newsettings, string moddir, N ModelAliasBank.Bank.ReloadAliasBank(); MapAliasBank.Bank.ReloadAliasBank(); - ParamBank.ReloadParams(newsettings, options); MtdBank.ReloadMtds(); - FMGBank.ReloadFMGs(); foreach (EditorScreen editor in _editors) { editor.OnProjectChanged(_projectSettings); + editor.Load(Locator.ActiveProject); } @@ -1033,7 +1032,7 @@ private unsafe void Update(float deltaseconds) ImGui.EndMainMenuBar(); } - _settingsMenu.Display(); + _settingsMenu.Display(_editors); HelpWindow.Display(); ImGui.PopStyleVar(1); @@ -1339,7 +1338,10 @@ private unsafe void Update(float deltaseconds) { ImGui.PopStyleColor(1); ImGui.PopStyleVar(1); - editor.OnGUI(commands); + if (editor.IsEnabled(Locator.ActiveProject)) + editor.OnGUI(commands); + else + ImGui.Text("Resources required for editor not loaded."); ImGui.End(); _focusedEditor = editor; editor.Update(deltaseconds); diff --git a/src/StudioCore/MsbEditor/MapCFG.cs b/src/StudioCore/MsbEditor/MapCFG.cs new file mode 100644 index 000000000..b65952a48 --- /dev/null +++ b/src/StudioCore/MsbEditor/MapCFG.cs @@ -0,0 +1,140 @@ +using StudioCore.Platform; +using StudioCore.Scene; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Numerics; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace StudioCore; + +public partial class CFG +{ + public bool Viewport_Enable_Selection_Outline = false; + + public bool MapEditor_MapObjectList_ShowMapNames = true; + public bool MapEditor_MapObjectList_ShowCharacterNames = true; + public bool MapEditor_MapObjectList_ShowAssetNames = true; + public bool MapEditor_MapObjectList_ShowMapPieceNames = true; + public bool MapEditor_MapObjectList_ShowPlayerCharacterNames = true; + public bool MapEditor_MapObjectList_ShowSystemCharacterNames = true; + public bool MapEditor_MapObjectList_ShowTreasureNames = true; + + public bool EnableFrustrumCulling = false; + public bool Map_AlwaysListLoadedMaps = true; + public bool EnableEldenRingAutoMapOffset = true; + + public bool Map_EnableViewportGrid = false; + public int Map_ViewportGridType = 0; + public Vector3 GFX_Viewport_Grid_Color = Utils.GetDecimalColor(Color.Red); + public int Map_ViewportGrid_TotalSize = 1000; + public int Map_ViewportGrid_IncrementSize = 10; + + public float Map_ViewportGrid_Offset = 0; + + public float Map_ViewportGrid_ShortcutIncrement = 1; + + public float Map_MoveSelectionToCamera_Radius = 3.0f; + public float GFX_Camera_FOV { get; set; } = 60.0f; + public float GFX_Camera_MoveSpeed_Slow { get; set; } = 1.0f; + public float GFX_Camera_MoveSpeed_Normal { get; set; } = 20.0f; + public float GFX_Camera_MoveSpeed_Fast { get; set; } = 200.0f; + public float GFX_Camera_Sensitivity { get; set; } = 0.0160f; + public float GFX_RenderDistance_Max { get; set; } = 50000.0f; + public float Map_ArbitraryRotation_X_Shift { get; set; } = 90.0f; + public float Map_ArbitraryRotation_Y_Shift { get; set; } = 90.0f; + + public float GFX_Framerate_Limit_Unfocused = 20.0f; + public float GFX_Framerate_Limit = 60.0f; + public uint GFX_Limit_Buffer_Flver_Bone = 65536; + public uint GFX_Limit_Buffer_Indirect_Draw = 50000; + public int GFX_Limit_Renderables = 50000; + + public float GFX_Wireframe_Color_Variance = 0.11f; + + public Vector3 GFX_Renderable_Box_BaseColor = Utils.GetDecimalColor(Color.Blue); + public Vector3 GFX_Renderable_Box_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); + + public Vector3 GFX_Renderable_Cylinder_BaseColor = Utils.GetDecimalColor(Color.Blue); + public Vector3 GFX_Renderable_Cylinder_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); + + public Vector3 GFX_Renderable_Sphere_BaseColor = Utils.GetDecimalColor(Color.Blue); + public Vector3 GFX_Renderable_Sphere_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); + + public Vector3 GFX_Renderable_Point_BaseColor = Utils.GetDecimalColor(Color.Yellow); + public Vector3 GFX_Renderable_Point_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); + + public Vector3 GFX_Renderable_DummyPoly_BaseColor = Utils.GetDecimalColor(Color.Yellow); + public Vector3 GFX_Renderable_DummyPoly_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); + + public Vector3 GFX_Renderable_BonePoint_BaseColor = Utils.GetDecimalColor(Color.Blue); + public Vector3 GFX_Renderable_BonePoint_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); + + public Vector3 GFX_Renderable_ModelMarker_Chr_BaseColor = Utils.GetDecimalColor(Color.Firebrick); + public Vector3 GFX_Renderable_ModelMarker_Chr_HighlightColor = Utils.GetDecimalColor(Color.Tomato); + + public Vector3 GFX_Renderable_ModelMarker_Object_BaseColor = Utils.GetDecimalColor(Color.MediumVioletRed); + public Vector3 GFX_Renderable_ModelMarker_Object_HighlightColor = Utils.GetDecimalColor(Color.DeepPink); + + public Vector3 GFX_Renderable_ModelMarker_Player_BaseColor = Utils.GetDecimalColor(Color.DarkOliveGreen); + public Vector3 GFX_Renderable_ModelMarker_Player_HighlightColor = Utils.GetDecimalColor(Color.OliveDrab); + + public Vector3 GFX_Renderable_ModelMarker_Other_BaseColor = Utils.GetDecimalColor(Color.Wheat); + public Vector3 GFX_Renderable_ModelMarker_Other_HighlightColor = Utils.GetDecimalColor(Color.AntiqueWhite); + + public Vector3 GFX_Renderable_PointLight_BaseColor = Utils.GetDecimalColor(Color.YellowGreen); + public Vector3 GFX_Renderable_PointLight_HighlightColor = Utils.GetDecimalColor(Color.Yellow); + + public Vector3 GFX_Renderable_SpotLight_BaseColor = Utils.GetDecimalColor(Color.Goldenrod); + public Vector3 GFX_Renderable_SpotLight_HighlightColor = Utils.GetDecimalColor(Color.Violet); + + public Vector3 GFX_Renderable_DirectionalLight_BaseColor = Utils.GetDecimalColor(Color.Cyan); + public Vector3 GFX_Renderable_DirectionalLight_HighlightColor = Utils.GetDecimalColor(Color.AliceBlue); + + public Vector3 GFX_Gizmo_X_BaseColor = new(0.952f, 0.211f, 0.325f); + public Vector3 GFX_Gizmo_X_HighlightColor = new(1.0f, 0.4f, 0.513f); + + public Vector3 GFX_Gizmo_Y_BaseColor = new(0.525f, 0.784f, 0.082f); + public Vector3 GFX_Gizmo_Y_HighlightColor = new(0.713f, 0.972f, 0.270f); + + public Vector3 GFX_Gizmo_Z_BaseColor = new(0.219f, 0.564f, 0.929f); + public Vector3 GFX_Gizmo_Z_HighlightColor = new(0.407f, 0.690f, 1.0f); + public RenderFilter LastSceneFilter { get; set; } = RenderFilter.All ^ RenderFilter.Light; + + public RenderFilterPreset SceneFilter_Preset_01 { get; set; } = new("Map", + RenderFilter.MapPiece | RenderFilter.Object | RenderFilter.Character | RenderFilter.Region); + + public RenderFilterPreset SceneFilter_Preset_02 { get; set; } = new("Collision", + RenderFilter.Collision | RenderFilter.Object | RenderFilter.Character | RenderFilter.Region); + + public RenderFilterPreset SceneFilter_Preset_03 { get; set; } = new("Collision & Navmesh", + RenderFilter.Collision | RenderFilter.Navmesh | RenderFilter.Object | RenderFilter.Character | + RenderFilter.Region); + + public RenderFilterPreset SceneFilter_Preset_04 { get; set; } = new("Lighting (Map)", + RenderFilter.MapPiece | RenderFilter.Object | RenderFilter.Character | RenderFilter.Light); + + public RenderFilterPreset SceneFilter_Preset_05 { get; set; } = new("Lighting (Collision)", + RenderFilter.Collision | RenderFilter.Object | RenderFilter.Character | RenderFilter.Light); + + public RenderFilterPreset SceneFilter_Preset_06 { get; set; } = new("All", RenderFilter.All); + + public class RenderFilterPreset + { + [JsonConstructor] + public RenderFilterPreset() + { + } + + public RenderFilterPreset(string name, RenderFilter filters) + { + Name = name; + Filters = filters; + } + + public string Name { get; set; } + public RenderFilter Filters { get; set; } + } +} diff --git a/src/StudioCore/MsbEditor/ModelEditorScreen.cs b/src/StudioCore/MsbEditor/ModelEditorScreen.cs index 0b0ee6cdc..7d8f8d82f 100644 --- a/src/StudioCore/MsbEditor/ModelEditorScreen.cs +++ b/src/StudioCore/MsbEditor/ModelEditorScreen.cs @@ -361,4 +361,17 @@ public void LoadModel(string modelid, ModelEditorModelType modelType, string map ResourceManager.AddResourceListener(asset.AssetVirtualPath, this, AccessLevel.AccessFull); } + + IEnumerable EditorScreen.GetDependencies(Project project) + { + return []; + } + + public void SettingsMenu() + { + if (ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) + { + //Nothing here + } + } } diff --git a/src/StudioCore/MsbEditor/MsbEditorScreen.cs b/src/StudioCore/MsbEditor/MsbEditorScreen.cs index d9d6f6150..6fd6a7317 100644 --- a/src/StudioCore/MsbEditor/MsbEditorScreen.cs +++ b/src/StudioCore/MsbEditor/MsbEditorScreen.cs @@ -2109,4 +2109,398 @@ private void GenerateMCGMCP(Dictionary orderedMaps) ImGui.EndCombo(); } } + + IEnumerable EditorScreen.GetDependencies(Project project) + { + return []; + } + + public void SettingsMenu() + { + if (ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) + { + EditorDecorations.ShowHelpMarker("Viewport FPS when window is focused."); + ImGui.DragFloat("Frame Limit", ref CFG.Current.GFX_Framerate_Limit, 1.0f, 5.0f, 300.0f); + + EditorDecorations.ShowHelpMarker("Viewport FPS when window is not focused."); + ImGui.DragFloat("Frame Limit (Unfocused)", ref CFG.Current.GFX_Framerate_Limit_Unfocused, 1.0f, 1.0f, 60.0f); + + EditorDecorations.ShowHelpMarker("Enabling this option will allow DSMS to render the textures of models within the viewport.\n\nNote, this feature is in an alpha state."); + ImGui.Checkbox("Enable texturing", ref CFG.Current.EnableTexturing); + + EditorDecorations.ShowHelpMarker("This option will cause loaded maps to always be visible within the map list, ignoring the search filter."); + ImGui.Checkbox("Exclude loaded maps from search filter", ref CFG.Current.Map_AlwaysListLoadedMaps); + + if (Locator.ActiveProject.Type is GameType.EldenRing) + { + if (CFG.Current.ShowUITooltips) + { + EditorDecorations.ShowHelpMarker(""); + ImGui.SameLine(); + } + ImGui.Checkbox("Enable Elden Ring auto map offset", ref CFG.Current.EnableEldenRingAutoMapOffset); + } + } + + // Scene View + // Scene View + if (ImGui.CollapsingHeader("Map Object List")) + { + ImGui.Checkbox("Display map names", ref CFG.Current.MapEditor_MapObjectList_ShowMapNames); + Editor.EditorDecorations.ShowHoverTooltip("Map names will be displayed within the scene view list."); + + ImGui.Checkbox("Display character names", ref CFG.Current.MapEditor_MapObjectList_ShowCharacterNames); + Editor.EditorDecorations.ShowHoverTooltip("Characters names will be displayed within the scene view list."); + + ImGui.Checkbox("Display asset names", ref CFG.Current.MapEditor_MapObjectList_ShowAssetNames); + Editor.EditorDecorations.ShowHoverTooltip("Asset/object names will be displayed within the scene view list."); + + ImGui.Checkbox("Display map piece names", ref CFG.Current.MapEditor_MapObjectList_ShowMapPieceNames); + Editor.EditorDecorations.ShowHoverTooltip("Map piece names will be displayed within the scene view list."); + + ImGui.Checkbox("Display treasure names", ref CFG.Current.MapEditor_MapObjectList_ShowTreasureNames); + Editor.EditorDecorations.ShowHoverTooltip("Treasure itemlot names will be displayed within the scene view list."); + } + + if (ImGui.CollapsingHeader("Selection")) + { + var arbitrary_rotation_x = CFG.Current.Map_ArbitraryRotation_X_Shift; + var arbitrary_rotation_y = CFG.Current.Map_ArbitraryRotation_Y_Shift; + var camera_radius_offset = CFG.Current.Map_MoveSelectionToCamera_Radius; + + ImGui.Checkbox("Enable selection outline", ref CFG.Current.Viewport_Enable_Selection_Outline); + Editor.EditorDecorations.ShowHoverTooltip("Enable the selection outline around map entities."); + + EditorDecorations.ShowHelpMarker("Set the angle increment amount used by Arbitary Rotation in the X-axis."); + if (ImGui.InputFloat("Rotation increment degrees: Roll", ref arbitrary_rotation_x)) + { + CFG.Current.Map_ArbitraryRotation_X_Shift = Math.Clamp(arbitrary_rotation_x, -180.0f, 180.0f); + } + + EditorDecorations.ShowHelpMarker("Set the angle increment amount used by Arbitary Rotation in the Y-axis."); + if (ImGui.InputFloat("Rotation increment degrees: Yaw", ref arbitrary_rotation_y)) + { + CFG.Current.Map_ArbitraryRotation_Y_Shift = Math.Clamp(arbitrary_rotation_y, -180.0f, 180.0f); + ; + } + + EditorDecorations.ShowHelpMarker("Set the distance at which the current select is offset from the camera when using the Move Selection to Camera action."); + if (ImGui.DragFloat("Move selection to camera (offset distance)", ref camera_radius_offset)) + { + CFG.Current.Map_MoveSelectionToCamera_Radius = camera_radius_offset; + } + } + + if (ImGui.CollapsingHeader("Camera")) + { + EditorDecorations.ShowHelpMarker("Resets all of the values within this section to their default values."); + if (ImGui.Button("Reset##ViewportCamera")) + { + CFG.Current.GFX_Camera_Sensitivity = CFG.Default.GFX_Camera_Sensitivity; + + CFG.Current.GFX_Camera_FOV = CFG.Default.GFX_Camera_FOV; + + CFG.Current.GFX_RenderDistance_Max = CFG.Default.GFX_RenderDistance_Max; + + Viewport.WorldView.CameraMoveSpeed_Slow = CFG.Default.GFX_Camera_MoveSpeed_Slow; + CFG.Current.GFX_Camera_MoveSpeed_Slow = Viewport.WorldView.CameraMoveSpeed_Slow; + + Viewport.WorldView.CameraMoveSpeed_Normal = CFG.Default.GFX_Camera_MoveSpeed_Normal; + CFG.Current.GFX_Camera_MoveSpeed_Normal = Viewport.WorldView.CameraMoveSpeed_Normal; + + Viewport.WorldView.CameraMoveSpeed_Fast = CFG.Default.GFX_Camera_MoveSpeed_Fast; + CFG.Current.GFX_Camera_MoveSpeed_Fast = Viewport.WorldView.CameraMoveSpeed_Fast; + } + + var cam_sensitivity = CFG.Current.GFX_Camera_Sensitivity; + + EditorDecorations.ShowHelpMarker("Mouse sensitivty for turning the camera."); + if (ImGui.SliderFloat("Camera sensitivity", ref cam_sensitivity, 0.0f, 0.1f)) + { + CFG.Current.GFX_Camera_Sensitivity = cam_sensitivity; + } + + var cam_fov = CFG.Current.GFX_Camera_FOV; + + EditorDecorations.ShowHelpMarker("Set the field of view used by the camera within DSMS."); + if (ImGui.SliderFloat("Camera FOV", ref cam_fov, 40.0f, 140.0f)) + { + CFG.Current.GFX_Camera_FOV = cam_fov; + } + + var farClip = CFG.Current.GFX_RenderDistance_Max; + EditorDecorations.ShowHelpMarker("Set the maximum distance at which entities will be rendered within the DSMS viewport."); + if (ImGui.SliderFloat("Map max render distance", ref farClip, 10.0f, 500000.0f)) + { + CFG.Current.GFX_RenderDistance_Max = farClip; + } + + EditorDecorations.ShowHelpMarker("Set the speed at which the camera will move when the Left or Right Shift key is pressed whilst moving."); + if (ImGui.SliderFloat("Map camera speed (slow)", + ref Viewport.WorldView.CameraMoveSpeed_Slow, 0.1f, 999.0f)) + { + CFG.Current.GFX_Camera_MoveSpeed_Slow = Viewport.WorldView.CameraMoveSpeed_Slow; + } + + EditorDecorations.ShowHelpMarker("Set the speed at which the camera will move whilst moving normally."); + if (ImGui.SliderFloat("Map camera speed (normal)", + ref Viewport.WorldView.CameraMoveSpeed_Normal, 0.1f, 999.0f)) + { + CFG.Current.GFX_Camera_MoveSpeed_Normal = Viewport.WorldView.CameraMoveSpeed_Normal; + } + + EditorDecorations.ShowHelpMarker("Set the speed at which the camera will move when the Left or Right Control key is pressed whilst moving."); + if (ImGui.SliderFloat("Map camera speed (fast)", + ref Viewport.WorldView.CameraMoveSpeed_Fast, 0.1f, 999.0f)) + { + CFG.Current.GFX_Camera_MoveSpeed_Fast = Viewport.WorldView.CameraMoveSpeed_Fast; + } + } + + if (ImGui.CollapsingHeader("Limits")) + { + EditorDecorations.ShowHelpMarker("Reset the values within this section to their default values."); + if (ImGui.Button("Reset##MapLimits")) + { + CFG.Current.GFX_Limit_Renderables = CFG.Default.GFX_Limit_Renderables; + CFG.Current.GFX_Limit_Buffer_Indirect_Draw = CFG.Default.GFX_Limit_Buffer_Indirect_Draw; + CFG.Current.GFX_Limit_Buffer_Flver_Bone = CFG.Default.GFX_Limit_Buffer_Flver_Bone; + } + + ImGui.Text("Please restart the program for changes to take effect."); + + ImGui.TextColored(new Vector4(1.0f, 0.0f, 0.0f, 1.0f), + @"Try smaller increments (+25%%) at first, as high values will cause issues."); + + EditorDecorations.ShowHelpMarker("This value constrains the number of renderable entities that are allowed. Exceeding this value will throw an exception."); + if (ImGui.InputInt("Renderables", ref CFG.Current.GFX_Limit_Renderables, 0, 0)) + { + if (CFG.Current.GFX_Limit_Renderables < CFG.Default.GFX_Limit_Renderables) + { + CFG.Current.GFX_Limit_Renderables = CFG.Default.GFX_Limit_Renderables; + } + } + + EditorDecorations.ShowHelpMarker("This value constrains the size of the indirect draw buffer. Exceeding this value will throw an exception."); + Utils.ImGui_InputUint("Indirect Draw buffer", ref CFG.Current.GFX_Limit_Buffer_Indirect_Draw); + + EditorDecorations.ShowHelpMarker("This value constrains the size of the FLVER bone buffer. Exceeding this value will throw an exception."); + Utils.ImGui_InputUint("FLVER Bone buffer", ref CFG.Current.GFX_Limit_Buffer_Flver_Bone); + } + + if (FeatureFlags.ViewportGrid) + { + if (ImGui.CollapsingHeader("Grid")) + { + EditorDecorations.ShowHelpMarker("Enable the viewport grid when in the Map Editor."); + ImGui.Checkbox("Enable viewport grid", ref CFG.Current.Map_EnableViewportGrid); + + EditorDecorations.ShowHelpMarker("The overall maximum size of the grid.\nThe grid will only update upon restarting DSMS after changing this value."); + ImGui.SliderInt("Grid size", ref CFG.Current.Map_ViewportGrid_TotalSize, 100, 1000); + + EditorDecorations.ShowHelpMarker("The increment size of the grid."); + ImGui.SliderInt("Grid increment", ref CFG.Current.Map_ViewportGrid_IncrementSize, 1, 100); + + EditorDecorations.ShowHelpMarker("The height at which the horizontal grid sits."); + ImGui.SliderFloat("Grid height", ref CFG.Current.Map_ViewportGrid_Offset, -1000, 1000); + + EditorDecorations.ShowHelpMarker("The amount to lower or raise the viewport grid height via the shortcuts."); + ImGui.SliderFloat("Grid height increment", ref CFG.Current.Map_ViewportGrid_ShortcutIncrement, 0.1f, 100); + + ImGui.ColorEdit3("Grid color", ref CFG.Current.GFX_Viewport_Grid_Color); + + EditorDecorations.ShowHelpMarker("Resets all of the values within this section to their default values."); + if (ImGui.Button("Reset")) + { + CFG.Current.GFX_Viewport_Grid_Color = Utils.GetDecimalColor(System.Drawing.Color.Red); + CFG.Current.Map_ViewportGrid_TotalSize = 1000; + CFG.Current.Map_ViewportGrid_IncrementSize = 10; + CFG.Current.Map_ViewportGrid_Offset = 0; + } + } + } + + if (ImGui.CollapsingHeader("Wireframes")) + { + EditorDecorations.ShowHelpMarker("Resets all of the values within this section to their default values."); + if (ImGui.Button("Reset")) + { + // Proxies + CFG.Current.GFX_Renderable_Box_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.Blue); + CFG.Current.GFX_Renderable_Box_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.DarkViolet); + + CFG.Current.GFX_Renderable_Cylinder_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.Blue); + CFG.Current.GFX_Renderable_Cylinder_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.DarkViolet); + + CFG.Current.GFX_Renderable_Sphere_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.Blue); + CFG.Current.GFX_Renderable_Sphere_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.DarkViolet); + + CFG.Current.GFX_Renderable_Point_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.Yellow); + CFG.Current.GFX_Renderable_Point_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.DarkViolet); + + CFG.Current.GFX_Renderable_DummyPoly_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.Yellow); + CFG.Current.GFX_Renderable_DummyPoly_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.DarkViolet); + + CFG.Current.GFX_Renderable_BonePoint_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.Blue); + CFG.Current.GFX_Renderable_BonePoint_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.DarkViolet); + + CFG.Current.GFX_Renderable_ModelMarker_Chr_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.Firebrick); + CFG.Current.GFX_Renderable_ModelMarker_Chr_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.Tomato); + + CFG.Current.GFX_Renderable_ModelMarker_Object_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.MediumVioletRed); + CFG.Current.GFX_Renderable_ModelMarker_Object_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.DeepPink); + + CFG.Current.GFX_Renderable_ModelMarker_Player_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.DarkOliveGreen); + CFG.Current.GFX_Renderable_ModelMarker_Player_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.OliveDrab); + + CFG.Current.GFX_Renderable_ModelMarker_Other_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.Wheat); + CFG.Current.GFX_Renderable_ModelMarker_Other_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.AntiqueWhite); + + CFG.Current.GFX_Renderable_PointLight_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.YellowGreen); + CFG.Current.GFX_Renderable_PointLight_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.Yellow); + + CFG.Current.GFX_Renderable_SpotLight_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.Goldenrod); + CFG.Current.GFX_Renderable_SpotLight_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.Violet); + + CFG.Current.GFX_Renderable_DirectionalLight_BaseColor = Utils.GetDecimalColor(System.Drawing.Color.Cyan); + CFG.Current.GFX_Renderable_DirectionalLight_HighlightColor = Utils.GetDecimalColor(System.Drawing.Color.AliceBlue); + + // Gizmos + CFG.Current.GFX_Gizmo_X_BaseColor = new Vector3(0.952f, 0.211f, 0.325f); + CFG.Current.GFX_Gizmo_X_HighlightColor = new Vector3(1.0f, 0.4f, 0.513f); + + CFG.Current.GFX_Gizmo_Y_BaseColor = new Vector3(0.525f, 0.784f, 0.082f); + CFG.Current.GFX_Gizmo_Y_HighlightColor = new Vector3(0.713f, 0.972f, 0.270f); + + CFG.Current.GFX_Gizmo_Z_BaseColor = new Vector3(0.219f, 0.564f, 0.929f); + CFG.Current.GFX_Gizmo_Z_HighlightColor = new Vector3(0.407f, 0.690f, 1.0f); + + // Color Variance + CFG.Current.GFX_Wireframe_Color_Variance = CFG.Default.GFX_Wireframe_Color_Variance; + } + + ImGui.SliderFloat("Wireframe color variance", ref CFG.Current.GFX_Wireframe_Color_Variance, 0.0f, 1.0f); + + // Proxies + ImGui.ColorEdit3("Box region - base color", ref CFG.Current.GFX_Renderable_Box_BaseColor); + ImGui.ColorEdit3("Box region - highlight color", ref CFG.Current.GFX_Renderable_Box_HighlightColor); + + ImGui.ColorEdit3("Cylinder region - base color", ref CFG.Current.GFX_Renderable_Cylinder_BaseColor); + ImGui.ColorEdit3("Cylinder region - highlight color", ref CFG.Current.GFX_Renderable_Cylinder_HighlightColor); + + ImGui.ColorEdit3("Sphere region - base color", ref CFG.Current.GFX_Renderable_Sphere_BaseColor); + ImGui.ColorEdit3("Sphere region - highlight color", ref CFG.Current.GFX_Renderable_Sphere_HighlightColor); + + ImGui.ColorEdit3("Point region - base color", ref CFG.Current.GFX_Renderable_Point_BaseColor); + ImGui.ColorEdit3("Point region - highlight color", ref CFG.Current.GFX_Renderable_Point_HighlightColor); + + ImGui.ColorEdit3("Dummy poly - base color", ref CFG.Current.GFX_Renderable_DummyPoly_BaseColor); + ImGui.ColorEdit3("Dummy poly - highlight color", ref CFG.Current.GFX_Renderable_DummyPoly_HighlightColor); + + ImGui.ColorEdit3("Bone point - base color", ref CFG.Current.GFX_Renderable_BonePoint_BaseColor); + ImGui.ColorEdit3("Bone point - highlight color", ref CFG.Current.GFX_Renderable_BonePoint_HighlightColor); + + ImGui.ColorEdit3("Chr marker - base color", ref CFG.Current.GFX_Renderable_ModelMarker_Chr_BaseColor); + ImGui.ColorEdit3("Chr marker - highlight color", ref CFG.Current.GFX_Renderable_ModelMarker_Chr_HighlightColor); + + ImGui.ColorEdit3("Object marker - base color", ref CFG.Current.GFX_Renderable_ModelMarker_Object_BaseColor); + ImGui.ColorEdit3("Object marker - highlight color", ref CFG.Current.GFX_Renderable_ModelMarker_Object_HighlightColor); + + ImGui.ColorEdit3("Player marker - base color", ref CFG.Current.GFX_Renderable_ModelMarker_Player_BaseColor); + ImGui.ColorEdit3("Player marker - highlight color", ref CFG.Current.GFX_Renderable_ModelMarker_Player_HighlightColor); + + ImGui.ColorEdit3("Other marker - base color", ref CFG.Current.GFX_Renderable_ModelMarker_Other_BaseColor); + ImGui.ColorEdit3("Other marker - highlight color", ref CFG.Current.GFX_Renderable_ModelMarker_Other_HighlightColor); + + ImGui.ColorEdit3("Point light - base color", ref CFG.Current.GFX_Renderable_PointLight_BaseColor); + ImGui.ColorEdit3("Point light - highlight color", ref CFG.Current.GFX_Renderable_PointLight_HighlightColor); + + ImGui.ColorEdit3("Spot light - base color", ref CFG.Current.GFX_Renderable_SpotLight_BaseColor); + ImGui.ColorEdit3("Spot light - highlight color", ref CFG.Current.GFX_Renderable_SpotLight_HighlightColor); + + ImGui.ColorEdit3("Directional light - base color", ref CFG.Current.GFX_Renderable_DirectionalLight_BaseColor); + ImGui.ColorEdit3("Directional light - highlight color", ref CFG.Current.GFX_Renderable_DirectionalLight_HighlightColor); + + // Gizmos + ImGui.ColorEdit3("Gizmo - X Axis - base color", ref CFG.Current.GFX_Gizmo_X_BaseColor); + ImGui.ColorEdit3("Gizmo - X Axis - highlight color", ref CFG.Current.GFX_Gizmo_X_HighlightColor); + + ImGui.ColorEdit3("Gizmo - Y Axis - base color", ref CFG.Current.GFX_Gizmo_Y_BaseColor); + ImGui.ColorEdit3("Gizmo - Y Axis - highlight color", ref CFG.Current.GFX_Gizmo_Y_HighlightColor); + + ImGui.ColorEdit3("Gizmo - Z Axis - base color", ref CFG.Current.GFX_Gizmo_Z_BaseColor); + ImGui.ColorEdit3("Gizmo - Z Axis - highlight color", ref CFG.Current.GFX_Gizmo_Z_HighlightColor); + } + + if (ImGui.CollapsingHeader("Map Object Display Presets")) + { + ImGui.Text("Configure each of the six display presets available."); + + EditorDecorations.ShowHelpMarker("Reset the values within this section to their default values."); + if (ImGui.Button("Reset##DisplayPresets")) + { + CFG.Current.SceneFilter_Preset_01.Name = CFG.Default.SceneFilter_Preset_01.Name; + CFG.Current.SceneFilter_Preset_01.Filters = CFG.Default.SceneFilter_Preset_01.Filters; + CFG.Current.SceneFilter_Preset_02.Name = CFG.Default.SceneFilter_Preset_02.Name; + CFG.Current.SceneFilter_Preset_02.Filters = CFG.Default.SceneFilter_Preset_02.Filters; + CFG.Current.SceneFilter_Preset_03.Name = CFG.Default.SceneFilter_Preset_03.Name; + CFG.Current.SceneFilter_Preset_03.Filters = CFG.Default.SceneFilter_Preset_03.Filters; + CFG.Current.SceneFilter_Preset_04.Name = CFG.Default.SceneFilter_Preset_04.Name; + CFG.Current.SceneFilter_Preset_04.Filters = CFG.Default.SceneFilter_Preset_04.Filters; + CFG.Current.SceneFilter_Preset_05.Name = CFG.Default.SceneFilter_Preset_05.Name; + CFG.Current.SceneFilter_Preset_05.Filters = CFG.Default.SceneFilter_Preset_05.Filters; + CFG.Current.SceneFilter_Preset_06.Name = CFG.Default.SceneFilter_Preset_06.Name; + CFG.Current.SceneFilter_Preset_06.Filters = CFG.Default.SceneFilter_Preset_06.Filters; + } + + SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_01); + SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_02); + SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_03); + SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_04); + SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_05); + SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_06); + } + + ImGui.Unindent(); + } + + private void SettingsRenderFilterPresetEditor(CFG.RenderFilterPreset preset) + { + ImGui.PushID($"{preset.Name}##PresetEdit"); + if (ImGui.CollapsingHeader($"{preset.Name}##Header")) + { + ImGui.Indent(); + var nameInput = preset.Name; + ImGui.InputText("Preset Name", ref nameInput, 32); + if (ImGui.IsItemDeactivatedAfterEdit()) + { + preset.Name = nameInput; + } + + foreach (RenderFilter e in Enum.GetValues(typeof(RenderFilter))) + { + var ticked = false; + if (preset.Filters.HasFlag(e)) + { + ticked = true; + } + + if (ImGui.Checkbox(e.ToString(), ref ticked)) + { + if (ticked) + { + preset.Filters |= e; + } + else + { + preset.Filters &= ~e; + } + } + } + + ImGui.Unindent(); + } + + ImGui.PopID(); + } } diff --git a/src/StudioCore/MsbEditor/SceneTree.cs b/src/StudioCore/MsbEditor/SceneTree.cs index 04dae0f4c..0e4161dcf 100644 --- a/src/StudioCore/MsbEditor/SceneTree.cs +++ b/src/StudioCore/MsbEditor/SceneTree.cs @@ -14,7 +14,8 @@ using System.Numerics; using System.Runtime.InteropServices; using Veldrid; -using StudioCore.Interface; +using StudioCore.Editor; +using StudioCore.Banks.AliasBank; namespace StudioCore.MsbEditor; @@ -272,7 +273,7 @@ private unsafe void MapObjectSelectable(Entity e, bool visicon, bool hierarchial } var alias = AliasUtils.GetEntityAliasName(e); - AliasUtils.DisplayAlias(alias); + EditorDecorations.DisplayAlias(alias); } if (ImGui.IsItemClicked()) @@ -615,7 +616,7 @@ public unsafe void OnGui() { if (Locator.AssetLocator.Type is GameType.DarkSoulsIISOTFS) { - if (ParamBank.PrimaryBank.IsLoadingParams) + if (ParamBank.PrimaryBank.IsLoading) { ImGui.NewLine(); ImGui.Text(" Please wait for params to finish loading."); @@ -713,7 +714,7 @@ public unsafe void OnGui() if (CFG.Current.MapEditor_MapObjectList_ShowMapNames) { - AliasUtils.DisplayAlias(aliasName); + EditorDecorations.DisplayAlias(aliasName); } ImGui.EndGroup(); diff --git a/src/StudioCore/ParamEditor/MassParamEdit.cs b/src/StudioCore/ParamEditor/MassParamEdit.cs index e323b3ce2..2bb1c36ba 100644 --- a/src/StudioCore/ParamEditor/MassParamEdit.cs +++ b/src/StudioCore/ParamEditor/MassParamEdit.cs @@ -895,12 +895,12 @@ private void Setup() "Gives the value of the equivalent cell/field in the specified regulation or parambnd for the currently selected cell/field, row and param.\nWill fail if a row does not have an aux equivilent. Consider using && auxprop ID .*", bankName => { - if (!ParamBank.AuxBanks.ContainsKey(bankName[0])) + if (!ResDirectory.CurrentGame.AuxProjects.ContainsKey(bankName[0])) { throw new Exception($@"Could not locate paramBank {bankName[0]}"); } - ParamBank bank = ParamBank.AuxBanks[bankName[0]]; + ParamBank bank = ResDirectory.CurrentGame.AuxProjects[bankName[0]].ParamBank; return (i, param) => { var paramName = ParamBank.PrimaryBank.GetKeyForParam(param); @@ -929,7 +929,7 @@ private void Setup() }; }; }; - }, () => ParamBank.AuxBanks.Count > 0)); + }, () => ResDirectory.CurrentGame.AuxProjects.Count > 0)); argumentGetters.Add("vanillafield", newGetter(new[] { "field internalName" }, "Gives the value of the specified cell/field in the vanilla regulation or parambnd for the currently selected row and param.\nWill fail if a row does not have a vanilla equivilent. Consider using && !added", field => (i, param) => @@ -963,12 +963,12 @@ private void Setup() "Gives the value of the specified cell/field in the specified regulation or parambnd for the currently selected row and param.\nWill fail if a row does not have an aux equivilent. Consider using && auxprop ID .*", bankAndField => { - if (!ParamBank.AuxBanks.ContainsKey(bankAndField[0])) + if (!ResDirectory.CurrentGame.AuxProjects.ContainsKey(bankAndField[0])) { throw new Exception($@"Could not locate paramBank {bankAndField[0]}"); } - ParamBank bank = ParamBank.AuxBanks[bankAndField[0]]; + ParamBank bank = ResDirectory.CurrentGame.AuxProjects[bankAndField[0]].ParamBank; return (i, param) => { var paramName = ParamBank.PrimaryBank.GetKeyForParam(param); @@ -996,7 +996,7 @@ private void Setup() return (k, c) => v; }; }; - }, () => ParamBank.AuxBanks.Count > 0)); + }, () => ResDirectory.CurrentGame.AuxProjects.Count > 0)); argumentGetters.Add("paramlookup", newGetter(new[] { "param name", "row id", "field name" }, "Returns the specific value specified by the exact param, row and field.", address => { diff --git a/src/StudioCore/ParamEditor/ParamBank.cs b/src/StudioCore/ParamEditor/ParamBank.cs index fad809199..7b9463d8e 100644 --- a/src/StudioCore/ParamEditor/ParamBank.cs +++ b/src/StudioCore/ParamEditor/ParamBank.cs @@ -1,6 +1,5 @@ using Andre.Formats; using Microsoft.Extensions.Logging; -using Octokit; using SoulsFormats; using StudioCore.Editor; using StudioCore.Platform; @@ -15,125 +14,31 @@ namespace StudioCore.ParamEditor; /// /// Utilities for dealing with global params for a game /// -public class ParamBank +public partial class ParamBank : DataBank { - public enum ParamUpgradeResult - { - Success = 0, - RowConflictsFound = -1, - OldRegulationNotFound = -2, - OldRegulationVersionMismatch = -3, - OldRegulationMatchesCurrent = -4 - } - - public enum RowGetType - { - AllRows = 0, - ModifiedRows = 1, - SelectedRows = 2 - } - public static ParamBank PrimaryBank => Locator.ActiveProject.ParamBank; public static ParamBank VanillaBank => Locator.ActiveProject.ParentProject.ParamBank; - public static Dictionary AuxBanks = new(); - - /// - /// Mapping from path -> PARAMDEF for cache and and comparison purposes. TODO: check for paramdef comparisons and evaluate if the file/paramdef was actually the same. - /// - private static readonly Dictionary _paramdefsCache = new(); - public static string ClipboardParam = null; public static List ClipboardRows = new(); - /// - /// Mapping from ParamType -> PARAMDEF. - /// - private Dictionary _paramdefs = new(); - - //TODO private this - public Dictionary ParamMetas = new(); - - - /// - /// Mapping from Param filename -> Manual ParamType. - /// This is for params with no usable ParamType at some particular game version. - /// By convention, ParamTypes ending in "_TENTATIVE" do not have official data to reference. - /// - private Dictionary _tentativeParamType; - - /// - /// Map related params. - /// - public static readonly List DS2MapParamlist = new() - { - "demopointlight", - "demospotlight", - "eventlocation", - "eventparam", - "GeneralLocationEventParam", - "generatorparam", - "generatorregistparam", - "generatorlocation", - "generatordbglocation", - "hitgroupparam", - "intrudepointparam", - "mapobjectinstanceparam", - "maptargetdirparam", - "npctalkparam", - "treasureboxparam" - }; - - /// - /// Param name - FMGCategory map - /// - public static readonly List<(string, FmgEntryCategory)> ParamToFmgCategoryList = new() - { - ("EquipParamAccessory", FmgEntryCategory.Rings), - ("EquipParamGoods", FmgEntryCategory.Goods), - ("EquipParamWeapon", FmgEntryCategory.Weapons), - ("EquipParamProtector", FmgEntryCategory.Armor), - ("Magic", FmgEntryCategory.Spells), - ("EquipParamGem", FmgEntryCategory.Gem), - ("SwordArtsParam", FmgEntryCategory.SwordArts), - ("EquipParamGenerator", FmgEntryCategory.Generator), - ("EquipParamFcs", FmgEntryCategory.FCS), - ("EquipParamBooster", FmgEntryCategory.Booster), - ("ArchiveParam", FmgEntryCategory.Archive), - ("MissionParam", FmgEntryCategory.Mission) - }; - - private static readonly HashSet EMPTYSET = new(); - - public Project Project; - private Dictionary _params; private ulong _paramVersion; - private bool _pendingUpgrade; - private Dictionary> _primaryDiffCache; //If param != primaryparam - private Dictionary> _storedStrippedRowNames; - /// /// Dictionary of param file names that were given a tentative ParamType, and the original ParamType it had. /// Used to later restore original ParamType on write (if possible). /// private Dictionary _usedTentativeParamTypes; - private Dictionary> _vanillaDiffCache; //If param != vanillaparam - private Param EnemyParam => _params["EnemyParam"]; - public bool IsDefsLoaded { get; private set; } - public static bool IsMetaLoaded { get; private set; } - public bool IsLoadingParams { get; private set; } - public IReadOnlyDictionary Params { get { - if (IsLoadingParams) + if (IsLoading) { return null; } @@ -143,53 +48,8 @@ public IReadOnlyDictionary Params } public ulong ParamVersion => _paramVersion; - - public IReadOnlyDictionary> VanillaDiffCache - { - get - { - if (IsLoadingParams) - { - return null; - } - - { - if (VanillaBank == this) - { - return null; - } - } - return _vanillaDiffCache; - } - } - - public IReadOnlyDictionary> PrimaryDiffCache - { - get - { - if (IsLoadingParams) - { - return null; - } - - { - if (PrimaryBank == this) - { - return null; - } - } - return _primaryDiffCache; - } - } - - public ParamBank(Project owner) + public ParamBank(Project owner) : base(owner, "Params") { - Project = owner; - } - - public Dictionary GetParamDefs() - { - return _paramdefs; } private static FileNotFoundException CreateParamMissingException(GameType type) @@ -210,91 +70,6 @@ private static FileNotFoundException CreateParamMissingException(GameType type) $"Cannot locate param files for {type}.\nYour game folder may be missing game files, please verify game files through steam to restore them."); } - private List<(string, PARAMDEF)> LoadParamdefs() - { - _paramdefs = new Dictionary(); - _tentativeParamType = new Dictionary(); - var files = Project.AssetLocator.GetAllProjectFiles($@"Paramdex\{AssetUtils.GetGameIDForDir(Locator.ActiveProject.Type)}\Defs", ["*.xml"], true, false); - List<(string, PARAMDEF)> defPairs = new(); - foreach (var f in files) - { - if (!_paramdefsCache.TryGetValue(f, out PARAMDEF pdef)) - { - pdef = PARAMDEF.XmlDeserialize(f, true); - } - _paramdefs.Add(pdef.ParamType, pdef); - defPairs.Add((f, pdef)); - } - - var tentativeMappingPath = Project.AssetLocator.GetProjectFilePath($@"{Project.AssetLocator.GetParamdexDir()}\Defs\TentativeParamType.csv"); - if (File.Exists(tentativeMappingPath)) - { - // No proper CSV library is used currently, and all CSV parsing is in the context of param files. - // If a CSV library is introduced in DSMapStudio, use it here. - foreach (var line in File.ReadAllLines(tentativeMappingPath).Skip(1)) - { - var parts = line.Split(','); - if (parts.Length != 2 || string.IsNullOrWhiteSpace(parts[0]) || string.IsNullOrWhiteSpace(parts[1])) - { - throw new FormatException($"Malformed line in {tentativeMappingPath}: {line}"); - } - - _tentativeParamType[parts[0]] = parts[1]; - } - } - - return defPairs; - } - public void LoadParamMeta(List<(string, PARAMDEF)> defPairs) - { - //This way of tying stuff together still sucks - var mdir = Project.AssetLocator.GetProjectFilePath($@"{Locator.ActiveProject.AssetLocator.GetParamdexDir()}\Meta"); - foreach ((var f, PARAMDEF pdef) in defPairs) - { - var fName = f.Substring(f.LastIndexOf('\\') + 1); - var md = ParamMetaData.XmlDeserialize($@"{mdir}\{fName}", pdef); - ParamMetas.Add(pdef, md); - } - } - - public CompoundAction LoadParamDefaultNames(string param = null, bool onlyAffectEmptyNames = false, bool onlyAffectVanillaNames = false) - { - var files = param == null - ? Project.AssetLocator.GetAllProjectFiles($@"{Project.AssetLocator.GetParamdexDir()}\Names", ["*.txt"], true) - : new[] { Project.AssetLocator.GetProjectFilePath($@"{Project.AssetLocator.GetParamdexDir()}\Names\{param}.txt") }; - List actions = new(); - foreach (var f in files) - { - var fName = Path.GetFileNameWithoutExtension(f); - if (!_params.ContainsKey(fName)) - { - continue; - } - - var names = File.ReadAllText(f); - (var result, CompoundAction action) = - ParamIO.ApplySingleCSV(this, names, fName, "Name", ' ', true, onlyAffectEmptyNames, onlyAffectVanillaNames); - if (action == null) - { - TaskLogs.AddLog($"Could not apply name files for {fName}", - LogLevel.Warning); - } - else - { - actions.Add(action); - } - } - - return new CompoundAction(actions); - } - - public ActionManager TrimNewlineChrsFromNames() - { - (MassEditResult r, ActionManager child) = - MassParamEditRegex.PerformMassEdit(this, "param .*: id .*: name: replace \r:0", null); - return child; - } - private void LoadParamFromBinder(IBinder parambnd, ref Dictionary paramBank, out ulong version, bool checkVersion = false) { @@ -327,9 +102,9 @@ private void LoadParamFromBinder(IBinder parambnd, ref Dictionary p = Param.ReadIgnoreCompression(f.Bytes); if (!string.IsNullOrEmpty(p.ParamType)) { - if (!_paramdefs.ContainsKey(p.ParamType)) + if (!ResDirectory.CurrentGame.ParamDefBank.GetParamDefs().ContainsKey(p.ParamType)) { - if (_tentativeParamType.TryGetValue(paramName, out var newParamType)) + if (ResDirectory.CurrentGame.ParamDefBank.GetTentativeParamTypes().TryGetValue(paramName, out var newParamType)) { _usedTentativeParamTypes.Add(paramName, p.ParamType); p.ParamType = newParamType; @@ -348,7 +123,7 @@ private void LoadParamFromBinder(IBinder parambnd, ref Dictionary } else { - if (_tentativeParamType.TryGetValue(paramName, out var newParamType)) + if (ResDirectory.CurrentGame.ParamDefBank.GetTentativeParamTypes().TryGetValue(paramName, out var newParamType)) { _usedTentativeParamTypes.Add(paramName, p.ParamType); p.ParamType = newParamType; @@ -368,7 +143,7 @@ private void LoadParamFromBinder(IBinder parambnd, ref Dictionary else { p = Param.ReadIgnoreCompression(f.Bytes); - if (!_paramdefs.ContainsKey(p.ParamType ?? "")) + if (!ResDirectory.CurrentGame.ParamDefBank.GetParamDefs().ContainsKey(p.ParamType ?? "")) { TaskLogs.AddLog( $"Couldn't find ParamDef for param {paramName} with ParamType \"{p.ParamType}\".", @@ -391,7 +166,7 @@ private void LoadParamFromBinder(IBinder parambnd, ref Dictionary throw new Exception("Param type is unexpectedly null"); } - PARAMDEF def = _paramdefs[p.ParamType]; + PARAMDEF def = ResDirectory.CurrentGame.ParamDefBank.GetParamDefs()[p.ParamType]; try { p.ApplyParamdef(def, version); @@ -549,7 +324,7 @@ private void LoadParamsDS2FromFile(IEnumerable looseParams, string path, if (loose) { // Loose params: override params already loaded via regulation - PARAMDEF def = _paramdefs[lp.ParamType]; + PARAMDEF def = ResDirectory.CurrentGame.ParamDefBank.GetParamDefs()[lp.ParamType]; lp.ApplyParamdef(def); _params[name] = lp; } @@ -558,7 +333,7 @@ private void LoadParamsDS2FromFile(IEnumerable looseParams, string path, // Non-loose params: do not override params already loaded via regulation if (!_params.ContainsKey(name)) { - PARAMDEF def = _paramdefs[lp.ParamType]; + PARAMDEF def = ResDirectory.CurrentGame.ParamDefBank.GetParamDefs()[lp.ParamType]; lp.ApplyParamdef(def); _params.Add(name, lp); } @@ -711,27 +486,10 @@ private void LoadParamsAC6FromFile(string path, bool encrypted = true) LoadParamFromBinder(bnd, ref _params, out _, false); } } - - private void LoadParams() + protected override void Load() { - - IsDefsLoaded = false; - IsLoadingParams = true; - _params = new Dictionary(); - if (Project.Type != GameType.Undefined) - { - List<(string, PARAMDEF)> defPairs = LoadParamdefs(); - IsDefsLoaded = true; - TaskManager.Run(new TaskManager.LiveTask("Param - Load Meta", - TaskManager.RequeueType.WaitThenRequeue, false, () => - { - LoadParamMeta(defPairs); - IsMetaLoaded = true; - })); - } - if (Project.Type == GameType.DemonsSouls) { LoadParamsDES(); @@ -772,16 +530,14 @@ private void LoadParams() LoadParamsAC6(); } - ClearParamDiffCaches(); - - IsLoadingParams = false; + UICache.ClearCaches(); } + // TODO: Repair on-load actions //Some returns and repetition, but it keeps all threading and loading-flags visible inside this method - public static void ReloadParams(ProjectSettings settings, NewProjectOptions options) + /*public static void ReloadParams(ProjectSettings settings, NewProjectOptions options) { - IsMetaLoaded = false; - + //TODO: subsume with databank system AuxBanks = new Dictionary(); UICache.ClearCaches(); @@ -820,188 +576,15 @@ public static void ReloadParams(ProjectSettings settings, NewProjectOptions opti } UICache.ClearCaches(); })); - } + }*/ public static void LoadAuxBank(string dir, ProjectSettings settings = null) { - // skip the meme and just treat as project Project siblingVirtualProject = new Project(dir, Locator.ActiveProject.ParentProject, settings); - ParamBank newBank = siblingVirtualProject.ParamBank; - - newBank.LoadParams(); - - newBank.RefreshParamDiffCaches(true); - AuxBanks[Path.GetFileName(dir).Replace(' ', '_')] = newBank; - } - - - public void ClearParamDiffCaches() - { - _vanillaDiffCache = new Dictionary>(); - _primaryDiffCache = new Dictionary>(); - foreach (var param in _params.Keys) - { - _vanillaDiffCache.Add(param, new HashSet()); - _primaryDiffCache.Add(param, new HashSet()); - } - } - - public static void RefreshAllParamDiffCaches(bool checkAuxVanillaDiff) - { - PrimaryBank.RefreshParamDiffCaches(true); - foreach (KeyValuePair bank in AuxBanks) - { - bank.Value.RefreshParamDiffCaches(checkAuxVanillaDiff); - } - - UICache.ClearCaches(); - } - - public void RefreshParamDiffCaches(bool checkVanillaDiff) - { - if (this != VanillaBank && checkVanillaDiff) - { - _vanillaDiffCache = GetParamDiff(VanillaBank); - } - - if (this == VanillaBank && PrimaryBank._vanillaDiffCache != null) - { - _primaryDiffCache = PrimaryBank._vanillaDiffCache; - } - else if (this != PrimaryBank) - { - _primaryDiffCache = GetParamDiff(PrimaryBank); - } - - UICache.ClearCaches(); + StudioResource.Load(siblingVirtualProject, [siblingVirtualProject.ParamBank, siblingVirtualProject.ParamDiffBank]); + ResDirectory.CurrentGame.AuxProjects[Path.GetFileName(siblingVirtualProject.AssetLocator.RootDirectory).Replace(' ', '_')] = siblingVirtualProject; } - private Dictionary> GetParamDiff(ParamBank otherBank) - { - if (IsLoadingParams || otherBank == null || otherBank.IsLoadingParams) - { - return null; - } - - Dictionary> newCache = new(); - foreach (var param in _params.Keys) - { - HashSet cache = new(); - newCache.Add(param, cache); - Param p = _params[param]; - if (!otherBank._params.ContainsKey(param)) - { - Console.WriteLine("Missing vanilla param " + param); - continue; - } - - Param.Row[] rows = _params[param].Rows.OrderBy(r => r.ID).ToArray(); - Param.Row[] vrows = otherBank._params[param].Rows.OrderBy(r => r.ID).ToArray(); - - var vanillaIndex = 0; - var lastID = -1; - ReadOnlySpan lastVanillaRows = default; - for (var i = 0; i < rows.Length; i++) - { - var ID = rows[i].ID; - if (ID == lastID) - { - RefreshParamRowDiffCache(rows[i], lastVanillaRows, cache); - } - else - { - lastID = ID; - while (vanillaIndex < vrows.Length && vrows[vanillaIndex].ID < ID) - { - vanillaIndex++; - } - - if (vanillaIndex >= vrows.Length) - { - RefreshParamRowDiffCache(rows[i], Span.Empty, cache); - } - else - { - var count = 0; - while (vanillaIndex + count < vrows.Length && vrows[vanillaIndex + count].ID == ID) - { - count++; - } - - lastVanillaRows = new ReadOnlySpan(vrows, vanillaIndex, count); - RefreshParamRowDiffCache(rows[i], lastVanillaRows, cache); - vanillaIndex += count; - } - } - } - } - - return newCache; - } - - private static void RefreshParamRowDiffCache(Param.Row row, ReadOnlySpan otherBankRows, - HashSet cache) - { - if (IsChanged(row, otherBankRows)) - { - cache.Add(row.ID); - } - else - { - cache.Remove(row.ID); - } - } - - public void RefreshParamRowDiffs(Param.Row row, string param) - { - if (param == null) - { - return; - } - - if (VanillaBank.Params.ContainsKey(param) && VanillaDiffCache != null && - VanillaDiffCache.ContainsKey(param)) - { - Param.Row[] otherBankRows = VanillaBank.Params[param].Rows.Where(cell => cell.ID == row.ID).ToArray(); - RefreshParamRowDiffCache(row, otherBankRows, VanillaDiffCache[param]); - } - - if (this != PrimaryBank) - { - return; - } - - foreach (ParamBank aux in AuxBanks.Values) - { - if (!aux.Params.ContainsKey(param) || aux.PrimaryDiffCache == null || - !aux.PrimaryDiffCache.ContainsKey(param)) - { - continue; // Don't try for now - } - - Param.Row[] otherBankRows = aux.Params[param].Rows.Where(cell => cell.ID == row.ID).ToArray(); - RefreshParamRowDiffCache(row, otherBankRows, aux.PrimaryDiffCache[param]); - } - } - - private static bool IsChanged(Param.Row row, ReadOnlySpan vanillaRows) - { - //List vanils = vanilla.Rows.Where(cell => cell.ID == row.ID).ToList(); - if (vanillaRows.Length == 0) - { - return true; - } - - foreach (Param.Row vrow in vanillaRows) - { - if (row.RowMatches(vrow)) - { - return false; //if we find a matching vanilla row - } - } - - return true; - } private void SaveParamsDS1() { var dir = Project.ParentProject.AssetLocator.RootDirectory; @@ -1481,9 +1064,9 @@ void OverwriteParamsAC6(BND4 paramBnd) _pendingUpgrade = false; } - - public void SaveParams(bool loose = false) + public override void Save() { + bool loose = Project.Settings.UseLooseParams; if (_params == null) { return; @@ -1530,347 +1113,6 @@ public void SaveParams(bool loose = false) } } - private static Param UpgradeParam(Param source, Param oldVanilla, Param newVanilla, HashSet rowConflicts) - { - // Presorting this would make it easier, but we're trying to preserve order as much as possible - // Unfortunately given that rows aren't guaranteed to be sorted and there can be duplicate IDs, - // we try to respect the existing order and IDs as much as possible. - - // In order to assemble the final param, the param needs to know where to sort rows from given the - // following rules: - // 1. If a row with a given ID is unchanged from source to oldVanilla, we source from newVanilla - // 2. If a row with a given ID is deleted from source compared to oldVanilla, we don't take any row - // 3. If a row with a given ID is changed from source compared to oldVanilla, we source from source - // 4. If a row has duplicate IDs, we treat them as if the rows were deduplicated and process them - // in the order they appear. - - // List of rows that are in source but not oldVanilla - Dictionary> addedRows = new(source.Rows.Count); - - // List of rows in oldVanilla that aren't in source - Dictionary> deletedRows = new(source.Rows.Count); - - // List of rows that are in source and oldVanilla, but are modified - Dictionary> modifiedRows = new(source.Rows.Count); - - // List of rows that only had the name changed - Dictionary> renamedRows = new(source.Rows.Count); - - // List of ordered edit operations for each ID - Dictionary> editOperations = new(source.Rows.Count); - - // First off we go through source and everything starts as an added param - foreach (Param.Row row in source.Rows) - { - if (!addedRows.ContainsKey(row.ID)) - { - addedRows.Add(row.ID, new List()); - } - - addedRows[row.ID].Add(row); - } - - // Next we go through oldVanilla to determine if a row is added, deleted, modified, or unmodified - foreach (Param.Row row in oldVanilla.Rows) - { - // First off if the row did not exist in the source, it's deleted - if (!addedRows.ContainsKey(row.ID)) - { - if (!deletedRows.ContainsKey(row.ID)) - { - deletedRows.Add(row.ID, new List()); - } - - deletedRows[row.ID].Add(row); - if (!editOperations.ContainsKey(row.ID)) - { - editOperations.Add(row.ID, new List()); - } - - editOperations[row.ID].Add(EditOperation.Delete); - continue; - } - - // Otherwise the row exists in source. Time to classify it. - List list = addedRows[row.ID]; - - // First we see if we match the first target row. If so we can remove it. - if (row.DataEquals(list[0])) - { - Param.Row modrow = list[0]; - list.RemoveAt(0); - if (list.Count == 0) - { - addedRows.Remove(row.ID); - } - - if (!editOperations.ContainsKey(row.ID)) - { - editOperations.Add(row.ID, new List()); - } - - // See if the name was not updated - if ((modrow.Name == null && row.Name == null) || - (modrow.Name != null && row.Name != null && modrow.Name == row.Name)) - { - editOperations[row.ID].Add(EditOperation.Match); - continue; - } - - // Name was updated - editOperations[row.ID].Add(EditOperation.NameChange); - if (!renamedRows.ContainsKey(row.ID)) - { - renamedRows.Add(row.ID, new List()); - } - - renamedRows[row.ID].Add(modrow); - - continue; - } - - // Otherwise it is modified - if (!modifiedRows.ContainsKey(row.ID)) - { - modifiedRows.Add(row.ID, new List()); - } - - modifiedRows[row.ID].Add(list[0]); - list.RemoveAt(0); - if (list.Count == 0) - { - addedRows.Remove(row.ID); - } - - if (!editOperations.ContainsKey(row.ID)) - { - editOperations.Add(row.ID, new List()); - } - - editOperations[row.ID].Add(EditOperation.Modify); - } - - // Mark all remaining rows as added - foreach (KeyValuePair> entry in addedRows) - { - if (!editOperations.ContainsKey(entry.Key)) - { - editOperations.Add(entry.Key, new List()); - } - - foreach (List k in editOperations.Values) - { - editOperations[entry.Key].Add(EditOperation.Add); - } - } - - if (editOperations.All(kvp => kvp.Value.All(eo => eo == EditOperation.Match))) - { - return oldVanilla; - } - - Param dest = new(newVanilla); - - // Now try to build the destination from the new regulation with the edit operations in mind - var pendingAdds = addedRows.Keys.OrderBy(e => e).ToArray(); - var currPendingAdd = 0; - var lastID = 0; - foreach (Param.Row row in newVanilla.Rows) - { - // See if we have any pending adds we can slot in - while (currPendingAdd < pendingAdds.Length && - pendingAdds[currPendingAdd] >= lastID && - pendingAdds[currPendingAdd] < row.ID) - { - if (!addedRows.ContainsKey(pendingAdds[currPendingAdd])) - { - currPendingAdd++; - continue; - } - - foreach (Param.Row arow in addedRows[pendingAdds[currPendingAdd]]) - { - dest.AddRow(new Param.Row(arow, dest)); - } - - addedRows.Remove(pendingAdds[currPendingAdd]); - editOperations.Remove(pendingAdds[currPendingAdd]); - currPendingAdd++; - } - - lastID = row.ID; - - if (!editOperations.ContainsKey(row.ID)) - { - // No edit operations for this ID, so just add it (likely a new row in the update) - dest.AddRow(new Param.Row(row, dest)); - continue; - } - - // Pop the latest operation we need to do - EditOperation operation = editOperations[row.ID][0]; - editOperations[row.ID].RemoveAt(0); - if (editOperations[row.ID].Count == 0) - { - editOperations.Remove(row.ID); - } - - if (operation == EditOperation.Add) - { - // Getting here means both the mod and the updated regulation added a row. Our current strategy is - // to overwrite the new vanilla row with the modded one and add to the conflict log to give the user - rowConflicts.Add(row.ID); - dest.AddRow(new Param.Row(addedRows[row.ID][0], dest)); - addedRows[row.ID].RemoveAt(0); - if (addedRows[row.ID].Count == 0) - { - addedRows.Remove(row.ID); - } - } - else if (operation == EditOperation.Match) - { - // Match means we inherit updated param - dest.AddRow(new Param.Row(row, dest)); - } - else if (operation == EditOperation.Delete) - { - // deleted means we don't add anything - deletedRows[row.ID].RemoveAt(0); - if (deletedRows[row.ID].Count == 0) - { - deletedRows.Remove(row.ID); - } - } - else if (operation == EditOperation.Modify) - { - // Modified means we use the modded regulation's param - dest.AddRow(new Param.Row(modifiedRows[row.ID][0], dest)); - modifiedRows[row.ID].RemoveAt(0); - if (modifiedRows[row.ID].Count == 0) - { - modifiedRows.Remove(row.ID); - } - } - else if (operation == EditOperation.NameChange) - { - // Inherit name - Param.Row newRow = new(row, dest); - newRow.Name = renamedRows[row.ID][0].Name; - dest.AddRow(newRow); - renamedRows[row.ID].RemoveAt(0); - if (renamedRows[row.ID].Count == 0) - { - renamedRows.Remove(row.ID); - } - } - } - - // Take care of any more pending adds - for (; currPendingAdd < pendingAdds.Length; currPendingAdd++) - { - // If the pending add doesn't exist in the added rows list, it was a conflicting row - if (!addedRows.ContainsKey(pendingAdds[currPendingAdd])) - { - continue; - } - - foreach (Param.Row arow in addedRows[pendingAdds[currPendingAdd]]) - { - dest.AddRow(new Param.Row(arow, dest)); - } - - addedRows.Remove(pendingAdds[currPendingAdd]); - editOperations.Remove(pendingAdds[currPendingAdd]); - } - - return dest; - } - - // Param upgrade. Currently for Elden Ring only. - public ParamUpgradeResult UpgradeRegulation(ParamBank vanillaBank, string oldVanillaParamPath, - Dictionary> conflictingParams) - { - // First we need to load the old regulation - if (!File.Exists(oldVanillaParamPath)) - { - return ParamUpgradeResult.OldRegulationNotFound; - } - - // Backup modded params - string modRegulationPath = $@"{Project.AssetLocator.RootDirectory}\regulation.bin"; - File.Copy(modRegulationPath, $@"{modRegulationPath}.upgrade.bak", true); - - // Load old vanilla regulation - BND4 oldVanillaParamBnd; - if (Project.Type == GameType.EldenRing) - { - oldVanillaParamBnd = SFUtil.DecryptERRegulation(oldVanillaParamPath); - } - else if (Project.Type == GameType.ArmoredCoreVI) - { - oldVanillaParamBnd = SFUtil.DecryptAC6Regulation(oldVanillaParamPath); - } - else - { - throw new NotImplementedException( - $"Param upgrading for game type {Project.Type} is not supported."); - } - - Dictionary oldVanillaParams = new(); - ulong version; - LoadParamFromBinder(oldVanillaParamBnd, ref oldVanillaParams, out version, true); - if (version != ParamVersion) - { - return ParamUpgradeResult.OldRegulationVersionMismatch; - } - - Dictionary updatedParams = new(); - // Now we must diff everything to try and find changed/added rows for each param - var anyUpgrades = false; - foreach (var k in vanillaBank.Params.Keys) - { - // If the param is completely new, just take it - if (!oldVanillaParams.ContainsKey(k) || !Params.ContainsKey(k)) - { - updatedParams.Add(k, vanillaBank.Params[k]); - continue; - } - - // Otherwise try to upgrade - HashSet conflicts = new(); - Param res = UpgradeParam(Params[k], oldVanillaParams[k], vanillaBank.Params[k], conflicts); - if (res != oldVanillaParams[k]) - { - anyUpgrades = true; - } - - updatedParams.Add(k, res); - - if (conflicts.Count > 0) - { - conflictingParams.Add(k, conflicts); - } - } - - if (!anyUpgrades) - { - return ParamUpgradeResult.OldRegulationMatchesCurrent; - } - - var oldVersion = _paramVersion; - - // Set new params - _params = updatedParams; - _paramVersion = VanillaBank.ParamVersion; - _pendingUpgrade = true; - - // Refresh dirty cache - UICache.ClearCaches(); - RefreshAllParamDiffCaches(false); - - return conflictingParams.Count > 0 ? ParamUpgradeResult.RowConflictsFound : ParamUpgradeResult.Success; - } - public string GetChrIDForEnemy(long enemyID) { Param.Row enemy = EnemyParam?[(int)enemyID]; @@ -1913,116 +1155,8 @@ public Param GetParamFromName(string param) return null; } - public HashSet GetVanillaDiffRows(string param) - { - IReadOnlyDictionary> allDiffs = VanillaDiffCache; - if (allDiffs == null || !allDiffs.ContainsKey(param)) - { - return EMPTYSET; - } - - return allDiffs[param]; - } - - public HashSet GetPrimaryDiffRows(string param) - { - IReadOnlyDictionary> allDiffs = PrimaryDiffCache; - if (allDiffs == null || !allDiffs.ContainsKey(param)) - { - return EMPTYSET; - } - - return allDiffs[param]; - } - - /// - /// Loads row names from external files and applies them to params. - /// Uses indicies rather than IDs. - /// - private void LoadExternalRowNames() - { - var failCount = 0; - foreach (KeyValuePair p in _params) - { - var path = Project.AssetLocator.GetStrippedRowNamesPath(p.Key); - if (File.Exists(path)) - { - var names = File.ReadAllLines(path); - if (names.Length != p.Value.Rows.Count) - { - TaskLogs.AddLog($"External row names could not be applied to {p.Key}, row count does not match", - LogLevel.Warning, TaskLogs.LogPriority.Low); - failCount++; - continue; - } - - for (var i = 0; i < names.Length; i++) - { - p.Value.Rows[i].Name = names[i]; - } - } - } - - if (failCount > 0) - { - TaskLogs.AddLog( - $"External row names could not be applied to {failCount} params due to non-matching row counts.", - LogLevel.Warning); - } - } - - /// - /// Strips row names from params, saves them to files, and stores them to be restored after saving params. - /// Should always be used in conjunction with RestoreStrippedRowNames(). - /// - private void StripRowNames() - { - _storedStrippedRowNames = new Dictionary>(); - foreach (KeyValuePair p in _params) - { - _storedStrippedRowNames.TryAdd(p.Key, new List()); - List list = _storedStrippedRowNames[p.Key]; - foreach (Param.Row r in p.Value.Rows) - { - list.Add(r.Name); - r.Name = ""; - } - - var path = Project.AssetLocator.GetStrippedRowNamesPath(p.Key); - Directory.CreateDirectory(Path.GetDirectoryName(path)); - File.WriteAllLines(path, list); - } - } - - /// - /// Restores stripped row names back to all params. - /// Should always be used in conjunction with StripRowNames(). - /// - private void RestoreStrippedRowNames() - { - if (_storedStrippedRowNames == null) - { - throw new InvalidOperationException("No stripped row names have been stored."); - } - - foreach (KeyValuePair p in _params) - { - List storedNames = _storedStrippedRowNames[p.Key]; - for (var i = 0; i < p.Value.Rows.Count; i++) - { - p.Value.Rows[i].Name = storedNames[i]; - } - } - - _storedStrippedRowNames = null; - } - - private enum EditOperation + protected override IEnumerable GetDependencies(Project project) { - Add, - Delete, - Modify, - NameChange, - Match + return [ResDirectory.CurrentGame.ParamDefBank]; } } diff --git a/src/StudioCore/ParamEditor/ParamBankUpgrader.cs b/src/StudioCore/ParamEditor/ParamBankUpgrader.cs new file mode 100644 index 000000000..adcee953c --- /dev/null +++ b/src/StudioCore/ParamEditor/ParamBankUpgrader.cs @@ -0,0 +1,378 @@ +using Andre.Formats; +using Microsoft.Extensions.Logging; +using SoulsFormats; +using StudioCore.Editor; +using StudioCore.Platform; +using StudioCore.TextEditor; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace StudioCore.ParamEditor; + +/// +/// Utilities for dealing with global params for a game +/// +public partial class ParamBank : DataBank +{ + public enum ParamUpgradeResult + { + Success = 0, + RowConflictsFound = -1, + OldRegulationNotFound = -2, + OldRegulationVersionMismatch = -3, + OldRegulationMatchesCurrent = -4 + } + private enum EditOperation + { + Add, + Delete, + Modify, + NameChange, + Match + } + + private bool _pendingUpgrade; + + private static Param UpgradeParam(Param source, Param oldVanilla, Param newVanilla, HashSet rowConflicts) + { + // Presorting this would make it easier, but we're trying to preserve order as much as possible + // Unfortunately given that rows aren't guaranteed to be sorted and there can be duplicate IDs, + // we try to respect the existing order and IDs as much as possible. + + // In order to assemble the final param, the param needs to know where to sort rows from given the + // following rules: + // 1. If a row with a given ID is unchanged from source to oldVanilla, we source from newVanilla + // 2. If a row with a given ID is deleted from source compared to oldVanilla, we don't take any row + // 3. If a row with a given ID is changed from source compared to oldVanilla, we source from source + // 4. If a row has duplicate IDs, we treat them as if the rows were deduplicated and process them + // in the order they appear. + + // List of rows that are in source but not oldVanilla + Dictionary> addedRows = new(source.Rows.Count); + + // List of rows in oldVanilla that aren't in source + Dictionary> deletedRows = new(source.Rows.Count); + + // List of rows that are in source and oldVanilla, but are modified + Dictionary> modifiedRows = new(source.Rows.Count); + + // List of rows that only had the name changed + Dictionary> renamedRows = new(source.Rows.Count); + + // List of ordered edit operations for each ID + Dictionary> editOperations = new(source.Rows.Count); + + // First off we go through source and everything starts as an added param + foreach (Param.Row row in source.Rows) + { + if (!addedRows.ContainsKey(row.ID)) + { + addedRows.Add(row.ID, new List()); + } + + addedRows[row.ID].Add(row); + } + + // Next we go through oldVanilla to determine if a row is added, deleted, modified, or unmodified + foreach (Param.Row row in oldVanilla.Rows) + { + // First off if the row did not exist in the source, it's deleted + if (!addedRows.ContainsKey(row.ID)) + { + if (!deletedRows.ContainsKey(row.ID)) + { + deletedRows.Add(row.ID, new List()); + } + + deletedRows[row.ID].Add(row); + if (!editOperations.ContainsKey(row.ID)) + { + editOperations.Add(row.ID, new List()); + } + + editOperations[row.ID].Add(EditOperation.Delete); + continue; + } + + // Otherwise the row exists in source. Time to classify it. + List list = addedRows[row.ID]; + + // First we see if we match the first target row. If so we can remove it. + if (row.DataEquals(list[0])) + { + Param.Row modrow = list[0]; + list.RemoveAt(0); + if (list.Count == 0) + { + addedRows.Remove(row.ID); + } + + if (!editOperations.ContainsKey(row.ID)) + { + editOperations.Add(row.ID, new List()); + } + + // See if the name was not updated + if ((modrow.Name == null && row.Name == null) || + (modrow.Name != null && row.Name != null && modrow.Name == row.Name)) + { + editOperations[row.ID].Add(EditOperation.Match); + continue; + } + + // Name was updated + editOperations[row.ID].Add(EditOperation.NameChange); + if (!renamedRows.ContainsKey(row.ID)) + { + renamedRows.Add(row.ID, new List()); + } + + renamedRows[row.ID].Add(modrow); + + continue; + } + + // Otherwise it is modified + if (!modifiedRows.ContainsKey(row.ID)) + { + modifiedRows.Add(row.ID, new List()); + } + + modifiedRows[row.ID].Add(list[0]); + list.RemoveAt(0); + if (list.Count == 0) + { + addedRows.Remove(row.ID); + } + + if (!editOperations.ContainsKey(row.ID)) + { + editOperations.Add(row.ID, new List()); + } + + editOperations[row.ID].Add(EditOperation.Modify); + } + + // Mark all remaining rows as added + foreach (KeyValuePair> entry in addedRows) + { + if (!editOperations.ContainsKey(entry.Key)) + { + editOperations.Add(entry.Key, new List()); + } + + foreach (List k in editOperations.Values) + { + editOperations[entry.Key].Add(EditOperation.Add); + } + } + + if (editOperations.All(kvp => kvp.Value.All(eo => eo == EditOperation.Match))) + { + return oldVanilla; + } + + Param dest = new(newVanilla); + + // Now try to build the destination from the new regulation with the edit operations in mind + var pendingAdds = addedRows.Keys.OrderBy(e => e).ToArray(); + var currPendingAdd = 0; + var lastID = 0; + foreach (Param.Row row in newVanilla.Rows) + { + // See if we have any pending adds we can slot in + while (currPendingAdd < pendingAdds.Length && + pendingAdds[currPendingAdd] >= lastID && + pendingAdds[currPendingAdd] < row.ID) + { + if (!addedRows.ContainsKey(pendingAdds[currPendingAdd])) + { + currPendingAdd++; + continue; + } + + foreach (Param.Row arow in addedRows[pendingAdds[currPendingAdd]]) + { + dest.AddRow(new Param.Row(arow, dest)); + } + + addedRows.Remove(pendingAdds[currPendingAdd]); + editOperations.Remove(pendingAdds[currPendingAdd]); + currPendingAdd++; + } + + lastID = row.ID; + + if (!editOperations.ContainsKey(row.ID)) + { + // No edit operations for this ID, so just add it (likely a new row in the update) + dest.AddRow(new Param.Row(row, dest)); + continue; + } + + // Pop the latest operation we need to do + EditOperation operation = editOperations[row.ID][0]; + editOperations[row.ID].RemoveAt(0); + if (editOperations[row.ID].Count == 0) + { + editOperations.Remove(row.ID); + } + + if (operation == EditOperation.Add) + { + // Getting here means both the mod and the updated regulation added a row. Our current strategy is + // to overwrite the new vanilla row with the modded one and add to the conflict log to give the user + rowConflicts.Add(row.ID); + dest.AddRow(new Param.Row(addedRows[row.ID][0], dest)); + addedRows[row.ID].RemoveAt(0); + if (addedRows[row.ID].Count == 0) + { + addedRows.Remove(row.ID); + } + } + else if (operation == EditOperation.Match) + { + // Match means we inherit updated param + dest.AddRow(new Param.Row(row, dest)); + } + else if (operation == EditOperation.Delete) + { + // deleted means we don't add anything + deletedRows[row.ID].RemoveAt(0); + if (deletedRows[row.ID].Count == 0) + { + deletedRows.Remove(row.ID); + } + } + else if (operation == EditOperation.Modify) + { + // Modified means we use the modded regulation's param + dest.AddRow(new Param.Row(modifiedRows[row.ID][0], dest)); + modifiedRows[row.ID].RemoveAt(0); + if (modifiedRows[row.ID].Count == 0) + { + modifiedRows.Remove(row.ID); + } + } + else if (operation == EditOperation.NameChange) + { + // Inherit name + Param.Row newRow = new(row, dest); + newRow.Name = renamedRows[row.ID][0].Name; + dest.AddRow(newRow); + renamedRows[row.ID].RemoveAt(0); + if (renamedRows[row.ID].Count == 0) + { + renamedRows.Remove(row.ID); + } + } + } + + // Take care of any more pending adds + for (; currPendingAdd < pendingAdds.Length; currPendingAdd++) + { + // If the pending add doesn't exist in the added rows list, it was a conflicting row + if (!addedRows.ContainsKey(pendingAdds[currPendingAdd])) + { + continue; + } + + foreach (Param.Row arow in addedRows[pendingAdds[currPendingAdd]]) + { + dest.AddRow(new Param.Row(arow, dest)); + } + + addedRows.Remove(pendingAdds[currPendingAdd]); + editOperations.Remove(pendingAdds[currPendingAdd]); + } + + return dest; + } + + // Param upgrade. Currently for Elden Ring only. + public ParamUpgradeResult UpgradeRegulation(ParamBank vanillaBank, string oldVanillaParamPath, + Dictionary> conflictingParams) + { + // First we need to load the old regulation + if (!File.Exists(oldVanillaParamPath)) + { + return ParamUpgradeResult.OldRegulationNotFound; + } + + // Backup modded params + string modRegulationPath = $@"{Project.AssetLocator.RootDirectory}\regulation.bin"; + File.Copy(modRegulationPath, $@"{modRegulationPath}.upgrade.bak", true); + + // Load old vanilla regulation + BND4 oldVanillaParamBnd; + if (Project.Type == GameType.EldenRing) + { + oldVanillaParamBnd = SFUtil.DecryptERRegulation(oldVanillaParamPath); + } + else if (Project.Type == GameType.ArmoredCoreVI) + { + oldVanillaParamBnd = SFUtil.DecryptAC6Regulation(oldVanillaParamPath); + } + else + { + throw new NotImplementedException( + $"Param upgrading for game type {Project.Type} is not supported."); + } + + Dictionary oldVanillaParams = new(); + ulong version; + LoadParamFromBinder(oldVanillaParamBnd, ref oldVanillaParams, out version, true); + if (version != ParamVersion) + { + return ParamUpgradeResult.OldRegulationVersionMismatch; + } + + Dictionary updatedParams = new(); + // Now we must diff everything to try and find changed/added rows for each param + var anyUpgrades = false; + foreach (var k in vanillaBank.Params.Keys) + { + // If the param is completely new, just take it + if (!oldVanillaParams.ContainsKey(k) || !Params.ContainsKey(k)) + { + updatedParams.Add(k, vanillaBank.Params[k]); + continue; + } + + // Otherwise try to upgrade + HashSet conflicts = new(); + Param res = UpgradeParam(Params[k], oldVanillaParams[k], vanillaBank.Params[k], conflicts); + if (res != oldVanillaParams[k]) + { + anyUpgrades = true; + } + + updatedParams.Add(k, res); + + if (conflicts.Count > 0) + { + conflictingParams.Add(k, conflicts); + } + } + + if (!anyUpgrades) + { + return ParamUpgradeResult.OldRegulationMatchesCurrent; + } + + var oldVersion = _paramVersion; + + // Set new params + _params = updatedParams; + _paramVersion = VanillaBank.ParamVersion; + _pendingUpgrade = true; + + // Refresh dirty cache + UICache.ClearCaches(); + ParamDiffBank.RefreshAllParamDiffCaches(false); + + return conflictingParams.Count > 0 ? ParamUpgradeResult.RowConflictsFound : ParamUpgradeResult.Success; + } +} diff --git a/src/StudioCore/ParamEditor/ParamBankUtilities.cs b/src/StudioCore/ParamEditor/ParamBankUtilities.cs new file mode 100644 index 000000000..112794fdb --- /dev/null +++ b/src/StudioCore/ParamEditor/ParamBankUtilities.cs @@ -0,0 +1,160 @@ +using Andre.Formats; +using Microsoft.Extensions.Logging; +using SoulsFormats; +using StudioCore.Editor; +using StudioCore.Platform; +using StudioCore.TextEditor; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace StudioCore.ParamEditor; + +/// +/// Utilities for dealing with global params for a game +/// +public partial class ParamBank : DataBank +{ + + /// + /// Param name - FMGCategory map + /// + public static readonly List<(string, FmgEntryCategory)> ParamToFmgCategoryList = new() + { + ("EquipParamAccessory", FmgEntryCategory.Rings), + ("EquipParamGoods", FmgEntryCategory.Goods), + ("EquipParamWeapon", FmgEntryCategory.Weapons), + ("EquipParamProtector", FmgEntryCategory.Armor), + ("Magic", FmgEntryCategory.Spells), + ("EquipParamGem", FmgEntryCategory.Gem), + ("SwordArtsParam", FmgEntryCategory.SwordArts), + ("EquipParamGenerator", FmgEntryCategory.Generator), + ("EquipParamFcs", FmgEntryCategory.FCS), + ("EquipParamBooster", FmgEntryCategory.Booster), + ("ArchiveParam", FmgEntryCategory.Archive), + ("MissionParam", FmgEntryCategory.Mission) + }; + + private Dictionary> _storedStrippedRowNames; + + public CompoundAction LoadParamDefaultNames(string param = null, bool onlyAffectEmptyNames = false, bool onlyAffectVanillaNames = false) + { + var files = param == null + ? Project.AssetLocator.GetAllProjectFiles($@"{Project.AssetLocator.GetParamdexDir()}\Names", ["*.txt"], true) + : new[] { Project.AssetLocator.GetProjectFilePath($@"{Project.AssetLocator.GetParamdexDir()}\Names\{param}.txt") }; + List actions = new(); + foreach (var f in files) + { + var fName = Path.GetFileNameWithoutExtension(f); + if (!_params.ContainsKey(fName)) + { + continue; + } + + var names = File.ReadAllText(f); + (var result, CompoundAction action) = + ParamIO.ApplySingleCSV(this, names, fName, "Name", ' ', true, onlyAffectEmptyNames, onlyAffectVanillaNames); + if (action == null) + { + TaskLogs.AddLog($"Could not apply name files for {fName}", + LogLevel.Warning); + } + else + { + actions.Add(action); + } + } + + return new CompoundAction(actions); + } + + public ActionManager TrimNewlineChrsFromNames() + { + (MassEditResult r, ActionManager child) = + MassParamEditRegex.PerformMassEdit(this, "param .*: id .*: name: replace \r:0", null); + return child; + } + + /// + /// Loads row names from external files and applies them to params. + /// Uses indicies rather than IDs. + /// + private void LoadExternalRowNames() + { + var failCount = 0; + foreach (KeyValuePair p in _params) + { + var path = Project.AssetLocator.GetStrippedRowNamesPath(p.Key); + if (File.Exists(path)) + { + var names = File.ReadAllLines(path); + if (names.Length != p.Value.Rows.Count) + { + TaskLogs.AddLog($"External row names could not be applied to {p.Key}, row count does not match", + LogLevel.Warning, TaskLogs.LogPriority.Low); + failCount++; + continue; + } + + for (var i = 0; i < names.Length; i++) + { + p.Value.Rows[i].Name = names[i]; + } + } + } + + if (failCount > 0) + { + TaskLogs.AddLog( + $"External row names could not be applied to {failCount} params due to non-matching row counts.", + LogLevel.Warning); + } + } + + /// + /// Strips row names from params, saves them to files, and stores them to be restored after saving params. + /// Should always be used in conjunction with RestoreStrippedRowNames(). + /// + private void StripRowNames() + { + _storedStrippedRowNames = new Dictionary>(); + foreach (KeyValuePair p in _params) + { + _storedStrippedRowNames.TryAdd(p.Key, new List()); + List list = _storedStrippedRowNames[p.Key]; + foreach (Param.Row r in p.Value.Rows) + { + list.Add(r.Name); + r.Name = ""; + } + + var path = Project.AssetLocator.GetStrippedRowNamesPath(p.Key); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + File.WriteAllLines(path, list); + } + } + + /// + /// Restores stripped row names back to all params. + /// Should always be used in conjunction with StripRowNames(). + /// + private void RestoreStrippedRowNames() + { + if (_storedStrippedRowNames == null) + { + throw new InvalidOperationException("No stripped row names have been stored."); + } + + foreach (KeyValuePair p in _params) + { + List storedNames = _storedStrippedRowNames[p.Key]; + for (var i = 0; i < p.Value.Rows.Count; i++) + { + p.Value.Rows[i].Name = storedNames[i]; + } + } + + _storedStrippedRowNames = null; + } +} diff --git a/src/StudioCore/ParamEditor/ParamCFG.cs b/src/StudioCore/ParamEditor/ParamCFG.cs new file mode 100644 index 000000000..788bfa000 --- /dev/null +++ b/src/StudioCore/ParamEditor/ParamCFG.cs @@ -0,0 +1,53 @@ +using StudioCore.Platform; +using StudioCore.Scene; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Numerics; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace StudioCore; + +public partial class CFG +{ + + //private string _Param_Export_Array_Delimiter = "|"; + private string _Param_Export_Delimiter = ","; + + public bool Param_AdvancedMassedit = false; + public bool Param_AllowFieldReorder = true; + public bool Param_AlphabeticalParams = true; + public bool Param_DisableLineWrapping = false; + public bool Param_DisableRowGrouping = false; + public bool Param_HideEnums = false; + public bool Param_HideReferenceRows = false; + public bool Param_MakeMetaNamesPrimary = true; + public bool Param_PasteAfterSelection = false; + public bool Param_PasteThenSelect = true; + public bool Param_ShowFieldOffsets = false; + public bool Param_ShowHotkeysInContextMenu = true; + public bool Param_ShowSecondaryNames = true; + public bool Param_ShowVanillaParams = true; + public bool UI_CompactParams = false; + + public string Param_Export_Delimiter + { + get + { + if (_Param_Export_Delimiter.Length == 0) + { + _Param_Export_Delimiter = Default.Param_Export_Delimiter; + } + else if (_Param_Export_Delimiter == "|") + { + _Param_Export_Delimiter = + Default.Param_Export_Delimiter; // Temporary measure to prevent conflicts with byte array delimiters. Will be removed later. + } + + return _Param_Export_Delimiter; + } + set => _Param_Export_Delimiter = value; + } +} diff --git a/src/StudioCore/ParamEditor/ParamDefBank.cs b/src/StudioCore/ParamEditor/ParamDefBank.cs new file mode 100644 index 000000000..f36204d36 --- /dev/null +++ b/src/StudioCore/ParamEditor/ParamDefBank.cs @@ -0,0 +1,96 @@ +using SoulsFormats; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace StudioCore.ParamEditor; + +/// +/// Utilities for dealing with global paramdefs for a game +/// +public class ParamDefBank : StudioResource +{ + /// + /// Mapping from path -> PARAMDEF for cache and and comparison purposes. TODO: check for paramdef comparisons and evaluate if the file/paramdef was actually the same. + /// + private static readonly Dictionary _paramdefsCache = new(); + + /// + /// Mapping from ParamType -> PARAMDEF. + /// + private Dictionary _paramdefs = new(); + + private List<(string, PARAMDEF)> _defPairs = new(); + + /// + /// Mapping from Param filename -> Manual ParamType. + /// This is for params with no usable ParamType at some particular game version. + /// By convention, ParamTypes ending in "_TENTATIVE" do not have official data to reference. + /// + private Dictionary _tentativeParamType; + + public ParamDefBank() : base(Locator.ActiveProject.Type, "Paramdefs") + { + } + + public Dictionary GetParamDefs() + { + return _paramdefs; + } + + public List<(string, PARAMDEF)> GetParamDefByFileNames() + { + return _defPairs; + } + + public Dictionary GetTentativeParamTypes() + { + return _tentativeParamType; + } + + private void LoadParamdefs() + { + _paramdefs = new Dictionary(); + _tentativeParamType = new Dictionary(); + var files = Locator.ActiveProject.AssetLocator.GetAllProjectFiles($@"Paramdex\{AssetUtils.GetGameIDForDir(Locator.ActiveProject.Type)}\Defs", ["*.xml"], true, false); + List<(string, PARAMDEF)> defPairs = new(); + foreach (var f in files) + { + if (!_paramdefsCache.TryGetValue(f, out PARAMDEF pdef)) + { + pdef = PARAMDEF.XmlDeserialize(f, true); + } + _paramdefs.Add(pdef.ParamType, pdef); + defPairs.Add((f, pdef)); + } + + var tentativeMappingPath = Locator.ActiveProject.AssetLocator.GetProjectFilePath($@"{Locator.ActiveProject.AssetLocator.GetParamdexDir()}\Defs\TentativeParamType.csv"); + if (File.Exists(tentativeMappingPath)) + { + // No proper CSV library is used currently, and all CSV parsing is in the context of param files. + // If a CSV library is introduced in DSMapStudio, use it here. + foreach (var line in File.ReadAllLines(tentativeMappingPath).Skip(1)) + { + var parts = line.Split(','); + if (parts.Length != 2 || string.IsNullOrWhiteSpace(parts[0]) || string.IsNullOrWhiteSpace(parts[1])) + { + throw new FormatException($"Malformed line in {tentativeMappingPath}: {line}"); + } + + _tentativeParamType[parts[0]] = parts[1]; + } + } + + _defPairs = defPairs; + } + protected override void Load() + { + LoadParamdefs(); + } + + protected override IEnumerable GetDependencies(Project project) + { + return []; + } +} diff --git a/src/StudioCore/ParamEditor/ParamDiffBank.cs b/src/StudioCore/ParamEditor/ParamDiffBank.cs new file mode 100644 index 000000000..2e745c3c6 --- /dev/null +++ b/src/StudioCore/ParamEditor/ParamDiffBank.cs @@ -0,0 +1,266 @@ +using Andre.Formats; +using Microsoft.Extensions.Logging; +using SoulsFormats; +using StudioCore.Editor; +using StudioCore.Platform; +using StudioCore.TextEditor; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace StudioCore.ParamEditor; + +/// +/// Utilities for dealing with global paramdiffs for a game +/// +public class ParamDiffBank : DataBank +{ + + private Dictionary> _primaryDiffCache; //If param != primaryparam + private Dictionary> _vanillaDiffCache; //If param != vanillaparam + + public ParamDiffBank(Project project) : base(project, "ParamDiffs") + { + } + + public IReadOnlyDictionary> VanillaDiffCache + { + get + { + if (IsLoading) + { + return null; + } + + if (Project.ParentProject == null) + { + return null; + } + return _vanillaDiffCache; + } + } + + public IReadOnlyDictionary> PrimaryDiffCache + { + get + { + if (IsLoading) + { + return null; + } + + if (Project == Locator.ActiveProject) + { + return null; + } + return _primaryDiffCache; + } + } + + private void ClearParamDiffCaches() + { + _vanillaDiffCache = new Dictionary>(); + _primaryDiffCache = new Dictionary>(); + foreach (var param in Project.ParamBank.Params?.Keys) + { + _vanillaDiffCache.Add(param, new HashSet()); + _primaryDiffCache.Add(param, new HashSet()); + } + } + + public static void RefreshAllParamDiffCaches(bool checkAuxVanillaDiff) + { + Locator.ActiveProject.ParamDiffBank.RefreshParamDiffCaches(true); + foreach (KeyValuePair aux in ResDirectory.CurrentGame.AuxProjects) + { + aux.Value.ParamDiffBank.RefreshParamDiffCaches(checkAuxVanillaDiff); + } + + UICache.ClearCaches(); + } + + public void RefreshParamDiffCaches(bool checkVanillaDiff) + { + if (Project.ParentProject != null && checkVanillaDiff) + { + _vanillaDiffCache = GetParamDiff(Project.ParentProject.ParamBank); + } + + if (Project.ParentProject == null && Locator.ActiveProject.ParamDiffBank._vanillaDiffCache != null) + { + _primaryDiffCache = Locator.ActiveProject.ParamDiffBank._vanillaDiffCache; + } + else if (Project != Locator.ActiveProject) + { + _primaryDiffCache = GetParamDiff(Locator.ActiveProject.ParamBank); + } + + UICache.ClearCaches(); + } + + private Dictionary> GetParamDiff(ParamBank otherBank) + { + if (otherBank == null || otherBank.IsLoading) + { + return null; + } + + Dictionary> newCache = new(); + foreach (var param in Project.ParamBank.Params.Keys) + { + HashSet cache = new(); + newCache.Add(param, cache); + Param p = Project.ParamBank.Params[param]; + if (!otherBank.Params.ContainsKey(param)) + { + Console.WriteLine("Missing vanilla param " + param); + continue; + } + + Param.Row[] rows = Project.ParamBank.Params[param].Rows.OrderBy(r => r.ID).ToArray(); + Param.Row[] vrows = otherBank.Params[param].Rows.OrderBy(r => r.ID).ToArray(); + + var vanillaIndex = 0; + var lastID = -1; + ReadOnlySpan lastVanillaRows = default; + for (var i = 0; i < rows.Length; i++) + { + var ID = rows[i].ID; + if (ID == lastID) + { + RefreshParamRowDiffCache(rows[i], lastVanillaRows, cache); + } + else + { + lastID = ID; + while (vanillaIndex < vrows.Length && vrows[vanillaIndex].ID < ID) + { + vanillaIndex++; + } + + if (vanillaIndex >= vrows.Length) + { + RefreshParamRowDiffCache(rows[i], Span.Empty, cache); + } + else + { + var count = 0; + while (vanillaIndex + count < vrows.Length && vrows[vanillaIndex + count].ID == ID) + { + count++; + } + + lastVanillaRows = new ReadOnlySpan(vrows, vanillaIndex, count); + RefreshParamRowDiffCache(rows[i], lastVanillaRows, cache); + vanillaIndex += count; + } + } + } + } + + return newCache; + } + + private static void RefreshParamRowDiffCache(Param.Row row, ReadOnlySpan otherBankRows, + HashSet cache) + { + if (IsChanged(row, otherBankRows)) + { + cache.Add(row.ID); + } + else + { + cache.Remove(row.ID); + } + } + + public void RefreshParamRowDiffs(Param.Row row, string param) + { + if (param == null) + { + return; + } + + if (Project.ParentProject.ParamBank.Params.ContainsKey(param) && VanillaDiffCache != null && + VanillaDiffCache.ContainsKey(param)) + { + Param.Row[] otherBankRows = Project.ParentProject.ParamBank.Params[param].Rows.Where(cell => cell.ID == row.ID).ToArray(); + RefreshParamRowDiffCache(row, otherBankRows, VanillaDiffCache[param]); + } + + if (Project != Locator.ActiveProject) + { + return; + } + + foreach (Project aux in ResDirectory.CurrentGame.AuxProjects.Values) + { + if (!aux.ParamBank.Params.ContainsKey(param) || aux.ParamDiffBank.PrimaryDiffCache == null || + !aux.ParamDiffBank.PrimaryDiffCache.ContainsKey(param)) + { + continue; // Don't try for now + } + + Param.Row[] otherBankRows = aux.ParamBank.Params[param].Rows.Where(cell => cell.ID == row.ID).ToArray(); + RefreshParamRowDiffCache(row, otherBankRows, aux.ParamDiffBank.PrimaryDiffCache[param]); + } + } + + private static bool IsChanged(Param.Row row, ReadOnlySpan vanillaRows) + { + //List vanils = vanilla.Rows.Where(cell => cell.ID == row.ID).ToList(); + if (vanillaRows.Length == 0) + { + return true; + } + + foreach (Param.Row vrow in vanillaRows) + { + if (row.RowMatches(vrow)) + { + return false; //if we find a matching vanilla row + } + } + + return true; + } + private static readonly HashSet EMPTYSET = new(); + + public HashSet GetVanillaDiffRows(string param) + { + IReadOnlyDictionary> allDiffs = VanillaDiffCache; + if (allDiffs == null || !allDiffs.ContainsKey(param)) + { + return EMPTYSET; + } + + return allDiffs[param]; + } + + public HashSet GetPrimaryDiffRows(string param) + { + IReadOnlyDictionary> allDiffs = PrimaryDiffCache; + if (allDiffs == null || !allDiffs.ContainsKey(param)) + { + return EMPTYSET; + } + + return allDiffs[param]; + } + + public override void Save() + { + } + + protected override void Load() + { + RefreshParamDiffCaches(true); + UICache.ClearCaches(); + } + + protected override IEnumerable GetDependencies(Project project) + { + return [project.ParamBank, project.ParentProject.ParamBank]; + } +} diff --git a/src/StudioCore/ParamEditor/ParamEditorScreen.cs b/src/StudioCore/ParamEditor/ParamEditorScreen.cs index 5a727b97b..1c2b21165 100644 --- a/src/StudioCore/ParamEditor/ParamEditorScreen.cs +++ b/src/StudioCore/ParamEditor/ParamEditorScreen.cs @@ -226,6 +226,31 @@ public ParamEditorScreen(Sdl2Window window, GraphicsDevice device) public string CommandEndpoint => "param"; public string SaveType => "Params"; + /// + /// Param name - FMGCategory map + /// + public static readonly List<(string, FmgEntryCategory)> ParamToFmgCategoryList = new() + { + ("EquipParamAccessory", FmgEntryCategory.Rings), + ("EquipParamGoods", FmgEntryCategory.Goods), + ("EquipParamWeapon", FmgEntryCategory.Weapons), + ("EquipParamProtector", FmgEntryCategory.Armor), + ("Magic", FmgEntryCategory.Spells), + ("EquipParamGem", FmgEntryCategory.Gem), + ("SwordArtsParam", FmgEntryCategory.SwordArts), + ("EquipParamGenerator", FmgEntryCategory.Generator), + ("EquipParamFcs", FmgEntryCategory.FCS), + ("EquipParamBooster", FmgEntryCategory.Booster), + ("ArchiveParam", FmgEntryCategory.Archive), + ("MissionParam", FmgEntryCategory.Mission) + }; + public enum RowGetType + { + AllRows = 0, + ModifiedRows = 1, + SelectedRows = 2 + } + public void DrawEditorMenu() { // Menu Options @@ -328,20 +353,20 @@ public void DrawEditorMenu() ImGui.Separator(); if (ImGui.BeginMenu("All rows")) { - CsvExportDisplay(ParamBank.RowGetType.AllRows); + CsvExportDisplay(RowGetType.AllRows); ImGui.EndMenu(); } if (ImGui.BeginMenu("Modified rows", - ParamBank.PrimaryBank.GetVanillaDiffRows(_activeView._selection.GetActiveParam()).Any())) + Locator.ActiveProject.ParamDiffBank.GetVanillaDiffRows(_activeView._selection.GetActiveParam()).Any())) { - CsvExportDisplay(ParamBank.RowGetType.ModifiedRows); + CsvExportDisplay(RowGetType.ModifiedRows); ImGui.EndMenu(); } if (ImGui.BeginMenu("Selected rows", _activeView._selection.RowSelectionExists())) { - CsvExportDisplay(ParamBank.RowGetType.SelectedRows); + CsvExportDisplay(RowGetType.SelectedRows); ImGui.EndMenu(); } @@ -416,7 +441,7 @@ public void DrawEditorMenu() TaskManager.Run(new TaskManager.LiveTask("Param - Check Differences", TaskManager.RequeueType.Repeat, true, TaskLogs.LogPriority.Low, - () => ParamBank.RefreshAllParamDiffCaches(false))); + () => ParamDiffBank.RefreshAllParamDiffCaches(false))); } else { @@ -444,7 +469,7 @@ public void DrawEditorMenu() TaskManager.Run(new TaskManager.LiveTask("Param - Check Differences", TaskManager.RequeueType.Repeat, true, TaskLogs.LogPriority.Low, - () => ParamBank.RefreshAllParamDiffCaches(false))); + () => ParamDiffBank.RefreshAllParamDiffCaches(false))); } } @@ -473,7 +498,7 @@ public void DrawEditorMenu() TaskManager.Run(new TaskManager.LiveTask("Param - Check Differences", TaskManager.RequeueType.Repeat, true, TaskLogs.LogPriority.Low, - () => ParamBank.RefreshAllParamDiffCaches(false))); + () => ParamDiffBank.RefreshAllParamDiffCaches(false))); } } } @@ -571,9 +596,9 @@ void ImportRowNames(bool currentParamOnly, string title) ImGui.Separator(); if (ImGui.MenuItem("Check all params for edits", null, false, - !ParamBank.PrimaryBank.IsLoadingParams && !ParamBank.VanillaBank.IsLoadingParams)) + ParamBank.PrimaryBank.IsLoaded && ParamBank.VanillaBank.IsLoaded)) { - ParamBank.RefreshAllParamDiffCaches(true); + ParamDiffBank.RefreshAllParamDiffCaches(true); } ImGui.Separator(); @@ -717,14 +742,14 @@ void ImportRowNames(bool currentParamOnly, string title) } } - if (ImGui.BeginMenu("Clear param comparison...", ParamBank.AuxBanks.Count > 0)) + if (ImGui.BeginMenu("Clear param comparison...", ResDirectory.CurrentGame.AuxProjects.Count > 0)) { - for (var i = 0; i < ParamBank.AuxBanks.Count; i++) + for (var i = 0; i < ResDirectory.CurrentGame.AuxProjects.Count; i++) { - KeyValuePair pb = ParamBank.AuxBanks.ElementAt(i); + KeyValuePair pb = ResDirectory.CurrentGame.AuxProjects.ElementAt(i); if (ImGui.MenuItem(pb.Key)) { - ParamBank.AuxBanks.Remove(pb.Key); + ResDirectory.CurrentGame.AuxProjects.Remove(pb.Key); break; } } @@ -732,9 +757,10 @@ void ImportRowNames(bool currentParamOnly, string title) ImGui.EndMenu(); } - if (ImGui.MenuItem("Clear all param comparisons", null, false, ParamBank.AuxBanks.Count > 0)) + if (ImGui.MenuItem("Clear all param comparisons", null, false, ResDirectory.CurrentGame.AuxProjects.Count > 0)) { - ParamBank.AuxBanks = new Dictionary(); + // TODO look into whether it's sensible for parambank to control auxprojects like this + ResDirectory.CurrentGame.AuxProjects = new Dictionary(); } ImGui.EndMenu(); @@ -859,7 +885,7 @@ public unsafe void OnGUI(string[] initcmd) return; } - if (ParamBank.PrimaryBank.IsLoadingParams) + if (ParamBank.PrimaryBank.IsLoading) { ImGui.Text("Loading Params..."); return; @@ -871,7 +897,7 @@ public unsafe void OnGUI(string[] initcmd) return; } - if (!ParamBank.IsMetaLoaded) + if (!ResDirectory.CurrentGame.ParamMetaBank.IsLoaded) { ImGui.Text("Loading Meta..."); return; @@ -905,7 +931,7 @@ public unsafe void OnGUI(string[] initcmd) if (InputTracker.GetKeyDown(KeyBindings.Current.Param_ExportCSV)) { - EditorCommandQueue.AddCommand($@"param/menu/massEditCSVExport/{ParamBank.RowGetType.AllRows}"); + EditorCommandQueue.AddCommand($@"param/menu/massEditCSVExport/{RowGetType.AllRows}"); } // Parse commands @@ -986,7 +1012,7 @@ public unsafe void OnGUI(string[] initcmd) } else if (initcmd[1] == "massEditCSVExport") { - IReadOnlyList rows = CsvExportGetRows(Enum.Parse(initcmd[2])); + IReadOnlyList rows = CsvExportGetRows(Enum.Parse(initcmd[2])); _currentMEditCSVOutput = ParamIO.GenerateCSV(rows, ParamBank.PrimaryBank.Params[_activeView._selection.GetActiveParam()], CFG.Current.Param_Export_Delimiter[0]); @@ -999,7 +1025,7 @@ public unsafe void OnGUI(string[] initcmd) else if (initcmd[1] == "massEditSingleCSVExport") { _currentMEditSingleCSVField = initcmd[2]; - IReadOnlyList rows = CsvExportGetRows(Enum.Parse(initcmd[3])); + IReadOnlyList rows = CsvExportGetRows(Enum.Parse(initcmd[3])); _currentMEditCSVOutput = ParamIO.GenerateSingleCSV(rows, ParamBank.PrimaryBank.Params[_activeView._selection.GetActiveParam()], _currentMEditSingleCSVField, @@ -1125,7 +1151,7 @@ public void Save() { if (_projectSettings != null) { - ParamBank.PrimaryBank.SaveParams(_projectSettings.UseLooseParams); + ParamBank.PrimaryBank.Save(); TaskLogs.AddLog("Saved params"); } } @@ -1147,7 +1173,7 @@ public void SaveAll() { if (_projectSettings != null) { - ParamBank.PrimaryBank.SaveParams(_projectSettings.UseLooseParams); + ParamBank.PrimaryBank.Save(); TaskLogs.AddLog("Saved params"); } } @@ -1216,12 +1242,12 @@ private void LoadUpgraderData() private void ParamUpgradeDisplay() { - if (Locator.ActiveProject != null && Locator.ActiveProject.ParamBank.IsDefsLoaded + if (Locator.ActiveProject != null && ResDirectory.CurrentGame.ParamDefBank.IsLoaded && ParamBank.PrimaryBank.Params != null && ParamBank.VanillaBank.Params != null && ParamUpgrade_SupportedGames.Contains(Locator.AssetLocator.Type) - && !ParamBank.PrimaryBank.IsLoadingParams - && !ParamBank.VanillaBank.IsLoadingParams + && ParamBank.PrimaryBank.IsLoaded + && ParamBank.VanillaBank.IsLoaded && ParamBank.PrimaryBank.ParamVersion < ParamBank.VanillaBank.ParamVersion) { if (!_paramUpgraderLoaded) @@ -1395,7 +1421,7 @@ public void UpgradeRegulation(ParamBank bank, ParamBank vanillaBank, string oldR } UICache.ClearCaches(); - ParamBank.RefreshAllParamDiffCaches(false); + ParamDiffBank.RefreshAllParamDiffCaches(false); } @@ -1464,7 +1490,7 @@ private void ParamUndo() TaskManager.Run(new TaskManager.LiveTask("Param - Check Differences", TaskManager.RequeueType.Repeat, true, TaskLogs.LogPriority.Low, - () => ParamBank.RefreshAllParamDiffCaches(false))); + () => ParamDiffBank.RefreshAllParamDiffCaches(false))); } private void ParamRedo() @@ -1473,27 +1499,27 @@ private void ParamRedo() TaskManager.Run(new TaskManager.LiveTask("Param - Check Differences", TaskManager.RequeueType.Repeat, true, TaskLogs.LogPriority.Low, - () => ParamBank.RefreshAllParamDiffCaches(false))); + () => ParamDiffBank.RefreshAllParamDiffCaches(false))); } - private IReadOnlyList CsvExportGetRows(ParamBank.RowGetType rowType) + private IReadOnlyList CsvExportGetRows(RowGetType rowType) { IReadOnlyList rows; var activeParam = _activeView._selection.GetActiveParam(); - if (rowType == ParamBank.RowGetType.AllRows) + if (rowType == RowGetType.AllRows) { // All rows rows = ParamBank.PrimaryBank.Params[activeParam].Rows; } - else if (rowType == ParamBank.RowGetType.ModifiedRows) + else if (rowType == RowGetType.ModifiedRows) { // Modified rows - HashSet vanillaDiffCache = ParamBank.PrimaryBank.GetVanillaDiffRows(activeParam); + HashSet vanillaDiffCache = Locator.ActiveProject.ParamDiffBank.GetVanillaDiffRows(activeParam); rows = ParamBank.PrimaryBank.Params[activeParam].Rows.Where(p => vanillaDiffCache.Contains(p.ID)) .ToList(); } - else if (rowType == ParamBank.RowGetType.SelectedRows) + else if (rowType == RowGetType.SelectedRows) { // Selected rows rows = _activeView._selection.GetSelectedRows(); @@ -1509,7 +1535,7 @@ private void ParamRedo() /// /// CSV Export DIsplay /// - private void CsvExportDisplay(ParamBank.RowGetType rowType) + private void CsvExportDisplay(RowGetType rowType) { if (ImGui.BeginMenu("Export to window...")) { @@ -1725,7 +1751,7 @@ public void MassEditPopups() TaskManager.Run(new TaskManager.LiveTask("Param - Check Differences", TaskManager.RequeueType.Repeat, true, TaskLogs.LogPriority.Low, - () => ParamBank.RefreshAllParamDiffCaches(false))); + () => ParamDiffBank.RefreshAllParamDiffCaches(false))); } _mEditRegexResult = r.Information; @@ -1791,7 +1817,7 @@ public void MassEditPopups() TaskManager.Run(new TaskManager.LiveTask("Param - Check Differences", TaskManager.RequeueType.Repeat, true, TaskLogs.LogPriority.Low, - () => ParamBank.RefreshAllParamDiffCaches(false))); + () => ParamDiffBank.RefreshAllParamDiffCaches(false))); } _mEditCSVResult = result; @@ -2092,4 +2118,63 @@ private static string TryReadFile(string path) return null; } } + + IEnumerable EditorScreen.GetDependencies(Project project) + { + return [project.ParamBank, project.ParentProject.ParamBank, project.ParamDiffBank, ResDirectory.CurrentGame.ParamMetaBank]; + } + + public void SettingsMenu() + { + if (ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) + { + EditorDecorations.ShowHelpMarker("Reduces the line height within the the Param Editor screen."); + ImGui.Checkbox("Use compact param editor", ref CFG.Current.UI_CompactParams); + + EditorDecorations.ShowHelpMarker("Show additional options within the MassEdit context menu."); + ImGui.Checkbox("Show advanced massedit options", ref CFG.Current.Param_AdvancedMassedit); + + EditorDecorations.ShowHelpMarker("Show the shortcut tools in the right-click context menu."); + ImGui.Checkbox("Show shortcut tools in context menus", ref CFG.Current.Param_ShowHotkeysInContextMenu); + } + + if (ImGui.CollapsingHeader("Params")) + { + EditorDecorations.ShowHelpMarker("Sort the Param View list alphabetically."); + if (ImGui.Checkbox("Sort params alphabetically", ref CFG.Current.Param_AlphabeticalParams)) + { + UICache.ClearCaches(); + } + } + + if (ImGui.CollapsingHeader("Rows")) + { + EditorDecorations.ShowHelpMarker("Disable the row names from wrapping within the Row View list."); + ImGui.Checkbox("Disable line wrapping", ref CFG.Current.Param_DisableLineWrapping); + + EditorDecorations.ShowHelpMarker("Disable the grouping of connected rows in certain params, such as ItemLotParam within the Row View list."); + ImGui.Checkbox("Disable row grouping", ref CFG.Current.Param_DisableRowGrouping); + } + + if (ImGui.CollapsingHeader("Fields")) + { + EditorDecorations.ShowHelpMarker("Crowd-sourced names will appear before the canonical name in the Field View list."); + ImGui.Checkbox("Show community field names first", ref CFG.Current.Param_MakeMetaNamesPrimary); + + EditorDecorations.ShowHelpMarker("The crowd-sourced name (or the canonical name if the above option is enabled) will appear after the initial name in the Field View list."); + ImGui.Checkbox("Show secondary field names", ref CFG.Current.Param_ShowSecondaryNames); + + EditorDecorations.ShowHelpMarker("The field offset within the .PARAM file will be show to the left in the Field View List."); + ImGui.Checkbox("Show field data offsets", ref CFG.Current.Param_ShowFieldOffsets); + + EditorDecorations.ShowHelpMarker("Hide the generated param references for fields that link to other params."); + ImGui.Checkbox("Hide field references", ref CFG.Current.Param_HideReferenceRows); + + EditorDecorations.ShowHelpMarker("Hide the crowd-sourced namelist for index-based enum fields."); + ImGui.Checkbox("Hide field enums", ref CFG.Current.Param_HideEnums); + + EditorDecorations.ShowHelpMarker("Allow the field order to be changed by an alternative order as defined within the Paramdex META file."); + ImGui.Checkbox("Allow field reordering", ref CFG.Current.Param_AllowFieldReorder); + } + } } diff --git a/src/StudioCore/ParamEditor/ParamEditorSelectionState.cs b/src/StudioCore/ParamEditor/ParamEditorSelectionState.cs index 63051c364..70dd98757 100644 --- a/src/StudioCore/ParamEditor/ParamEditorSelectionState.cs +++ b/src/StudioCore/ParamEditor/ParamEditorSelectionState.cs @@ -181,9 +181,9 @@ public void SetActiveRow(Param.Row row, bool clearSelection, bool isHistory = fa if (_activeParam != null) { ParamEditorParamSelectionState s = _paramStates[_activeParam]; - if (s.activeRow != null && !ParamBank.VanillaBank.IsLoadingParams) + if (s.activeRow != null && ParamBank.VanillaBank.IsLoaded) { - ParamBank.PrimaryBank.RefreshParamRowDiffs(s.activeRow, _activeParam); + Locator.ActiveProject.ParamDiffBank.RefreshParamRowDiffs(s.activeRow, _activeParam); } if (!isHistory) @@ -194,9 +194,9 @@ public void SetActiveRow(Param.Row row, bool clearSelection, bool isHistory = fa s.activeRow = row; s.selectionRows.Clear(); s.selectionRows.Add(row); - if (s.activeRow != null && !ParamBank.VanillaBank.IsLoadingParams) + if (s.activeRow != null && ParamBank.VanillaBank.IsLoaded) { - ParamBank.PrimaryBank.RefreshParamRowDiffs(s.activeRow, _activeParam); + Locator.ActiveProject.ParamDiffBank.RefreshParamRowDiffs(s.activeRow, _activeParam); } s.selectionCacheDirty = true; diff --git a/src/StudioCore/ParamEditor/ParamEditorView.cs b/src/StudioCore/ParamEditor/ParamEditorView.cs index 41f3d559b..8f5a3fd6b 100644 --- a/src/StudioCore/ParamEditor/ParamEditorView.cs +++ b/src/StudioCore/ParamEditor/ParamEditorView.cs @@ -132,7 +132,7 @@ private void ParamView_ParamList_Pinned(float scale) //ImGui.Text(" Pinned Params"); foreach (var paramKey in pinnedParamKeyList) { - HashSet primary = ParamBank.PrimaryBank.VanillaDiffCache.GetValueOrDefault(paramKey, null); + HashSet primary = Locator.ActiveProject.ParamDiffBank.VanillaDiffCache.GetValueOrDefault(paramKey, null); Param p = ParamBank.PrimaryBank.Params[paramKey]; if (p != null) { @@ -173,6 +173,28 @@ private void ParamView_ParamList_Pinned(float scale) } } + /// + /// Map related params. + /// + public static readonly List DS2MapParamlist = new() + { + "demopointlight", + "demospotlight", + "eventlocation", + "eventparam", + "GeneralLocationEventParam", + "generatorparam", + "generatorregistparam", + "generatorlocation", + "generatordbglocation", + "hitgroupparam", + "intrudepointparam", + "mapobjectinstanceparam", + "maptargetdirparam", + "npctalkparam", + "treasureboxparam" + }; + private void ParamView_ParamList_Main(bool doFocus, float scale, float scrollTo) { List paramKeyList = UICache.GetCached(_paramEditor, _viewIndex, () => @@ -199,11 +221,11 @@ or GameType.DarkSoulsPTDE { if (_mapParamView) { - keyList = keyList.FindAll(p => ParamBank.DS2MapParamlist.Contains(p.Split('_')[0])); + keyList = keyList.FindAll(p => DS2MapParamlist.Contains(p.Split('_')[0])); } else { - keyList = keyList.FindAll(p => !ParamBank.DS2MapParamlist.Contains(p.Split('_')[0])); + keyList = keyList.FindAll(p => !DS2MapParamlist.Contains(p.Split('_')[0])); } } else if (Locator.AssetLocator.Type is GameType.EldenRing) @@ -257,7 +279,7 @@ or GameType.DarkSoulsPTDE foreach (var paramKey in paramKeyList) { - HashSet primary = ParamBank.PrimaryBank.VanillaDiffCache.GetValueOrDefault(paramKey, null); + HashSet primary = Locator.ActiveProject.ParamDiffBank.VanillaDiffCache?.GetValueOrDefault(paramKey, null); Param p = ParamBank.PrimaryBank.Params[paramKey]; if (p != null) { @@ -417,9 +439,9 @@ private void ParamView_RowList(bool doFocus, bool isActiveView, float scrollTo, ParamView_RowList_Header(ref doFocus, isActiveView, ref scrollTo, activeParam); Param para = ParamBank.PrimaryBank.Params[activeParam]; - HashSet vanillaDiffCache = ParamBank.PrimaryBank.GetVanillaDiffRows(activeParam); - List<(HashSet, HashSet)> auxDiffCaches = ParamBank.AuxBanks.Select((bank, i) => - (bank.Value.GetVanillaDiffRows(activeParam), bank.Value.GetPrimaryDiffRows(activeParam))).ToList(); + HashSet vanillaDiffCache = Locator.ActiveProject.ParamDiffBank.GetVanillaDiffRows(activeParam); + List<(HashSet, HashSet)> auxDiffCaches = ResDirectory.CurrentGame.AuxProjects.Select((project, i) => + (project.Value.ParamDiffBank.GetVanillaDiffRows(activeParam), project.Value.ParamDiffBank.GetPrimaryDiffRows(activeParam))).ToList(); Param.Column compareCol = _selection.GetCompareCol(); PropertyInfo compareColProp = typeof(Param.Cell).GetProperty("Value"); @@ -559,8 +581,8 @@ private void ParamView_FieldList(bool isActiveView, string activeParam, Param.Ro ParamBank.PrimaryBank, activeRow, vanillaParam?[activeRow.ID], - ParamBank.AuxBanks.Select((bank, i) => - (bank.Key, bank.Value.Params?.GetValueOrDefault(activeParam)?[activeRow.ID])).ToList(), + ResDirectory.CurrentGame.AuxProjects.Select((project, i) => + (project.Key, project.Value.ParamBank.Params?.GetValueOrDefault(activeParam)?[activeRow.ID])).ToList(), _selection.GetCompareRow(), ref _selection.GetCurrentPropSearchString(), activeParam, @@ -835,9 +857,9 @@ private bool ParamView_RowList_Entry(bool[] selectionCache, int selectionCacheIn ImGui.PushStyleVarVec2(ImGuiStyleVar.FramePadding, new Vector2(0, 0)); ParamEditorCommon.PropertyField(compareCol.ValueType, c.Value, ref newval, false); if (ParamEditorCommon.UpdateProperty(_propEditor.ContextActionManager, c, compareColProp, - c.Value) && !ParamBank.VanillaBank.IsLoadingParams) + c.Value) && ParamBank.VanillaBank.IsLoaded) { - ParamBank.PrimaryBank.RefreshParamRowDiffs(r, activeParam); + Locator.ActiveProject.ParamDiffBank.RefreshParamRowDiffs(r, activeParam); } ImGui.PopStyleVar(1); diff --git a/src/StudioCore/ParamEditor/ParamMeta.cs b/src/StudioCore/ParamEditor/ParamMeta.cs index 363597b4f..cfc4fc332 100644 --- a/src/StudioCore/ParamEditor/ParamMeta.cs +++ b/src/StudioCore/ParamEditor/ParamMeta.cs @@ -213,12 +213,12 @@ private ParamMetaData(XmlDocument xml, string path, PARAMDEF def) public static ParamMetaData Get(PARAMDEF def) { - if (!ParamBank.IsMetaLoaded) + if (!ResDirectory.CurrentGame.ParamMetaBank.IsLoaded) { return null; } - return Locator.ActiveProject.ParamBank.ParamMetas[def]; + return ResDirectory.CurrentGame.ParamMetaBank.ParamMetas[def]; } internal static XmlNode GetXmlNode(XmlDocument xml, XmlNode parent, string child) { @@ -372,7 +372,7 @@ public static void SaveAll() field.Value.Commit(FixName(field.Key.InternalName)); //does not handle shared names } - foreach (ParamMetaData param in Locator.ActiveProject.ParamBank.ParamMetas.Values) + foreach (ParamMetaData param in ResDirectory.CurrentGame.ParamMetaBank.ParamMetas.Values) { param.Commit(); param.Save(); @@ -519,7 +519,7 @@ public FieldMetaData(ParamMetaData parent, XmlNode fieldMeta, PARAMDEF.Field fie public static FieldMetaData Get(PARAMDEF.Field def) { - if (!ParamBank.IsMetaLoaded) + if (!ResDirectory.CurrentGame.ParamMetaBank.IsLoaded) { return null; } diff --git a/src/StudioCore/ParamEditor/ParamMetaBank.cs b/src/StudioCore/ParamEditor/ParamMetaBank.cs new file mode 100644 index 000000000..d28a0741c --- /dev/null +++ b/src/StudioCore/ParamEditor/ParamMetaBank.cs @@ -0,0 +1,36 @@ +using SoulsFormats; +using System.Collections.Generic; + +namespace StudioCore.ParamEditor; + +/// +/// Utilities for dealing with global paramdefs for a game +/// +public class ParamMetaBank : StudioResource +{ + public Dictionary ParamMetas = new(); + + public ParamMetaBank() : base(Locator.ActiveProject.Type, "ParamMetas") + { + } + public void LoadParamMeta() + { + List<(string, PARAMDEF)> defPairs = ResDirectory.CurrentGame.ParamDefBank.GetParamDefByFileNames(); + var mdir = Locator.ActiveProject.AssetLocator.GetProjectFilePath($@"{Locator.ActiveProject.AssetLocator.GetParamdexDir()}\Meta"); + foreach ((var f, PARAMDEF pdef) in defPairs) + { + var fName = f.Substring(f.LastIndexOf('\\') + 1); + var md = ParamMetaData.XmlDeserialize($@"{mdir}\{fName}", pdef); + ParamMetas.Add(pdef, md); + } + } + protected override void Load() + { + LoadParamMeta(); + } + + protected override IEnumerable GetDependencies(Project project) + { + return [ResDirectory.CurrentGame.ParamDefBank]; + } +} diff --git a/src/StudioCore/ParamEditor/ParamReloader.cs b/src/StudioCore/ParamEditor/ParamReloader.cs index 7e3164150..d7d82ecb8 100644 --- a/src/StudioCore/ParamEditor/ParamReloader.cs +++ b/src/StudioCore/ParamEditor/ParamReloader.cs @@ -40,7 +40,7 @@ public static bool GameIsSupported(GameType gameType) public static bool CanReloadMemoryParams(ParamBank bank, ProjectSettings projectSettings) { - if (projectSettings != null && GameIsSupported(projectSettings.GameType) && bank.IsLoadingParams == false) + if (projectSettings != null && GameIsSupported(projectSettings.GameType) && !bank.IsLoading) { return true; } diff --git a/src/StudioCore/ParamEditor/ParamRowEditor.cs b/src/StudioCore/ParamEditor/ParamRowEditor.cs index d666a50b1..6ca5332b0 100644 --- a/src/StudioCore/ParamEditor/ParamRowEditor.cs +++ b/src/StudioCore/ParamEditor/ParamRowEditor.cs @@ -212,7 +212,7 @@ public void PropEditorParamRow(ParamBank bank, Param.Row row, Param.Row vrow, Li List> auxCols = UICache.GetCached(_paramEditor, auxRows, "auxFieldFilter", () => auxRows.Select((r, i) => - cols.Select((c, j) => c.GetAs(ParamBank.AuxBanks[r.Item1].GetParamFromName(activeParam))) + cols.Select((c, j) => c.GetAs(ResDirectory.CurrentGame.AuxProjects[r.Item1].ParamBank.GetParamFromName(activeParam))) .ToList()).ToList()); if (pinnedFields?.Count > 0) @@ -500,9 +500,9 @@ private void PropEditorPropRow(ParamBank bank, object oldval, object compareval, var committed = ParamEditorCommon.UpdateProperty(ContextActionManager, nullableCell != null ? nullableCell : row, proprow, oldval); - if (committed && !ParamBank.VanillaBank.IsLoadingParams) + if (committed && ParamBank.VanillaBank.IsLoaded) { - ParamBank.PrimaryBank.RefreshParamRowDiffs(row, activeParam); + Locator.ActiveProject.ParamDiffBank.RefreshParamRowDiffs(row, activeParam); } ImGui.PopID(); diff --git a/src/StudioCore/ParamEditor/SearchEngine.cs b/src/StudioCore/ParamEditor/SearchEngine.cs index 184a1cde5..0fd5e83ae 100644 --- a/src/StudioCore/ParamEditor/SearchEngine.cs +++ b/src/StudioCore/ParamEditor/SearchEngine.cs @@ -281,7 +281,7 @@ internal class ParamSearchEngine : SearchEngine internal override void Setup() { unpacker = dummy => - ParamBank.AuxBanks.Select((aux, i) => aux.Value.Params.Select((x, i) => (aux.Value, x.Value))) + ResDirectory.CurrentGame.AuxProjects.Select((aux, i) => aux.Value.ParamBank.Params.Select((x, i) => (aux.Value.ParamBank, x.Value))) .Aggregate(bank.Params.Values.Select((x, i) => (bank, x)), (o, n) => o.Concat(n)).ToList(); filterList.Add("modified", newCmd(new string[0], "Selects params where any rows do not match the vanilla version, or where any are added. Ignores row names", @@ -292,7 +292,7 @@ internal override void Setup() return false; } - HashSet cache = bank.GetVanillaDiffRows(bank.GetKeyForParam(param.Item2)); + HashSet cache = bank.Project.ParamDiffBank.GetVanillaDiffRows(bank.GetKeyForParam(param.Item2)); return cache.Count > 0; })))); filterList.Add("param", newCmd(new[] { "param name (regex)" }, @@ -310,7 +310,7 @@ internal override void Setup() "Selects params from the specified regulation or parambnd where the param name matches the given regex", (args, lenient) => { - ParamBank auxBank = ParamBank.AuxBanks[args[0]]; + ParamBank auxBank = ResDirectory.CurrentGame.AuxProjects[args[0]].ParamBank; Regex rx = lenient ? new Regex(args[1], RegexOptions.IgnoreCase) : new Regex($@"^{args[1]}$"); return noContext(param => param.Item1 != auxBank @@ -318,7 +318,7 @@ internal override void Setup() : rx.IsMatch(auxBank.GetKeyForParam(param.Item2) == null ? "" : auxBank.GetKeyForParam(param.Item2))); - }, () => ParamBank.AuxBanks.Count > 0 && CFG.Current.Param_AdvancedMassedit)); + }, () => ResDirectory.CurrentGame.AuxProjects.Count > 0 && CFG.Current.Param_AdvancedMassedit)); defaultFilter = newCmd(new[] { "param name (regex)" }, "Selects all params whose name matches the given regex", (args, lenient) => { @@ -345,7 +345,7 @@ internal override void Setup() "Selects rows which do not match the vanilla version, or are added. Ignores row name", noArgs(context => { var paramName = context.Item1.GetKeyForParam(context.Item2); - HashSet cache = context.Item1.GetVanillaDiffRows(paramName); + HashSet cache = context.Item1.Project.ParamDiffBank.GetVanillaDiffRows(paramName); return row => cache.Contains(row.ID); } ))); @@ -372,27 +372,27 @@ internal override void Setup() return row => true; } - HashSet pCache = ParamBank.PrimaryBank.GetVanillaDiffRows(paramName); - List<(HashSet, HashSet)> auxCaches = ParamBank.AuxBanks.Select(x => - (x.Value.GetPrimaryDiffRows(paramName), x.Value.GetVanillaDiffRows(paramName))).ToList(); + HashSet pCache = Locator.ActiveProject.ParamDiffBank.GetVanillaDiffRows(paramName); + List<(HashSet, HashSet)> auxCaches = ResDirectory.CurrentGame.AuxProjects.Select(x => + (x.Value.ParamDiffBank.GetPrimaryDiffRows(paramName), x.Value.ParamDiffBank.GetVanillaDiffRows(paramName))).ToList(); return row => !pCache.Contains(row.ID) && auxCaches.Where(x => x.Item2.Contains(row.ID) && x.Item1.Contains(row.ID)).Count() == 1; } - ), () => ParamBank.AuxBanks.Count > 0)); + ), () => ResDirectory.CurrentGame.AuxProjects.Count > 0)); filterList.Add("conflicts", newCmd(new string[0], "Selects rows which, among all equivalents in the primary and additional regulations or parambnds, there is more than row 1 which is modified", noArgs(context => { var paramName = context.Item1.GetKeyForParam(context.Item2); - HashSet pCache = ParamBank.PrimaryBank.GetVanillaDiffRows(paramName); - List<(HashSet, HashSet)> auxCaches = ParamBank.AuxBanks.Select(x => - (x.Value.GetPrimaryDiffRows(paramName), x.Value.GetVanillaDiffRows(paramName))).ToList(); + HashSet pCache = Locator.ActiveProject.ParamDiffBank.GetVanillaDiffRows(paramName); + List<(HashSet, HashSet)> auxCaches = ResDirectory.CurrentGame.AuxProjects.Select(x => + (x.Value.ParamDiffBank.GetPrimaryDiffRows(paramName), x.Value.ParamDiffBank.GetVanillaDiffRows(paramName))).ToList(); return row => (pCache.Contains(row.ID) ? 1 : 0) + auxCaches .Where(x => x.Item2.Contains(row.ID) && x.Item1.Contains(row.ID)).Count() > 1; } - ), () => ParamBank.AuxBanks.Count > 0)); + ), () => ResDirectory.CurrentGame.AuxProjects.Count > 0)); filterList.Add("id", newCmd(new[] { "row id (regex)" }, "Selects rows whose ID matches the given regex", (args, lenient) => { @@ -608,15 +608,15 @@ internal override void Setup() { Regex rx = lenient ? new Regex(args[2], RegexOptions.IgnoreCase) : new Regex($@"^{args[2]}$"); var field = args[1]; - ParamBank bank; - if (!ParamBank.AuxBanks.TryGetValue(args[0], out bank)) + Project proj; + if (!ResDirectory.CurrentGame.AuxProjects.TryGetValue(args[0], out proj)) { throw new Exception("Unable to find auxbank " + args[0]); } return param => { - Param vparam = bank.GetParamFromName(param.Item1.GetKeyForParam(param.Item2)); + Param vparam = proj.ParamBank.GetParamFromName(param.Item1.GetKeyForParam(param.Item2)); return row => { Param.Row vrow = vparam[row.ID]; @@ -636,7 +636,7 @@ internal override void Setup() return rx.IsMatch(term); }; }; - }, () => ParamBank.AuxBanks.Count > 0 && CFG.Current.Param_AdvancedMassedit)); + }, () => ResDirectory.CurrentGame.AuxProjects.Count > 0 && CFG.Current.Param_AdvancedMassedit)); filterList.Add("auxproprange", newCmd( new[] { @@ -649,15 +649,15 @@ internal override void Setup() var field = args[0]; var floor = double.Parse(args[1]); var ceil = double.Parse(args[2]); - ParamBank bank; - if (!ParamBank.AuxBanks.TryGetValue(args[0], out bank)) + Project proj; + if (!ResDirectory.CurrentGame.AuxProjects.TryGetValue(args[0], out proj)) { throw new Exception("Unable to find auxbank " + args[0]); } return param => { - Param vparam = bank.GetParamFromName(param.Item1.GetKeyForParam(param.Item2)); + Param vparam = proj.ParamBank.GetParamFromName(param.Item1.GetKeyForParam(param.Item2)); return row => { Param.Row vrow = vparam[row.ID]; @@ -670,7 +670,7 @@ internal override void Setup() return Convert.ToDouble(c.Value.Value) >= floor && Convert.ToDouble(c.Value.Value) <= ceil; }; }; - }, () => ParamBank.AuxBanks.Count > 0 && CFG.Current.Param_AdvancedMassedit)); + }, () => ResDirectory.CurrentGame.AuxProjects.Count > 0 && CFG.Current.Param_AdvancedMassedit)); filterList.Add("semijoin", newCmd( new[] @@ -870,12 +870,12 @@ internal override void Setup() "Selects cells/fields where the equivalent cell in the specified regulation or parambnd has a different value", (args, lenient) => { - if (!ParamBank.AuxBanks.ContainsKey(args[0])) + if (!ResDirectory.CurrentGame.AuxProjects.ContainsKey(args[0])) { throw new Exception("Can't check if cell is modified - parambank not found"); } - ParamBank bank = ParamBank.AuxBanks[args[0]]; + Project proj = ResDirectory.CurrentGame.AuxProjects[args[0]]; return row => { if (row.Item1 == null) @@ -883,7 +883,7 @@ internal override void Setup() throw new Exception("Can't check if cell is modified - not part of a param"); } - Param auxParam = bank.Params?[row.Item1]; + Param auxParam = proj?.ParamBank.Params?[row.Item1]; if (auxParam == null) { throw new Exception("Can't check if cell is modified - no aux param"); @@ -916,7 +916,7 @@ internal override void Setup() return ParamUtils.IsValueDiff(ref valA, ref valB, col.GetColumnType()); }; }; - }, () => ParamBank.AuxBanks.Count > 0)); + }, () => ResDirectory.CurrentGame.AuxProjects.Count > 0)); filterList.Add("sftype", newCmd(new[] { "paramdef type" }, "Selects cells/fields where the field's data type, as enumerated by soulsformats, matches the given regex", (args, lenient) => diff --git a/src/StudioCore/Project.cs b/src/StudioCore/Project.cs index a5660aa9f..15421c8ec 100644 --- a/src/StudioCore/Project.cs +++ b/src/StudioCore/Project.cs @@ -21,6 +21,7 @@ public class Project public readonly ProjectAssetLocator AssetLocator; public readonly ParamBank ParamBank; + public readonly ParamDiffBank ParamDiffBank; public readonly FMGBank FMGBank; @@ -32,11 +33,12 @@ public class Project /// public Project(ProjectSettings settings) { - Settings = settings; + Settings = settings.CopyForGameDir(); AssetLocator = new(this, settings.GameRoot); ParentProject = null; ParamBank = new(this); + ParamDiffBank = new(this); FMGBank = new(this); } /// @@ -49,6 +51,7 @@ public Project(ProjectSettings settings, string moddir) ParentProject = new Project(settings); ParamBank = new(this); + ParamDiffBank = new(this); FMGBank = new(this); } /// @@ -68,6 +71,7 @@ public Project(string moddir, Project parent, ProjectSettings settings = null) ParentProject = parent; ParamBank = new(this); + ParamDiffBank = new(this); FMGBank = new(this); } @@ -86,6 +90,7 @@ public Project(Project parent) } ParamBank = parent.ParamBank; + ParamDiffBank = parent.ParamDiffBank; FMGBank = parent.FMGBank; } diff --git a/src/StudioCore/ResDirectory.cs b/src/StudioCore/ResDirectory.cs new file mode 100644 index 000000000..4a1cb8c4d --- /dev/null +++ b/src/StudioCore/ResDirectory.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Identity.Data; +using SoulsFormats; +using StudioCore.Editor; +using StudioCore.ParamEditor; +using StudioCore.TextEditor; +using System; +using System.Globalization; +using System.IO; +using System.Linq; + +namespace StudioCore; + +/// +/// Static data not tied to a project, but perhaps tied to a loaded game. +/// +public class ResDirectory +{ + public static ResDirectory CurrentGame { get; set; } = new(); + + public Dictionary AuxProjects = new(); + + public ParamDefBank ParamDefBank = new(); + public ParamMetaBank ParamMetaBank = new(); + +} diff --git a/src/StudioCore/SettingsMenu.cs b/src/StudioCore/SettingsMenu.cs index c741aa355..4d6a129f8 100644 --- a/src/StudioCore/SettingsMenu.cs +++ b/src/StudioCore/SettingsMenu.cs @@ -12,8 +12,10 @@ using System.Numerics; using System.Reflection; using Veldrid; -using StudioCore.Interface; +using StudioCore.Editor; using System.Globalization; +using System.Collections; +using System.Collections.Generic; namespace StudioCore; @@ -44,26 +46,14 @@ private void DisplaySettings_System() { if (ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("When enabled DSMS will automatically check for new versions upon program start."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("When enabled DSMS will automatically check for new versions upon program start."); ImGui.Checkbox("Check for new versions of DSMapStudio during startup", ref CFG.Current.EnableCheckProgramUpdate); - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("This is a tooltip."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("This is a tooltip."); ImGui.Checkbox("Show UI tooltips", ref CFG.Current.ShowUITooltips); - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Adjusts the scale of the user interface throughout all of DSMS."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("Adjusts the scale of the user interface throughout all of DSMS."); ImGui.SliderFloat("UI scale", ref _tempUiScale, 0.5f, 4.0f); if (ImGui.IsItemDeactivatedAfterEdit()) { @@ -81,11 +71,7 @@ private void DisplaySettings_System() MapStudioNew.UIScaleChanged?.Invoke(null, EventArgs.Empty); } - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Multiplies the user interface scale by your monitor's DPI setting."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("Multiplies the user interface scale by your monitor's DPI setting."); ImGui.Checkbox($"Multiply UI scale by DPI ({(MapStudioNew.Dpi / 96).ToString("P0", new NumberFormatInfo { PercentPositivePattern = 1, PercentNegativePattern = 1 })})", ref CFG.Current.UIScaleByDPI); if (ImGui.IsItemDeactivatedAfterEdit()) { @@ -106,51 +92,31 @@ private void DisplaySettings_System() // Additional Language Fonts if (ImGui.CollapsingHeader("Additional Language Fonts")) { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Include Chinese font.\nAdditional fonts take more VRAM and increase startup time."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("Include Chinese font.\nAdditional fonts take more VRAM and increase startup time."); if (ImGui.Checkbox("Chinese", ref CFG.Current.FontChinese)) { MapStudioNew.FontRebuildRequest = true; } - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Include Korean font.\nAdditional fonts take more VRAM and increase startup time."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("Include Korean font.\nAdditional fonts take more VRAM and increase startup time."); if (ImGui.Checkbox("Korean", ref CFG.Current.FontKorean)) { MapStudioNew.FontRebuildRequest = true; } - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Include Thai font.\nAdditional fonts take more VRAM and increase startup time."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("Include Thai font.\nAdditional fonts take more VRAM and increase startup time."); if (ImGui.Checkbox("Thai", ref CFG.Current.FontThai)) { MapStudioNew.FontRebuildRequest = true; } - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Include Vietnamese font.\nAdditional fonts take more VRAM and increase startup time."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("Include Vietnamese font.\nAdditional fonts take more VRAM and increase startup time."); if (ImGui.Checkbox("Vietnamese", ref CFG.Current.FontVietnamese)) { MapStudioNew.FontRebuildRequest = true; } - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Include Cyrillic font.\nAdditional fonts take more VRAM and increase startup time."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("Include Cyrillic font.\nAdditional fonts take more VRAM and increase startup time."); if (ImGui.Checkbox("Cyrillic", ref CFG.Current.FontCyrillic)) { MapStudioNew.FontRebuildRequest = true; @@ -160,7 +126,7 @@ private void DisplaySettings_System() if (ImGui.CollapsingHeader("Resources", ImGuiTreeNodeFlags.DefaultOpen)) { ImGui.Checkbox("Alias Banks - Editor Mode", ref CFG.Current.AliasBank_EditorMode); - ImguiUtils.ShowHelpMarker("If enabled, editing the name and tags for alias banks will commit the changes to the DSMS base version instead of the mod-specific version."); + Editor.EditorDecorations.ShowHelpMarker("If enabled, editing the name and tags for alias banks will commit the changes to the DSMS base version instead of the mod-specific version."); } if (ImGui.CollapsingHeader("Project", ImGuiTreeNodeFlags.DefaultOpen)) @@ -169,7 +135,7 @@ private void DisplaySettings_System() { if (CFG.Current.ShowUITooltips) { - ShowHelpMarker("No project has been loaded yet."); + EditorDecorations.ShowHelpMarker("No project has been loaded yet."); ImGui.SameLine(); } ImGui.Text("No project loaded"); @@ -178,20 +144,12 @@ private void DisplaySettings_System() { if (TaskManager.AnyActiveTasks()) { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("DSMS must finished all program tasks before it can load a project."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("DSMS must finished all program tasks before it can load a project."); ImGui.Text("Waiting for program tasks to finish..."); } else { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("This is the currently loaded project."); - ImGui.SameLine(); - } + EditorDecorations.ShowHelpMarker("This is the currently loaded project."); ImGui.Text($@"Project: {ProjSettings.ProjectName}"); if (ImGui.Button("Open project settings file")) @@ -203,12 +161,7 @@ private void DisplaySettings_System() var useLoose = ProjSettings.UseLooseParams; if (ProjSettings.GameType is GameType.DarkSoulsIISOTFS or GameType.DarkSoulsIII) { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Loose params means the .PARAM files will be saved outside of the regulation.bin file.\n\nFor Dark Souls II: Scholar of the First Sin, it is recommended that you enable this if add any additional rows."); - ImGui.SameLine(); - } - + EditorDecorations.ShowHelpMarker("Loose params means the .PARAM files will be saved outside of the regulation.bin file.\n\nFor Dark Souls II: Scholar of the First Sin, it is recommended that you enable this if add any additional rows."); if (ImGui.Checkbox("Use loose params", ref useLoose)) { ProjSettings.UseLooseParams = useLoose; @@ -222,621 +175,6 @@ private void DisplaySettings_System() } } - private unsafe void DisplaySettings_MapEditor() - { - if (ImGui.BeginTabItem("Map Editor")) - { - if (ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Viewport FPS when window is focused."); - ImGui.SameLine(); - } - ImGui.DragFloat("Frame Limit", ref CFG.Current.GFX_Framerate_Limit, 1.0f, 5.0f, 300.0f); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Viewport FPS when window is not focused."); - ImGui.SameLine(); - } - ImGui.DragFloat("Frame Limit (Unfocused)", ref CFG.Current.GFX_Framerate_Limit_Unfocused, 1.0f, 1.0f, 60.0f); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Enabling this option will allow DSMS to render the textures of models within the viewport.\n\nNote, this feature is in an alpha state."); - ImGui.SameLine(); - } - ImGui.Checkbox("Enable texturing", ref CFG.Current.EnableTexturing); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("This option will cause loaded maps to always be visible within the map list, ignoring the search filter."); - ImGui.SameLine(); - } - ImGui.Checkbox("Exclude loaded maps from search filter", ref CFG.Current.Map_AlwaysListLoadedMaps); - - if (ProjSettings.GameType is GameType.EldenRing) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker(""); - ImGui.SameLine(); - } - ImGui.Checkbox("Enable Elden Ring auto map offset", ref CFG.Current.EnableEldenRingAutoMapOffset); - } - } - - // Scene View - // Scene View - if (ImGui.CollapsingHeader("Map Object List")) - { - ImGui.Checkbox("Display map names", ref CFG.Current.MapEditor_MapObjectList_ShowMapNames); - ImguiUtils.ShowHoverTooltip("Map names will be displayed within the scene view list."); - - ImGui.Checkbox("Display character names", ref CFG.Current.MapEditor_MapObjectList_ShowCharacterNames); - ImguiUtils.ShowHoverTooltip("Characters names will be displayed within the scene view list."); - - ImGui.Checkbox("Display asset names", ref CFG.Current.MapEditor_MapObjectList_ShowAssetNames); - ImguiUtils.ShowHoverTooltip("Asset/object names will be displayed within the scene view list."); - - ImGui.Checkbox("Display map piece names", ref CFG.Current.MapEditor_MapObjectList_ShowMapPieceNames); - ImguiUtils.ShowHoverTooltip("Map piece names will be displayed within the scene view list."); - - ImGui.Checkbox("Display treasure names", ref CFG.Current.MapEditor_MapObjectList_ShowTreasureNames); - ImguiUtils.ShowHoverTooltip("Treasure itemlot names will be displayed within the scene view list."); - } - - if (ImGui.CollapsingHeader("Selection")) - { - var arbitrary_rotation_x = CFG.Current.Map_ArbitraryRotation_X_Shift; - var arbitrary_rotation_y = CFG.Current.Map_ArbitraryRotation_Y_Shift; - var camera_radius_offset = CFG.Current.Map_MoveSelectionToCamera_Radius; - - ImGui.Checkbox("Enable selection outline", ref CFG.Current.Viewport_Enable_Selection_Outline); - ImguiUtils.ShowHoverTooltip("Enable the selection outline around map entities."); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Set the angle increment amount used by Arbitary Rotation in the X-axis."); - ImGui.SameLine(); - } - if (ImGui.InputFloat("Rotation increment degrees: Roll", ref arbitrary_rotation_x)) - { - CFG.Current.Map_ArbitraryRotation_X_Shift = Math.Clamp(arbitrary_rotation_x, -180.0f, 180.0f); - } - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Set the angle increment amount used by Arbitary Rotation in the Y-axis."); - ImGui.SameLine(); - } - if (ImGui.InputFloat("Rotation increment degrees: Yaw", ref arbitrary_rotation_y)) - { - CFG.Current.Map_ArbitraryRotation_Y_Shift = Math.Clamp(arbitrary_rotation_y, -180.0f, 180.0f); - ; - } - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Set the distance at which the current select is offset from the camera when using the Move Selection to Camera action."); - ImGui.SameLine(); - } - if (ImGui.DragFloat("Move selection to camera (offset distance)", ref camera_radius_offset)) - { - CFG.Current.Map_MoveSelectionToCamera_Radius = camera_radius_offset; - } - } - - if (ImGui.CollapsingHeader("Camera")) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Resets all of the values within this section to their default values."); - ImGui.SameLine(); - } - if (ImGui.Button("Reset##ViewportCamera")) - { - CFG.Current.GFX_Camera_Sensitivity = CFG.Default.GFX_Camera_Sensitivity; - - CFG.Current.GFX_Camera_FOV = CFG.Default.GFX_Camera_FOV; - - CFG.Current.GFX_RenderDistance_Max = CFG.Default.GFX_RenderDistance_Max; - - MsbEditor.Viewport.WorldView.CameraMoveSpeed_Slow = CFG.Default.GFX_Camera_MoveSpeed_Slow; - CFG.Current.GFX_Camera_MoveSpeed_Slow = MsbEditor.Viewport.WorldView.CameraMoveSpeed_Slow; - - MsbEditor.Viewport.WorldView.CameraMoveSpeed_Normal = CFG.Default.GFX_Camera_MoveSpeed_Normal; - CFG.Current.GFX_Camera_MoveSpeed_Normal = MsbEditor.Viewport.WorldView.CameraMoveSpeed_Normal; - - MsbEditor.Viewport.WorldView.CameraMoveSpeed_Fast = CFG.Default.GFX_Camera_MoveSpeed_Fast; - CFG.Current.GFX_Camera_MoveSpeed_Fast = MsbEditor.Viewport.WorldView.CameraMoveSpeed_Fast; - } - - var cam_sensitivity = CFG.Current.GFX_Camera_Sensitivity; - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Mouse sensitivty for turning the camera."); - ImGui.SameLine(); - } - if (ImGui.SliderFloat("Camera sensitivity", ref cam_sensitivity, 0.0f, 0.1f)) - { - CFG.Current.GFX_Camera_Sensitivity = cam_sensitivity; - } - - var cam_fov = CFG.Current.GFX_Camera_FOV; - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Set the field of view used by the camera within DSMS."); - ImGui.SameLine(); - } - if (ImGui.SliderFloat("Camera FOV", ref cam_fov, 40.0f, 140.0f)) - { - CFG.Current.GFX_Camera_FOV = cam_fov; - } - - var farClip = CFG.Current.GFX_RenderDistance_Max; - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Set the maximum distance at which entities will be rendered within the DSMS viewport."); - ImGui.SameLine(); - } - if (ImGui.SliderFloat("Map max render distance", ref farClip, 10.0f, 500000.0f)) - { - CFG.Current.GFX_RenderDistance_Max = farClip; - } - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Set the speed at which the camera will move when the Left or Right Shift key is pressed whilst moving."); - ImGui.SameLine(); - } - if (ImGui.SliderFloat("Map camera speed (slow)", - ref MsbEditor.Viewport.WorldView.CameraMoveSpeed_Slow, 0.1f, 999.0f)) - { - CFG.Current.GFX_Camera_MoveSpeed_Slow = MsbEditor.Viewport.WorldView.CameraMoveSpeed_Slow; - } - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Set the speed at which the camera will move whilst moving normally."); - ImGui.SameLine(); - } - if (ImGui.SliderFloat("Map camera speed (normal)", - ref MsbEditor.Viewport.WorldView.CameraMoveSpeed_Normal, 0.1f, 999.0f)) - { - CFG.Current.GFX_Camera_MoveSpeed_Normal = MsbEditor.Viewport.WorldView.CameraMoveSpeed_Normal; - } - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Set the speed at which the camera will move when the Left or Right Control key is pressed whilst moving."); - ImGui.SameLine(); - } - if (ImGui.SliderFloat("Map camera speed (fast)", - ref MsbEditor.Viewport.WorldView.CameraMoveSpeed_Fast, 0.1f, 999.0f)) - { - CFG.Current.GFX_Camera_MoveSpeed_Fast = MsbEditor.Viewport.WorldView.CameraMoveSpeed_Fast; - } - } - - if (ImGui.CollapsingHeader("Limits")) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Reset the values within this section to their default values."); - ImGui.SameLine(); - } - if (ImGui.Button("Reset##MapLimits")) - { - CFG.Current.GFX_Limit_Renderables = CFG.Default.GFX_Limit_Renderables; - CFG.Current.GFX_Limit_Buffer_Indirect_Draw = CFG.Default.GFX_Limit_Buffer_Indirect_Draw; - CFG.Current.GFX_Limit_Buffer_Flver_Bone = CFG.Default.GFX_Limit_Buffer_Flver_Bone; - } - - ImGui.Text("Please restart the program for changes to take effect."); - - ImGui.TextColored(new Vector4(1.0f, 0.0f, 0.0f, 1.0f), - @"Try smaller increments (+25%%) at first, as high values will cause issues."); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("This value constrains the number of renderable entities that are allowed. Exceeding this value will throw an exception."); - ImGui.SameLine(); - } - if (ImGui.InputInt("Renderables", ref CFG.Current.GFX_Limit_Renderables, 0, 0)) - { - if (CFG.Current.GFX_Limit_Renderables < CFG.Default.GFX_Limit_Renderables) - { - CFG.Current.GFX_Limit_Renderables = CFG.Default.GFX_Limit_Renderables; - } - } - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("This value constrains the size of the indirect draw buffer. Exceeding this value will throw an exception."); - ImGui.SameLine(); - } - Utils.ImGui_InputUint("Indirect Draw buffer", ref CFG.Current.GFX_Limit_Buffer_Indirect_Draw); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("This value constrains the size of the FLVER bone buffer. Exceeding this value will throw an exception."); - ImGui.SameLine(); - } - Utils.ImGui_InputUint("FLVER Bone buffer", ref CFG.Current.GFX_Limit_Buffer_Flver_Bone); - } - - if (FeatureFlags.ViewportGrid) - { - if (ImGui.CollapsingHeader("Grid")) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Enable the viewport grid when in the Map Editor."); - ImGui.SameLine(); - } - ImGui.Checkbox("Enable viewport grid", ref CFG.Current.Map_EnableViewportGrid); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("The overall maximum size of the grid.\nThe grid will only update upon restarting DSMS after changing this value."); - ImGui.SameLine(); - } - ImGui.SliderInt("Grid size", ref CFG.Current.Map_ViewportGrid_TotalSize, 100, 1000); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("The increment size of the grid."); - ImGui.SameLine(); - } - ImGui.SliderInt("Grid increment", ref CFG.Current.Map_ViewportGrid_IncrementSize, 1, 100); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("The height at which the horizontal grid sits."); - ImGui.SameLine(); - } - ImGui.SliderFloat("Grid height", ref CFG.Current.Map_ViewportGrid_Offset, -1000, 1000); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("The amount to lower or raise the viewport grid height via the shortcuts."); - ImGui.SameLine(); - } - ImGui.SliderFloat("Grid height increment", ref CFG.Current.Map_ViewportGrid_ShortcutIncrement, 0.1f, 100); - - ImGui.ColorEdit3("Grid color", ref CFG.Current.GFX_Viewport_Grid_Color); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Resets all of the values within this section to their default values."); - ImGui.SameLine(); - } - if (ImGui.Button("Reset")) - { - CFG.Current.GFX_Viewport_Grid_Color = Utils.GetDecimalColor(Color.Red); - CFG.Current.Map_ViewportGrid_TotalSize = 1000; - CFG.Current.Map_ViewportGrid_IncrementSize = 10; - CFG.Current.Map_ViewportGrid_Offset = 0; - } - } - } - - if (ImGui.CollapsingHeader("Wireframes")) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Resets all of the values within this section to their default values."); - ImGui.SameLine(); - } - if (ImGui.Button("Reset")) - { - // Proxies - CFG.Current.GFX_Renderable_Box_BaseColor = Utils.GetDecimalColor(Color.Blue); - CFG.Current.GFX_Renderable_Box_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - CFG.Current.GFX_Renderable_Cylinder_BaseColor = Utils.GetDecimalColor(Color.Blue); - CFG.Current.GFX_Renderable_Cylinder_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - CFG.Current.GFX_Renderable_Sphere_BaseColor = Utils.GetDecimalColor(Color.Blue); - CFG.Current.GFX_Renderable_Sphere_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - CFG.Current.GFX_Renderable_Point_BaseColor = Utils.GetDecimalColor(Color.Yellow); - CFG.Current.GFX_Renderable_Point_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - CFG.Current.GFX_Renderable_DummyPoly_BaseColor = Utils.GetDecimalColor(Color.Yellow); - CFG.Current.GFX_Renderable_DummyPoly_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - CFG.Current.GFX_Renderable_BonePoint_BaseColor = Utils.GetDecimalColor(Color.Blue); - CFG.Current.GFX_Renderable_BonePoint_HighlightColor = Utils.GetDecimalColor(Color.DarkViolet); - - CFG.Current.GFX_Renderable_ModelMarker_Chr_BaseColor = Utils.GetDecimalColor(Color.Firebrick); - CFG.Current.GFX_Renderable_ModelMarker_Chr_HighlightColor = Utils.GetDecimalColor(Color.Tomato); - - CFG.Current.GFX_Renderable_ModelMarker_Object_BaseColor = Utils.GetDecimalColor(Color.MediumVioletRed); - CFG.Current.GFX_Renderable_ModelMarker_Object_HighlightColor = Utils.GetDecimalColor(Color.DeepPink); - - CFG.Current.GFX_Renderable_ModelMarker_Player_BaseColor = Utils.GetDecimalColor(Color.DarkOliveGreen); - CFG.Current.GFX_Renderable_ModelMarker_Player_HighlightColor = Utils.GetDecimalColor(Color.OliveDrab); - - CFG.Current.GFX_Renderable_ModelMarker_Other_BaseColor = Utils.GetDecimalColor(Color.Wheat); - CFG.Current.GFX_Renderable_ModelMarker_Other_HighlightColor = Utils.GetDecimalColor(Color.AntiqueWhite); - - CFG.Current.GFX_Renderable_PointLight_BaseColor = Utils.GetDecimalColor(Color.YellowGreen); - CFG.Current.GFX_Renderable_PointLight_HighlightColor = Utils.GetDecimalColor(Color.Yellow); - - CFG.Current.GFX_Renderable_SpotLight_BaseColor = Utils.GetDecimalColor(Color.Goldenrod); - CFG.Current.GFX_Renderable_SpotLight_HighlightColor = Utils.GetDecimalColor(Color.Violet); - - CFG.Current.GFX_Renderable_DirectionalLight_BaseColor = Utils.GetDecimalColor(Color.Cyan); - CFG.Current.GFX_Renderable_DirectionalLight_HighlightColor = Utils.GetDecimalColor(Color.AliceBlue); - - // Gizmos - CFG.Current.GFX_Gizmo_X_BaseColor = new Vector3(0.952f, 0.211f, 0.325f); - CFG.Current.GFX_Gizmo_X_HighlightColor = new Vector3(1.0f, 0.4f, 0.513f); - - CFG.Current.GFX_Gizmo_Y_BaseColor = new Vector3(0.525f, 0.784f, 0.082f); - CFG.Current.GFX_Gizmo_Y_HighlightColor = new Vector3(0.713f, 0.972f, 0.270f); - - CFG.Current.GFX_Gizmo_Z_BaseColor = new Vector3(0.219f, 0.564f, 0.929f); - CFG.Current.GFX_Gizmo_Z_HighlightColor = new Vector3(0.407f, 0.690f, 1.0f); - - // Color Variance - CFG.Current.GFX_Wireframe_Color_Variance = CFG.Default.GFX_Wireframe_Color_Variance; - } - - ImGui.SliderFloat("Wireframe color variance", ref CFG.Current.GFX_Wireframe_Color_Variance, 0.0f, 1.0f); - - // Proxies - ImGui.ColorEdit3("Box region - base color", ref CFG.Current.GFX_Renderable_Box_BaseColor); - ImGui.ColorEdit3("Box region - highlight color", ref CFG.Current.GFX_Renderable_Box_HighlightColor); - - ImGui.ColorEdit3("Cylinder region - base color", ref CFG.Current.GFX_Renderable_Cylinder_BaseColor); - ImGui.ColorEdit3("Cylinder region - highlight color", ref CFG.Current.GFX_Renderable_Cylinder_HighlightColor); - - ImGui.ColorEdit3("Sphere region - base color", ref CFG.Current.GFX_Renderable_Sphere_BaseColor); - ImGui.ColorEdit3("Sphere region - highlight color", ref CFG.Current.GFX_Renderable_Sphere_HighlightColor); - - ImGui.ColorEdit3("Point region - base color", ref CFG.Current.GFX_Renderable_Point_BaseColor); - ImGui.ColorEdit3("Point region - highlight color", ref CFG.Current.GFX_Renderable_Point_HighlightColor); - - ImGui.ColorEdit3("Dummy poly - base color", ref CFG.Current.GFX_Renderable_DummyPoly_BaseColor); - ImGui.ColorEdit3("Dummy poly - highlight color", ref CFG.Current.GFX_Renderable_DummyPoly_HighlightColor); - - ImGui.ColorEdit3("Bone point - base color", ref CFG.Current.GFX_Renderable_BonePoint_BaseColor); - ImGui.ColorEdit3("Bone point - highlight color", ref CFG.Current.GFX_Renderable_BonePoint_HighlightColor); - - ImGui.ColorEdit3("Chr marker - base color", ref CFG.Current.GFX_Renderable_ModelMarker_Chr_BaseColor); - ImGui.ColorEdit3("Chr marker - highlight color", ref CFG.Current.GFX_Renderable_ModelMarker_Chr_HighlightColor); - - ImGui.ColorEdit3("Object marker - base color", ref CFG.Current.GFX_Renderable_ModelMarker_Object_BaseColor); - ImGui.ColorEdit3("Object marker - highlight color", ref CFG.Current.GFX_Renderable_ModelMarker_Object_HighlightColor); - - ImGui.ColorEdit3("Player marker - base color", ref CFG.Current.GFX_Renderable_ModelMarker_Player_BaseColor); - ImGui.ColorEdit3("Player marker - highlight color", ref CFG.Current.GFX_Renderable_ModelMarker_Player_HighlightColor); - - ImGui.ColorEdit3("Other marker - base color", ref CFG.Current.GFX_Renderable_ModelMarker_Other_BaseColor); - ImGui.ColorEdit3("Other marker - highlight color", ref CFG.Current.GFX_Renderable_ModelMarker_Other_HighlightColor); - - ImGui.ColorEdit3("Point light - base color", ref CFG.Current.GFX_Renderable_PointLight_BaseColor); - ImGui.ColorEdit3("Point light - highlight color", ref CFG.Current.GFX_Renderable_PointLight_HighlightColor); - - ImGui.ColorEdit3("Spot light - base color", ref CFG.Current.GFX_Renderable_SpotLight_BaseColor); - ImGui.ColorEdit3("Spot light - highlight color", ref CFG.Current.GFX_Renderable_SpotLight_HighlightColor); - - ImGui.ColorEdit3("Directional light - base color", ref CFG.Current.GFX_Renderable_DirectionalLight_BaseColor); - ImGui.ColorEdit3("Directional light - highlight color", ref CFG.Current.GFX_Renderable_DirectionalLight_HighlightColor); - - // Gizmos - ImGui.ColorEdit3("Gizmo - X Axis - base color", ref CFG.Current.GFX_Gizmo_X_BaseColor); - ImGui.ColorEdit3("Gizmo - X Axis - highlight color", ref CFG.Current.GFX_Gizmo_X_HighlightColor); - - ImGui.ColorEdit3("Gizmo - Y Axis - base color", ref CFG.Current.GFX_Gizmo_Y_BaseColor); - ImGui.ColorEdit3("Gizmo - Y Axis - highlight color", ref CFG.Current.GFX_Gizmo_Y_HighlightColor); - - ImGui.ColorEdit3("Gizmo - Z Axis - base color", ref CFG.Current.GFX_Gizmo_Z_BaseColor); - ImGui.ColorEdit3("Gizmo - Z Axis - highlight color", ref CFG.Current.GFX_Gizmo_Z_HighlightColor); - } - - if (ImGui.CollapsingHeader("Map Object Display Presets")) - { - ImGui.Text("Configure each of the six display presets available."); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Reset the values within this section to their default values."); - ImGui.SameLine(); - } - if (ImGui.Button("Reset##DisplayPresets")) - { - CFG.Current.SceneFilter_Preset_01.Name = CFG.Default.SceneFilter_Preset_01.Name; - CFG.Current.SceneFilter_Preset_01.Filters = CFG.Default.SceneFilter_Preset_01.Filters; - CFG.Current.SceneFilter_Preset_02.Name = CFG.Default.SceneFilter_Preset_02.Name; - CFG.Current.SceneFilter_Preset_02.Filters = CFG.Default.SceneFilter_Preset_02.Filters; - CFG.Current.SceneFilter_Preset_03.Name = CFG.Default.SceneFilter_Preset_03.Name; - CFG.Current.SceneFilter_Preset_03.Filters = CFG.Default.SceneFilter_Preset_03.Filters; - CFG.Current.SceneFilter_Preset_04.Name = CFG.Default.SceneFilter_Preset_04.Name; - CFG.Current.SceneFilter_Preset_04.Filters = CFG.Default.SceneFilter_Preset_04.Filters; - CFG.Current.SceneFilter_Preset_05.Name = CFG.Default.SceneFilter_Preset_05.Name; - CFG.Current.SceneFilter_Preset_05.Filters = CFG.Default.SceneFilter_Preset_05.Filters; - CFG.Current.SceneFilter_Preset_06.Name = CFG.Default.SceneFilter_Preset_06.Name; - CFG.Current.SceneFilter_Preset_06.Filters = CFG.Default.SceneFilter_Preset_06.Filters; - } - - SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_01); - SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_02); - SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_03); - SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_04); - SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_05); - SettingsRenderFilterPresetEditor(CFG.Current.SceneFilter_Preset_06); - } - - ImGui.Unindent(); - ImGui.EndTabItem(); - } - } - - private void DisplaySettings_ModelEditor() - { - if (ImGui.BeginTabItem("Model Editor")) - { - if (ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) - { - - } - - ImGui.EndTabItem(); - } - } - - private void DisplaySettings_ParamEditor() - { - if (ImGui.BeginTabItem("Param Editor")) - { - if (ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Reduces the line height within the the Param Editor screen."); - ImGui.SameLine(); - } - ImGui.Checkbox("Use compact param editor", ref CFG.Current.UI_CompactParams); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Show additional options within the MassEdit context menu."); - ImGui.SameLine(); - } - ImGui.Checkbox("Show advanced massedit options", ref CFG.Current.Param_AdvancedMassedit); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Show the shortcut tools in the right-click context menu."); - ImGui.SameLine(); - } - ImGui.Checkbox("Show shortcut tools in context menus", ref CFG.Current.Param_ShowHotkeysInContextMenu); - } - - if (ImGui.CollapsingHeader("Params")) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Sort the Param View list alphabetically."); - ImGui.SameLine(); - } - if (ImGui.Checkbox("Sort params alphabetically", ref CFG.Current.Param_AlphabeticalParams)) - { - UICache.ClearCaches(); - } - } - - if (ImGui.CollapsingHeader("Rows")) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Disable the row names from wrapping within the Row View list."); - ImGui.SameLine(); - } - ImGui.Checkbox("Disable line wrapping", ref CFG.Current.Param_DisableLineWrapping); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Disable the grouping of connected rows in certain params, such as ItemLotParam within the Row View list."); - ImGui.SameLine(); - } - ImGui.Checkbox("Disable row grouping", ref CFG.Current.Param_DisableRowGrouping); - } - - if (ImGui.CollapsingHeader("Fields")) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Crowd-sourced names will appear before the canonical name in the Field View list."); - ImGui.SameLine(); - } - ImGui.Checkbox("Show community field names first", ref CFG.Current.Param_MakeMetaNamesPrimary); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("The crowd-sourced name (or the canonical name if the above option is enabled) will appear after the initial name in the Field View list."); - ImGui.SameLine(); - } - ImGui.Checkbox("Show secondary field names", ref CFG.Current.Param_ShowSecondaryNames); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("The field offset within the .PARAM file will be show to the left in the Field View List."); - ImGui.SameLine(); - } - ImGui.Checkbox("Show field data offsets", ref CFG.Current.Param_ShowFieldOffsets); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Hide the generated param references for fields that link to other params."); - ImGui.SameLine(); - } - ImGui.Checkbox("Hide field references", ref CFG.Current.Param_HideReferenceRows); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Hide the crowd-sourced namelist for index-based enum fields."); - ImGui.SameLine(); - } - ImGui.Checkbox("Hide field enums", ref CFG.Current.Param_HideEnums); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Allow the field order to be changed by an alternative order as defined within the Paramdex META file."); - ImGui.SameLine(); - } - ImGui.Checkbox("Allow field reordering", ref CFG.Current.Param_AllowFieldReorder); - } - - ImGui.EndTabItem(); - } - } - - private void DisplaySettings_TextEditor() - { - if (ImGui.BeginTabItem("Text Editor")) - { - if (ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) - { - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("Show the original FMG file names within the Text Editor file list."); - ImGui.SameLine(); - } - ImGui.Checkbox("Show original FMG names", ref CFG.Current.FMG_ShowOriginalNames); - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("If enabled then FMG entries will not be grouped automatically."); - ImGui.SameLine(); - } - if (ImGui.Checkbox("Separate related FMGs and entries", ref CFG.Current.FMG_NoGroupedFmgEntries)) - { - TextEditor.OnProjectChanged(ProjSettings); - } - - if (CFG.Current.ShowUITooltips) - { - ShowHelpMarker("If enabled then FMG files added from DLCs will not be grouped with vanilla FMG files."); - ImGui.SameLine(); - } - if (ImGui.Checkbox("Separate patch FMGs", ref CFG.Current.FMG_NoFmgPatching)) - { - TextEditor.OnProjectChanged(ProjSettings); - } - } - - ImGui.EndTabItem(); - } - } - private void DisplaySettings_Keybinds() { if (ImGui.BeginTabItem("Keybinds")) @@ -905,20 +243,20 @@ private void DisplaySettings_AssetBrowser() if (ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) { ImGui.Checkbox("Display aliases in browser list", ref CFG.Current.AssetBrowser_ShowAliasesInBrowser); - ImguiUtils.ShowHoverTooltip("Show the aliases for each entry within the browser list as part of their displayed name."); + Editor.EditorDecorations.ShowHoverTooltip("Show the aliases for each entry within the browser list as part of their displayed name."); ImGui.Checkbox("Display tags in browser list", ref CFG.Current.AssetBrowser_ShowTagsInBrowser); - ImguiUtils.ShowHoverTooltip("Show the tags for each entry within the browser list as part of their displayed name."); + Editor.EditorDecorations.ShowHoverTooltip("Show the tags for each entry within the browser list as part of their displayed name."); ImGui.Checkbox("Display low-detail parts in browser list", ref CFG.Current.AssetBrowser_ShowLowDetailParts); - ImguiUtils.ShowHoverTooltip("Show the _l (low-detail) part entries in the Model Editor instance of the Asset Browser."); + Editor.EditorDecorations.ShowHoverTooltip("Show the _l (low-detail) part entries in the Model Editor instance of the Asset Browser."); } ImGui.EndTabItem(); } } - public void Display() + public void Display(IEnumerable editorScreens) { var scale = MapStudioNew.GetUIScale(); if (!MenuOpenState) @@ -942,10 +280,14 @@ public void Display() // Settings Order DisplaySettings_System(); DisplaySettings_AssetBrowser(); - DisplaySettings_MapEditor(); - //DisplaySettings_ModelEditor(); - DisplaySettings_ParamEditor(); - DisplaySettings_TextEditor(); + foreach (EditorScreen scr in editorScreens) + { + if (ImGui.BeginTabItem(scr.EditorName)) + { + scr.SettingsMenu(); + ImGui.EndTabItem(); + } + } DisplaySettings_Keybinds(); ImGui.PopItemWidth(); @@ -958,57 +300,4 @@ public void Display() ImGui.PopStyleVar(3); ImGui.PopStyleColor(2); } - - public void ShowHelpMarker(string desc) - { - ImGui.TextDisabled("(?)"); - if (ImGui.IsItemHovered(0)) - { - ImGui.BeginTooltip(); - ImGui.PushTextWrapPos(450.0f); - ImGui.TextUnformatted(desc); - ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); - } - } - - private void SettingsRenderFilterPresetEditor(CFG.RenderFilterPreset preset) - { - ImGui.PushID($"{preset.Name}##PresetEdit"); - if (ImGui.CollapsingHeader($"{preset.Name}##Header")) - { - ImGui.Indent(); - var nameInput = preset.Name; - ImGui.InputText("Preset Name", ref nameInput, 32); - if (ImGui.IsItemDeactivatedAfterEdit()) - { - preset.Name = nameInput; - } - - foreach (RenderFilter e in Enum.GetValues(typeof(RenderFilter))) - { - var ticked = false; - if (preset.Filters.HasFlag(e)) - { - ticked = true; - } - - if (ImGui.Checkbox(e.ToString(), ref ticked)) - { - if (ticked) - { - preset.Filters |= e; - } - else - { - preset.Filters &= ~e; - } - } - } - - ImGui.Unindent(); - } - - ImGui.PopID(); - } } diff --git a/src/StudioCore/StudioResource.cs b/src/StudioCore/StudioResource.cs new file mode 100644 index 000000000..471c8d2e8 --- /dev/null +++ b/src/StudioCore/StudioResource.cs @@ -0,0 +1,82 @@ +using StudioCore.Editor; +using System.Collections.Generic; + +namespace StudioCore; + +/// +/// Class that stores a collection of mapstudio data independent of a given project +/// +public abstract class StudioResource +{ + public GameType GameType; + public readonly string nameForUI; + + public StudioResource(GameType gameType, string name) + { + GameType = gameType; + nameForUI = name; + } + + public bool IsLoaded { get; private set; } + public bool IsLoading { get; private set; } + + private void Load(Project project) + { + if (IsLoaded || IsLoading) + return; + // Locking on this isn't ideal if anything else could lock the object. But we don't have Lock yet. + lock (this) + { + IsLoading = true; + List tasks = new(); + foreach (StudioResource res in GetDependencies(project)) + { + if (!res.IsLoaded) + { + TaskManager.LiveTask t = new TaskManager.LiveTask(res.GetTaskName(), TaskManager.RequeueType.None, true, () => { + res.Load(project); + }); + t = TaskManager.Run(t); + tasks.Add(t); + } + } + foreach (TaskManager.LiveTask t in tasks) + { + if (!t.Task.IsCompleted) + t.Task.Wait(); + } + Load(); + IsLoaded = true; + IsLoading = false; + } + } + + public virtual string GetTaskName() + { + return $@"Resource - Loading {nameForUI}"; + } + protected abstract void Load(); + protected abstract IEnumerable GetDependencies(Project project); + + public static bool AreResourcesLoaded(IEnumerable res) + { + foreach (StudioResource r in res) + { + if (!r.IsLoaded) + return false; + } + return true; + } + public static void Load(Project project, IEnumerable resources) + { + foreach (StudioResource res in resources) + { + if (!res.IsLoaded) + { + TaskManager.Run(new TaskManager.LiveTask(res.GetTaskName(), TaskManager.RequeueType.None, true, () => { + res.Load(project); + })); + } + } + } +} diff --git a/src/StudioCore/TextEditor/FMGBank.cs b/src/StudioCore/TextEditor/FMGBank.cs index caf2af43b..93ee7e9df 100644 --- a/src/StudioCore/TextEditor/FMGBank.cs +++ b/src/StudioCore/TextEditor/FMGBank.cs @@ -551,11 +551,11 @@ private void SaveFMGsNormal() /// /// Class that stores all the strings for a Souls project. /// -public class FMGBank +public class FMGBank : DataBank { public Project Project; - public FMGBank(Project project) + public FMGBank(Project project) : base(project, "FMGs") { Project = project; } @@ -611,9 +611,9 @@ internal static string RemovePatchStrings(string str) return str; } - public static void ReloadFMGs() + protected override void Load() { - Locator.ActiveProject.FMGBank.LoadFMGs(); + LoadFMGs(); } public void LoadFMGs(string languageFolder = "") { @@ -646,8 +646,8 @@ public void LoadFMGs(string languageFolder = "") fmgLangs.Add(lang.LanguageFolder, lang); })); } - - public void SaveFMGs() + + public override void Save() { foreach (FMGLanguage lang in fmgLangs.Values) { @@ -795,6 +795,11 @@ public FMGEntryGroup GenerateEntryGroup(int id, FMGInfo fmgInfo) return eGroup; } + + protected override IEnumerable GetDependencies(Project project) + { + return []; + } } /// diff --git a/src/StudioCore/TextEditor/TextCFG.cs b/src/StudioCore/TextEditor/TextCFG.cs new file mode 100644 index 000000000..673fe0ab1 --- /dev/null +++ b/src/StudioCore/TextEditor/TextCFG.cs @@ -0,0 +1,17 @@ +using StudioCore.Platform; +using StudioCore.Scene; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Numerics; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace StudioCore; +public partial class CFG +{ + public bool FMG_NoFmgPatching = false; + public bool FMG_NoGroupedFmgEntries = false; + public bool FMG_ShowOriginalNames = false; +} diff --git a/src/StudioCore/TextEditor/TextEditorScreen.cs b/src/StudioCore/TextEditor/TextEditorScreen.cs index 6bff6cc73..92bced791 100644 --- a/src/StudioCore/TextEditor/TextEditorScreen.cs +++ b/src/StudioCore/TextEditor/TextEditorScreen.cs @@ -295,12 +295,12 @@ public void OnProjectChanged(ProjectSettings newSettings) public void Save() { - Locator.ActiveProject.FMGBank.SaveFMGs(); + Locator.ActiveProject.FMGBank.Save(); } public void SaveAll() { - Locator.ActiveProject.FMGBank.SaveFMGs(); + Locator.ActiveProject.FMGBank.Save(); } private void ClearTextEditorCache() @@ -770,4 +770,32 @@ private void ChangeLanguage(string path) ResetActionManager(); Locator.ActiveProject.FMGBank.LoadFMGs(path); } + + IEnumerable EditorScreen.GetDependencies(Project project) + { + return [project.FMGBank]; + } + + public void SettingsMenu() + { + if (ImGui.CollapsingHeader("General", ImGuiTreeNodeFlags.DefaultOpen)) + { + EditorDecorations.ShowHelpMarker("Show the original FMG file names within the Text Editor file list."); + ImGui.Checkbox("Show original FMG names", ref CFG.Current.FMG_ShowOriginalNames); + + EditorDecorations.ShowHelpMarker("If enabled then FMG entries will not be grouped automatically."); + if (ImGui.Checkbox("Separate related FMGs and entries", ref CFG.Current.FMG_NoGroupedFmgEntries)) + { + //This is still ghastly + OnProjectChanged(_projectSettings); + } + + EditorDecorations.ShowHelpMarker("If enabled then FMG files added from DLCs will not be grouped with vanilla FMG files."); + if (ImGui.Checkbox("Separate patch FMGs", ref CFG.Current.FMG_NoFmgPatching)) + { + //no really this has to go + OnProjectChanged(_projectSettings); + } + } + } } diff --git a/src/StudioCore/Utilities/ParamValidationTest.cs b/src/StudioCore/Utilities/ParamValidationTest.cs index 7271b98a0..5775c7f41 100644 --- a/src/StudioCore/Utilities/ParamValidationTest.cs +++ b/src/StudioCore/Utilities/ParamValidationTest.cs @@ -77,7 +77,7 @@ public static void ValidatePaddingForParam(string selectedParamName) public static void ValidateParamdef() { // Read params from regulation.bin via SF PARAM impl - _paramdefs = ParamBank.PrimaryBank.GetParamDefs(); + _paramdefs = ResDirectory.CurrentGame.ParamDefBank.GetParamDefs(); var dir = Locator.ActiveProject.ParentProject.AssetLocator.RootDirectory; var mod = Locator.ActiveProject.AssetLocator.RootDirectory;