//#define ENABLE_ASSERTS using System.Numerics; using System.Globalization; using System.Runtime.CompilerServices; #if ENABLE_ASSERTS using System.Diagnostics; #endif using ImGuiNET; using SharpPluginLoader.Core; using SharpPluginLoader.Core.Memory; namespace WorldTuningTool { internal static class Utility { internal const float defaultParamWidth = 0.21f; internal const float fadeAlphaFactor = 0.7f; internal const float disableAlphaFactor = 0.6f; private static bool loggedAssertFailed = false; internal static void Assert(bool condition = false, [CallerLineNumber] int line = 0) { if (!condition && !loggedAssertFailed) { Log.Error($"Assert failed on line {line}, further asserts won't be logged."); loggedAssertFailed = true; } #if ENABLE_ASSERTS Trace.Assert(condition); #endif } internal static byte ByteFlag(bool f) { return f ? (byte)0x1 : (byte)0x0; } internal static T Get(nint addr, nint offset = 0x0) where T : unmanaged { return MemoryUtil.Read(addr + offset); } internal static T Get(long addr, nint offset = 0x0) where T : unmanaged { return MemoryUtil.Read((nint)addr + offset); } internal static unsafe T *GetPtrInline(nint addr, nint offset = 0x0) where T : unmanaged { return (T *)(addr + offset); } internal static ref T GetRef(nint addr, nint offset = 0x0) where T : unmanaged { return ref MemoryUtil.GetRef(addr + offset); } internal static int BitIndex(int mask) { return BitOperations.Log2((uint)(mask & (-mask))); } internal static Vector4 ColorVectorFromInt(int c) { return new Vector4( ((c ) & 0xFF) / 255.0f, ((c >> 8 ) & 0xFF) / 255.0f, ((c >> 16) & 0xFF) / 255.0f, ((c >> 24) & 0xFF) / 255.0f); } internal static int ColorVectorToInt(Vector4 v) { return ((int)MathF.Round(v.X * 255.0f) | ((int)MathF.Round(v.Y * 255.0f) << 8) | ((int)MathF.Round(v.Z * 255.0f) << 16) | ((int)MathF.Round(v.W * 255.0f) << 24)); } internal static float StringToSingle(string s) { return Convert.ToSingle(s, CultureInfo.InvariantCulture); } internal static bool StringToBoolean(string s) { return Convert.ToBoolean(s, CultureInfo.InvariantCulture); } internal static int StringToInt32(string s, int fromBase = 10) { if (fromBase == 10) return Convert.ToInt32(s, CultureInfo.InvariantCulture); return Convert.ToInt32(s, fromBase); } internal static string SingleToString(float f, string? format = null) { return f.ToString(format, CultureInfo.InvariantCulture); } internal static string BooleanToString(bool b) { return b.ToString(CultureInfo.InvariantCulture); } internal static string Int32ToString(int i, string? format = null) { return i.ToString(format, CultureInfo.InvariantCulture); } internal static Vector2 StringToVector2(string s, char delim = ',') { if (string.IsNullOrEmpty(s)) throw new FormatException(); string[] sp = s.Split(delim).Select((v) => v.Trim()).ToArray(); if (sp.Length != 2) throw new FormatException(); return new(StringToSingle(sp[0]), StringToSingle(sp[1])); } internal static Vector3 StringToVector3(string s, char delim = ',') { if (string.IsNullOrEmpty(s)) throw new FormatException(); string[] sp = s.Split(delim).Select((v) => v.Trim()).ToArray(); if (sp.Length != 3) throw new FormatException(); return new(StringToSingle(sp[0]), StringToSingle(sp[1]), StringToSingle(sp[2])); } internal static Vector4 StringToVector4(string s, char delim = ',') { if (string.IsNullOrEmpty(s)) throw new FormatException(); string[] sp = s.Split(delim).Select((v) => v.Trim()).ToArray(); if (sp.Length != 4) throw new FormatException(); return new(StringToSingle(sp[0]), StringToSingle(sp[1]), StringToSingle(sp[2]), StringToSingle(sp[3])); } internal static string Vector2ToString(Vector2 v) { return string.Format(CultureInfo.InvariantCulture, "{0},{1}", v.X, v.Y); } internal static string Vector3ToString(Vector3 v) { return string.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", v.X, v.Y, v.Z); } internal static string Vector4ToString(Vector4 v) { return string.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", v.X, v.Y, v.Z, v.W); } internal class PresetSelection { internal string Selected = ""; internal bool comboLastFocused = false; internal bool renaming = false; internal bool confirmDelete = false; internal string typed = ""; internal Dictionary> dict; internal string typeName; internal PresetSelection(Dictionary> dict, string typeName) { this.dict = dict; this.typeName = typeName; } internal string Draw(float width) { ImGui.PushItemWidth(width * 0.35f); if (!comboLastFocused) { ImGui.PushItemFlag(ImGuiItemFlags.NoNav, true); } if (ImGui.Button("+")) { string name = $"New {typeName}"; int dup = 0; while (dict.ContainsKey(name)) { dup++; name = $"New {typeName} ({dup})"; } dict[name] = new List(); Selected = name; renaming = true; typed = Selected; } if (!comboLastFocused) { ImGui.PopItemFlag(); } ImGui.SameLine(); if (renaming) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * disableAlphaFactor); } bool beginCombo = ImGui.BeginCombo($"##{typeName}", Selected); comboLastFocused = ImGui.IsItemActive() || ImGui.IsItemFocused(); if (beginCombo) { confirmDelete = false; if (ImGui.Selectable("(None)", false)) { Selected = ""; } foreach ((string name, List overrides) in dict) { bool isSelected = name == Selected; if (ImGui.Selectable(name, isSelected)) { Selected = name; } if (isSelected) ImGui.SetItemDefaultFocus(); } ImGui.EndCombo(); } string preRenameSelected = Selected; if (renaming) { ImGui.PopStyleVar(); ImGui.PopItemFlag(); } ImGui.PopItemWidth(); if (Selected != "") { ImGui.SameLine(); ImGui.PushItemWidth(width * 0.2f); if (renaming) { ImGui.InputText($"##{typeName} Name", ref typed, 99); ImGui.SameLine(); if (typed == "") { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * disableAlphaFactor); } if (ImGui.Button("Save")) { string name = typed; if (name != Selected) { int dup = 0; while (dict.ContainsKey(name)) { dup++; name = $"{typed} ({dup})"; } List tmpOverrides = dict[Selected]; dict.Remove(Selected); dict[name] = tmpOverrides; Selected = name; } renaming = false; } if (typed == "") { ImGui.PopStyleVar(); ImGui.PopItemFlag(); } } else { if (confirmDelete) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); } if (ImGui.Button("Rename")) { renaming = true; typed = Selected; } if (confirmDelete) { ImGui.PopItemFlag(); } ImGui.SameLine(); if (confirmDelete) { ImGui.AlignTextToFramePadding(); ImGui.Text("Are you sure?"); ImGui.SameLine(); if (ImGui.Button("No")) { confirmDelete = false; } ImGui.SameLine(); if (ImGui.Button("Yes")) { dict.Remove(Selected); Selected = ""; confirmDelete = false; } } else if (ImGui.Button("Delete")) { confirmDelete = true; } } ImGui.PopItemWidth(); } return preRenameSelected; } } } }