diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 674e3772..8e6f8404 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -45,4 +45,4 @@ jobs: automatic_release_tag: ${{ github.event.inputs.appVersion }} repo_token: ${{ secrets.GITHUB_TOKEN }} prerelease: false - files: FModel.zip + files: FModel.zip \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 05465fcd..959570f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/FabianFG/CUE4Parse [submodule "EpicManifestParser"] path = EpicManifestParser - url = https://github.com/FModel/EpicManifestParser + url = https://github.com/FModel/EpicManifestParser \ No newline at end of file diff --git a/CUE4Parse b/CUE4Parse index 3ea132c5..d9c3db37 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 3ea132c5b9464d3e0c991dadc4302a1b8da7bd06 +Subproject commit d9c3db37652e615cf7160c168ccdb995171d1947 diff --git a/FModel/Constants.cs b/FModel/Constants.cs index 9ea96e56..e2667514 100644 --- a/FModel/Constants.cs +++ b/FModel/Constants.cs @@ -9,6 +9,9 @@ public static class Constants public const string ZERO_64_CHAR = "0000000000000000000000000000000000000000000000000000000000000000"; public static readonly FGuid ZERO_GUID = new(0U); + public const float SCALE_DOWN_RATIO = 0.01F; + public const int SAMPLES_COUNT = 4; + public const string WHITE = "#DAE5F2"; public const string GRAY = "#BBBBBB"; public const string RED = "#E06C75"; diff --git a/FModel/Creator/Bases/FN/BaseIcon.cs b/FModel/Creator/Bases/FN/BaseIcon.cs index 897fbbfd..3fd87232 100644 --- a/FModel/Creator/Bases/FN/BaseIcon.cs +++ b/FModel/Creator/Bases/FN/BaseIcon.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Windows; +using CUE4Parse.GameTypes.FN.Enums; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Assets.Exports.Engine; using CUE4Parse.UE4.Assets.Exports.Material; @@ -12,7 +13,6 @@ using CUE4Parse.UE4.Objects.Core.Math; using CUE4Parse.UE4.Objects.GameplayTags; using CUE4Parse.UE4.Objects.UObject; using CUE4Parse_Conversion.Textures; -using CUE4Parse.GameTypes.FN.Enums; using FModel.Settings; using SkiaSharp; @@ -25,9 +25,7 @@ public class BaseIcon : UCreator protected string CosmeticSource { get; set; } protected Dictionary UserFacingFlags { get; set; } - public BaseIcon(UObject uObject, EIconStyle style) : base(uObject, style) - { - } + public BaseIcon(UObject uObject, EIconStyle style) : base(uObject, style) { } public void ParseForReward(bool isUsingDisplayAsset) { @@ -198,26 +196,39 @@ public class BaseIcon : UCreator protected string GetCosmeticSeason(string seasonNumber) { var s = seasonNumber["Cosmetics.Filter.Season.".Length..]; - var number = int.Parse(s); + var initial = int.Parse(s); + var number = initial; - switch (number) + var chapterIdx = 0; + var seasonIdx = 0; + + while (number > 0) { - case 10: - s = "X"; - break; - case > 18: - number += 2; - s = number.ToString(); - break; + var seasonsInChapter = GetSeasonsInChapter(++chapterIdx); + if (number > seasonsInChapter) + number -= seasonsInChapter; + else + { + seasonIdx = number; + number = 0; + } } + static int GetSeasonsInChapter(int chapter) => chapter switch + { + 1 => 10, + 2 => 8, + 3 => 4, + _ => 10 + }; + var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}"); var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in {0}."); - if (number <= 10) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, s))); + if (initial <= 10) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, s))); var chapter = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterTextFormat", "Chapter {0}"); var chapterFormat = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterSeasonTextFormat", "{0}, {1}"); - var d = string.Format(chapterFormat, string.Format(chapter, number / 10 + 1), string.Format(season, s[^1..])); + var d = string.Format(chapterFormat, string.Format(chapter, chapterIdx), string.Format(season, seasonIdx)); return Utils.RemoveHtmlTags(string.Format(introduced, d)); } diff --git a/FModel/Creator/Utils.cs b/FModel/Creator/Utils.cs index 8035f74d..40970544 100644 --- a/FModel/Creator/Utils.cs +++ b/FModel/Creator/Utils.cs @@ -204,6 +204,9 @@ public static class Utils return string.Empty; } + public static string FixPath(string weirdPath) => + _applicationView.CUE4Parse.Provider.FixPath(weirdPath, StringComparison.Ordinal); + public static void DrawCenteredMultilineText(SKCanvas c, string text, int maxCount, int size, int margin, SKTextAlign side, SKRect area, SKPaint paint) { var lineHeight = paint.TextSize * 1.2f; diff --git a/FModel/Enums.cs b/FModel/Enums.cs index 4396f0ac..39e3f0e6 100644 --- a/FModel/Enums.cs +++ b/FModel/Enums.cs @@ -95,6 +95,8 @@ public enum FGame PandaGame, [Description("Tower of Fantasy")] Hotta, + [Description("eFootball 2023")] + eFootball } public enum ELoadingMode diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj index afa3c78a..6f8d2a90 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -5,9 +5,9 @@ net6.0-windows true FModel.ico - 4.3.2 - 4.3.2.1 - 4.3.2.1 + 4.4.0 + 4.4.0.0 + 4.4.0.0 false true win-x64 @@ -93,6 +93,20 @@ + + + + + + + + + + + + + + @@ -102,6 +116,20 @@ + + + + + + + + + + + + + + @@ -111,15 +139,17 @@ - + + + @@ -190,6 +220,14 @@ + + + + + + + + diff --git a/FModel/Framework/FStatus.cs b/FModel/Framework/FStatus.cs new file mode 100644 index 00000000..28421deb --- /dev/null +++ b/FModel/Framework/FStatus.cs @@ -0,0 +1,45 @@ +namespace FModel.Framework; + +public class FStatus : ViewModel +{ + private bool _isReady; + public bool IsReady + { + get => _isReady; + private set => SetProperty(ref _isReady, value); + } + + private EStatusKind _kind; + public EStatusKind Kind + { + get => _kind; + private set + { + SetProperty(ref _kind, value); + IsReady = Kind != EStatusKind.Loading && Kind != EStatusKind.Stopping; + } + } + + private string _label; + public string Label + { + get => _label; + private set => SetProperty(ref _label, value); + } + + public FStatus() + { + SetStatus(EStatusKind.Loading); + } + + public void SetStatus(EStatusKind kind, string label = "") + { + Kind = kind; + UpdateStatusLabel(label); + } + + public void UpdateStatusLabel(string label) + { + Label = Kind == EStatusKind.Loading ? $"{Kind} {label}".Trim() : Kind.ToString(); + } +} diff --git a/FModel/Framework/ImGuiController.cs b/FModel/Framework/ImGuiController.cs new file mode 100644 index 00000000..e275a633 --- /dev/null +++ b/FModel/Framework/ImGuiController.cs @@ -0,0 +1,637 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using ImGuiNET; +using OpenTK.Graphics.OpenGL4; +using OpenTK.Windowing.Desktop; +using OpenTK.Windowing.GraphicsLibraryFramework; +using ErrorCode = OpenTK.Graphics.OpenGL4.ErrorCode; + +namespace FModel.Framework; + +public class ImGuiController : IDisposable +{ + private bool _frameBegun; + + private int _vertexArray; + private int _vertexBuffer; + private int _vertexBufferSize; + private int _indexBuffer; + private int _indexBufferSize; + + //private Texture _fontTexture; + + private int _fontTexture; + + private int _shader; + private int _shaderFontTextureLocation; + private int _shaderProjectionMatrixLocation; + + private int _windowWidth; + private int _windowHeight; + // private string _iniPath; + + private ImFontPtr _normal; + private ImFontPtr _bold; + private ImFontPtr _semiBold; + + private readonly Vector2 _scaleFactor = Vector2.One; + + private static bool KHRDebugAvailable = false; + + public ImGuiController(int width, int height) + { + _windowWidth = width; + _windowHeight = height; + // _iniPath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "imgui.ini"); + + int major = GL.GetInteger(GetPName.MajorVersion); + int minor = GL.GetInteger(GetPName.MinorVersion); + + KHRDebugAvailable = (major == 4 && minor >= 3) || IsExtensionSupported("KHR_debug"); + + IntPtr context = ImGui.CreateContext(); + ImGui.SetCurrentContext(context); + // ImGui.LoadIniSettingsFromDisk(_iniPath); + + var io = ImGui.GetIO(); + _normal = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeui.ttf", 16); + _bold = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeuib.ttf", 16); + _semiBold = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\seguisb.ttf", 16); + + io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset; + io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard; + io.ConfigFlags |= ImGuiConfigFlags.DockingEnable; + io.Fonts.Flags |= ImFontAtlasFlags.NoBakedLines; + + CreateDeviceResources(); + SetKeyMappings(); + + SetPerFrameImGuiData(1f / 60f); + + ImGui.NewFrame(); + _frameBegun = true; + } + + public void Bold() => PushFont(_bold); + public void SemiBold() => PushFont(_semiBold); + + public void PopFont() + { + ImGui.PopFont(); + PushFont(_normal); + } + + private void PushFont(ImFontPtr ptr) => ImGui.PushFont(ptr); + + public void WindowResized(int width, int height) + { + _windowWidth = width; + _windowHeight = height; + } + + public void DestroyDeviceObjects() + { + Dispose(); + } + + public void CreateDeviceResources() + { + _vertexBufferSize = 10000; + _indexBufferSize = 2000; + + int prevVAO = GL.GetInteger(GetPName.VertexArrayBinding); + int prevArrayBuffer = GL.GetInteger(GetPName.ArrayBufferBinding); + + _vertexArray = GL.GenVertexArray(); + GL.BindVertexArray(_vertexArray); + LabelObject(ObjectLabelIdentifier.VertexArray, _vertexArray, "ImGui"); + + _vertexBuffer = GL.GenBuffer(); + GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBuffer); + LabelObject(ObjectLabelIdentifier.Buffer, _vertexBuffer, "VBO: ImGui"); + GL.BufferData(BufferTarget.ArrayBuffer, _vertexBufferSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); + + _indexBuffer = GL.GenBuffer(); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBuffer); + LabelObject(ObjectLabelIdentifier.Buffer, _indexBuffer, "EBO: ImGui"); + GL.BufferData(BufferTarget.ElementArrayBuffer, _indexBufferSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); + + RecreateFontDeviceTexture(); + + string VertexSource = @"#version 330 core +uniform mat4 projection_matrix; +layout(location = 0) in vec2 in_position; +layout(location = 1) in vec2 in_texCoord; +layout(location = 2) in vec4 in_color; +out vec4 color; +out vec2 texCoord; +void main() +{ +gl_Position = projection_matrix * vec4(in_position, 0, 1); +color = in_color; +texCoord = in_texCoord; +}"; + string FragmentSource = @"#version 330 core +uniform sampler2D in_fontTexture; +in vec4 color; +in vec2 texCoord; +out vec4 outputColor; +void main() +{ +outputColor = color * texture(in_fontTexture, texCoord); +}"; + + _shader = CreateProgram("ImGui", VertexSource, FragmentSource); + _shaderProjectionMatrixLocation = GL.GetUniformLocation(_shader, "projection_matrix"); + _shaderFontTextureLocation = GL.GetUniformLocation(_shader, "in_fontTexture"); + + int stride = Unsafe.SizeOf(); + GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, stride, 0); + GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, stride, 8); + GL.VertexAttribPointer(2, 4, VertexAttribPointerType.UnsignedByte, true, stride, 16); + + GL.EnableVertexAttribArray(0); + GL.EnableVertexAttribArray(1); + GL.EnableVertexAttribArray(2); + + GL.BindVertexArray(prevVAO); + GL.BindBuffer(BufferTarget.ArrayBuffer, prevArrayBuffer); + + CheckGLError("End of ImGui setup"); + } + + /// + /// Recreates the device texture used to render text. + /// + public void RecreateFontDeviceTexture() + { + ImGuiIOPtr io = ImGui.GetIO(); + io.Fonts.GetTexDataAsRGBA32(out IntPtr pixels, out int width, out int height, out int bytesPerPixel); + + int mips = (int)Math.Floor(Math.Log(Math.Max(width, height), 2)); + + int prevActiveTexture = GL.GetInteger(GetPName.ActiveTexture); + GL.ActiveTexture(TextureUnit.Texture0); + int prevTexture2D = GL.GetInteger(GetPName.TextureBinding2D); + + _fontTexture = GL.GenTexture(); + GL.BindTexture(TextureTarget.Texture2D, _fontTexture); + GL.TexStorage2D(TextureTarget2d.Texture2D, mips, SizedInternalFormat.Rgba8, width, height); + LabelObject(ObjectLabelIdentifier.Texture, _fontTexture, "ImGui Text Atlas"); + + GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, width, height, PixelFormat.Bgra, PixelType.UnsignedByte, pixels); + + GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, mips - 1); + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); + + // Restore state + GL.BindTexture(TextureTarget.Texture2D, prevTexture2D); + GL.ActiveTexture((TextureUnit)prevActiveTexture); + + io.Fonts.SetTexID((IntPtr)_fontTexture); + + io.Fonts.ClearTexData(); + } + + /// + /// Renders the ImGui draw list data. + /// + public void Render() + { + if (_frameBegun) + { + _frameBegun = false; + ImGui.Render(); + RenderImDrawData(ImGui.GetDrawData()); + } + CheckGLError("End of frame"); + } + + /// + /// Updates ImGui input and IO configuration state. + /// + public void Update(GameWindow wnd, float deltaSeconds) + { + if (_frameBegun) + { + ImGui.Render(); + } + + SetPerFrameImGuiData(deltaSeconds); + UpdateImGuiInput(wnd); + + _frameBegun = true; + ImGui.NewFrame(); + } + + /// + /// Sets per-frame data based on the associated window. + /// This is called by Update(float). + /// + private void SetPerFrameImGuiData(float deltaSeconds) + { + ImGuiIOPtr io = ImGui.GetIO(); + // if (io.WantSaveIniSettings) ImGui.SaveIniSettingsToDisk(_iniPath); + io.DisplaySize = new Vector2( + _windowWidth / _scaleFactor.X, + _windowHeight / _scaleFactor.Y); + io.DisplayFramebufferScale = _scaleFactor; + io.DeltaTime = deltaSeconds; // DeltaTime is in seconds. + } + + readonly List PressedChars = new List(); + + private void UpdateImGuiInput(GameWindow wnd) + { + ImGuiIOPtr io = ImGui.GetIO(); + var mState = wnd.MouseState; + var kState = wnd.KeyboardState; + + io.AddMousePosEvent(mState.X, mState.Y); + io.AddMouseButtonEvent(0, mState[MouseButton.Left]); + io.AddMouseButtonEvent(1, mState[MouseButton.Right]); + io.AddMouseButtonEvent(2, mState[MouseButton.Middle]); + io.AddMouseButtonEvent(3, mState[MouseButton.Button1]); + io.AddMouseButtonEvent(4, mState[MouseButton.Button2]); + io.AddMouseWheelEvent(mState.ScrollDelta.X, mState.ScrollDelta.Y); + + foreach (Keys key in Enum.GetValues(typeof(Keys))) + { + if (key == Keys.Unknown || io.KeyMap[(int) key] == -1) continue; + io.AddKeyEvent((ImGuiKey) io.KeyMap[(int) key], kState.IsKeyDown(key)); + } + + foreach (var c in PressedChars) + { + io.AddInputCharacter(c); + } + PressedChars.Clear(); + + io.KeyShift = kState.IsKeyDown(Keys.LeftShift) || kState.IsKeyDown(Keys.RightShift); + io.KeyCtrl = kState.IsKeyDown(Keys.LeftControl) || kState.IsKeyDown(Keys.RightControl); + io.KeyAlt = kState.IsKeyDown(Keys.LeftAlt) || kState.IsKeyDown(Keys.RightAlt); + io.KeySuper = kState.IsKeyDown(Keys.LeftSuper) || kState.IsKeyDown(Keys.RightSuper); + } + + public void PressChar(char keyChar) + { + PressedChars.Add(keyChar); + } + + private static void SetKeyMappings() + { + ImGuiIOPtr io = ImGui.GetIO(); + io.KeyMap[(int)ImGuiKey.LeftShift] = (int)Keys.LeftShift; + io.KeyMap[(int)ImGuiKey.RightShift] = (int)Keys.RightShift; + io.KeyMap[(int)ImGuiKey.LeftCtrl] = (int)Keys.LeftControl; + io.KeyMap[(int)ImGuiKey.RightCtrl] = (int)Keys.RightControl; + io.KeyMap[(int)ImGuiKey.LeftAlt] = (int)Keys.LeftAlt; + io.KeyMap[(int)ImGuiKey.RightAlt] = (int)Keys.RightAlt; + io.KeyMap[(int)ImGuiKey.LeftSuper] = (int)Keys.LeftSuper; + io.KeyMap[(int)ImGuiKey.RightSuper] = (int)Keys.RightSuper; + io.KeyMap[(int)ImGuiKey.Menu] = (int)Keys.Menu; + io.KeyMap[(int)ImGuiKey.UpArrow] = (int)Keys.Up; + io.KeyMap[(int)ImGuiKey.DownArrow] = (int)Keys.Down; + io.KeyMap[(int)ImGuiKey.LeftArrow] = (int)Keys.Left; + io.KeyMap[(int)ImGuiKey.RightArrow] = (int)Keys.Right; + io.KeyMap[(int)ImGuiKey.Enter] = (int)Keys.Enter; + io.KeyMap[(int)ImGuiKey.Escape] = (int)Keys.Escape; + io.KeyMap[(int)ImGuiKey.Space] = (int)Keys.Space; + io.KeyMap[(int)ImGuiKey.Tab] = (int)Keys.Tab; + io.KeyMap[(int)ImGuiKey.Backspace] = (int)Keys.Backspace; + io.KeyMap[(int)ImGuiKey.Insert] = (int)Keys.Insert; + io.KeyMap[(int)ImGuiKey.Delete] = (int)Keys.Delete; + io.KeyMap[(int)ImGuiKey.PageUp] = (int)Keys.PageUp; + io.KeyMap[(int)ImGuiKey.PageDown] = (int)Keys.PageDown; + io.KeyMap[(int)ImGuiKey.Home] = (int)Keys.Home; + io.KeyMap[(int)ImGuiKey.End] = (int)Keys.End; + io.KeyMap[(int)ImGuiKey.CapsLock] = (int)Keys.CapsLock; + io.KeyMap[(int)ImGuiKey.ScrollLock] = (int)Keys.ScrollLock; + io.KeyMap[(int)ImGuiKey.PrintScreen] = (int)Keys.PrintScreen; + io.KeyMap[(int)ImGuiKey.Pause] = (int)Keys.Pause; + io.KeyMap[(int)ImGuiKey.NumLock] = (int)Keys.NumLock; + io.KeyMap[(int)ImGuiKey.KeypadDivide] = (int)Keys.KeyPadDivide; + io.KeyMap[(int)ImGuiKey.KeypadMultiply] = (int)Keys.KeyPadMultiply; + io.KeyMap[(int)ImGuiKey.KeypadSubtract] = (int)Keys.KeyPadSubtract; + io.KeyMap[(int)ImGuiKey.KeypadAdd] = (int)Keys.KeyPadAdd; + io.KeyMap[(int)ImGuiKey.KeypadDecimal] = (int)Keys.KeyPadDecimal; + io.KeyMap[(int)ImGuiKey.KeypadEnter] = (int)Keys.KeyPadEnter; + io.KeyMap[(int)ImGuiKey.GraveAccent] = (int)Keys.GraveAccent; + io.KeyMap[(int)ImGuiKey.Minus] = (int)Keys.Minus; + io.KeyMap[(int)ImGuiKey.Equal] = (int)Keys.Equal; + io.KeyMap[(int)ImGuiKey.LeftBracket] = (int)Keys.LeftBracket; + io.KeyMap[(int)ImGuiKey.RightBracket] = (int)Keys.RightBracket; + io.KeyMap[(int)ImGuiKey.Semicolon] = (int)Keys.Semicolon; + io.KeyMap[(int)ImGuiKey.Apostrophe] = (int)Keys.Apostrophe; + io.KeyMap[(int)ImGuiKey.Comma] = (int)Keys.Comma; + io.KeyMap[(int)ImGuiKey.Period] = (int)Keys.Period; + io.KeyMap[(int)ImGuiKey.Slash] = (int)Keys.Slash; + io.KeyMap[(int)ImGuiKey.Backslash] = (int)Keys.Backslash; + io.KeyMap[(int)ImGuiKey.F1] = (int)Keys.F1; + io.KeyMap[(int)ImGuiKey.F2] = (int)Keys.F2; + io.KeyMap[(int)ImGuiKey.F3] = (int)Keys.F3; + io.KeyMap[(int)ImGuiKey.F4] = (int)Keys.F4; + io.KeyMap[(int)ImGuiKey.F5] = (int)Keys.F5; + io.KeyMap[(int)ImGuiKey.F6] = (int)Keys.F6; + io.KeyMap[(int)ImGuiKey.F7] = (int)Keys.F7; + io.KeyMap[(int)ImGuiKey.F8] = (int)Keys.F8; + io.KeyMap[(int)ImGuiKey.F9] = (int)Keys.F9; + io.KeyMap[(int)ImGuiKey.F10] = (int)Keys.F10; + io.KeyMap[(int)ImGuiKey.F11] = (int)Keys.F11; + io.KeyMap[(int)ImGuiKey.F12] = (int)Keys.F12; + io.KeyMap[(int)ImGuiKey.Keypad0] = (int)Keys.KeyPad0; + io.KeyMap[(int)ImGuiKey.Keypad1] = (int)Keys.KeyPad1; + io.KeyMap[(int)ImGuiKey.Keypad2] = (int)Keys.KeyPad2; + io.KeyMap[(int)ImGuiKey.Keypad3] = (int)Keys.KeyPad3; + io.KeyMap[(int)ImGuiKey.Keypad4] = (int)Keys.KeyPad4; + io.KeyMap[(int)ImGuiKey.Keypad5] = (int)Keys.KeyPad5; + io.KeyMap[(int)ImGuiKey.Keypad6] = (int)Keys.KeyPad6; + io.KeyMap[(int)ImGuiKey.Keypad7] = (int)Keys.KeyPad7; + io.KeyMap[(int)ImGuiKey.Keypad8] = (int)Keys.KeyPad8; + io.KeyMap[(int)ImGuiKey.Keypad9] = (int)Keys.KeyPad9; + io.KeyMap[(int)ImGuiKey._0] = (int)Keys.D0; + io.KeyMap[(int)ImGuiKey._1] = (int)Keys.D1; + io.KeyMap[(int)ImGuiKey._2] = (int)Keys.D2; + io.KeyMap[(int)ImGuiKey._3] = (int)Keys.D3; + io.KeyMap[(int)ImGuiKey._4] = (int)Keys.D4; + io.KeyMap[(int)ImGuiKey._5] = (int)Keys.D5; + io.KeyMap[(int)ImGuiKey._6] = (int)Keys.D6; + io.KeyMap[(int)ImGuiKey._7] = (int)Keys.D7; + io.KeyMap[(int)ImGuiKey._8] = (int)Keys.D8; + io.KeyMap[(int)ImGuiKey._9] = (int)Keys.D9; + io.KeyMap[(int)ImGuiKey.A] = (int)Keys.A; + io.KeyMap[(int)ImGuiKey.B] = (int)Keys.B; + io.KeyMap[(int)ImGuiKey.C] = (int)Keys.C; + io.KeyMap[(int)ImGuiKey.D] = (int)Keys.D; + io.KeyMap[(int)ImGuiKey.E] = (int)Keys.E; + io.KeyMap[(int)ImGuiKey.F] = (int)Keys.F; + io.KeyMap[(int)ImGuiKey.G] = (int)Keys.G; + io.KeyMap[(int)ImGuiKey.H] = (int)Keys.H; + io.KeyMap[(int)ImGuiKey.I] = (int)Keys.I; + io.KeyMap[(int)ImGuiKey.J] = (int)Keys.J; + io.KeyMap[(int)ImGuiKey.K] = (int)Keys.K; + io.KeyMap[(int)ImGuiKey.L] = (int)Keys.L; + io.KeyMap[(int)ImGuiKey.M] = (int)Keys.M; + io.KeyMap[(int)ImGuiKey.N] = (int)Keys.N; + io.KeyMap[(int)ImGuiKey.O] = (int)Keys.O; + io.KeyMap[(int)ImGuiKey.P] = (int)Keys.P; + io.KeyMap[(int)ImGuiKey.Q] = (int)Keys.Q; + io.KeyMap[(int)ImGuiKey.R] = (int)Keys.R; + io.KeyMap[(int)ImGuiKey.S] = (int)Keys.S; + io.KeyMap[(int)ImGuiKey.T] = (int)Keys.T; + io.KeyMap[(int)ImGuiKey.U] = (int)Keys.U; + io.KeyMap[(int)ImGuiKey.V] = (int)Keys.V; + io.KeyMap[(int)ImGuiKey.W] = (int)Keys.W; + io.KeyMap[(int)ImGuiKey.X] = (int)Keys.X; + io.KeyMap[(int)ImGuiKey.Y] = (int)Keys.Y; + io.KeyMap[(int)ImGuiKey.Z] = (int)Keys.Z; + } + + private void RenderImDrawData(ImDrawDataPtr draw_data) + { + if (draw_data.CmdListsCount == 0) + { + return; + } + + // Get intial state. + int prevVAO = GL.GetInteger(GetPName.VertexArrayBinding); + int prevArrayBuffer = GL.GetInteger(GetPName.ArrayBufferBinding); + int prevProgram = GL.GetInteger(GetPName.CurrentProgram); + bool prevBlendEnabled = GL.GetBoolean(GetPName.Blend); + bool prevScissorTestEnabled = GL.GetBoolean(GetPName.ScissorTest); + int prevBlendEquationRgb = GL.GetInteger(GetPName.BlendEquationRgb); + int prevBlendEquationAlpha = GL.GetInteger(GetPName.BlendEquationAlpha); + int prevBlendFuncSrcRgb = GL.GetInteger(GetPName.BlendSrcRgb); + int prevBlendFuncSrcAlpha = GL.GetInteger(GetPName.BlendSrcAlpha); + int prevBlendFuncDstRgb = GL.GetInteger(GetPName.BlendDstRgb); + int prevBlendFuncDstAlpha = GL.GetInteger(GetPName.BlendDstAlpha); + bool prevCullFaceEnabled = GL.GetBoolean(GetPName.CullFace); + bool prevDepthTestEnabled = GL.GetBoolean(GetPName.DepthTest); + int prevActiveTexture = GL.GetInteger(GetPName.ActiveTexture); + GL.ActiveTexture(TextureUnit.Texture0); + int prevTexture2D = GL.GetInteger(GetPName.TextureBinding2D); + Span prevScissorBox = stackalloc int[4]; + unsafe + { + fixed (int* iptr = &prevScissorBox[0]) + { + GL.GetInteger(GetPName.ScissorBox, iptr); + } + } + + // Bind the element buffer (thru the VAO) so that we can resize it. + GL.BindVertexArray(_vertexArray); + // Bind the vertex buffer so that we can resize it. + GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBuffer); + for (int i = 0; i < draw_data.CmdListsCount; i++) + { + ImDrawListPtr cmd_list = draw_data.CmdListsRange[i]; + + int vertexSize = cmd_list.VtxBuffer.Size * Unsafe.SizeOf(); + if (vertexSize > _vertexBufferSize) + { + int newSize = (int)Math.Max(_vertexBufferSize * 1.5f, vertexSize); + + GL.BufferData(BufferTarget.ArrayBuffer, newSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); + _vertexBufferSize = newSize; + } + + int indexSize = cmd_list.IdxBuffer.Size * sizeof(ushort); + if (indexSize > _indexBufferSize) + { + int newSize = (int)Math.Max(_indexBufferSize * 1.5f, indexSize); + GL.BufferData(BufferTarget.ElementArrayBuffer, newSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); + _indexBufferSize = newSize; + } + } + + // Setup orthographic projection matrix into our constant buffer + ImGuiIOPtr io = ImGui.GetIO(); + var mvp = OpenTK.Mathematics.Matrix4.CreateOrthographicOffCenter( + 0.0f, + io.DisplaySize.X, + io.DisplaySize.Y, + 0.0f, + -1.0f, + 1.0f); + + GL.UseProgram(_shader); + GL.UniformMatrix4(_shaderProjectionMatrixLocation, false, ref mvp); + GL.Uniform1(_shaderFontTextureLocation, 0); + CheckGLError("Projection"); + + GL.BindVertexArray(_vertexArray); + CheckGLError("VAO"); + + draw_data.ScaleClipRects(io.DisplayFramebufferScale); + + GL.Enable(EnableCap.Blend); + GL.Enable(EnableCap.ScissorTest); + GL.BlendEquation(BlendEquationMode.FuncAdd); + GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); + GL.Disable(EnableCap.CullFace); + GL.Disable(EnableCap.DepthTest); + + // Render command lists + for (int n = 0; n < draw_data.CmdListsCount; n++) + { + ImDrawListPtr cmd_list = draw_data.CmdListsRange[n]; + + GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, cmd_list.VtxBuffer.Size * Unsafe.SizeOf(), cmd_list.VtxBuffer.Data); + CheckGLError($"Data Vert {n}"); + + GL.BufferSubData(BufferTarget.ElementArrayBuffer, IntPtr.Zero, cmd_list.IdxBuffer.Size * sizeof(ushort), cmd_list.IdxBuffer.Data); + CheckGLError($"Data Idx {n}"); + + for (int cmd_i = 0; cmd_i < cmd_list.CmdBuffer.Size; cmd_i++) + { + ImDrawCmdPtr pcmd = cmd_list.CmdBuffer[cmd_i]; + if (pcmd.UserCallback != IntPtr.Zero) + { + throw new NotImplementedException(); + } + else + { + GL.ActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, (int)pcmd.TextureId); + CheckGLError("Texture"); + + // We do _windowHeight - (int)clip.W instead of (int)clip.Y because gl has flipped Y when it comes to these coordinates + var clip = pcmd.ClipRect; + GL.Scissor((int)clip.X, _windowHeight - (int)clip.W, (int)(clip.Z - clip.X), (int)(clip.W - clip.Y)); + CheckGLError("Scissor"); + + if ((io.BackendFlags & ImGuiBackendFlags.RendererHasVtxOffset) != 0) + { + GL.DrawElementsBaseVertex(PrimitiveType.Triangles, (int)pcmd.ElemCount, DrawElementsType.UnsignedShort, (IntPtr)(pcmd.IdxOffset * sizeof(ushort)), unchecked((int)pcmd.VtxOffset)); + } + else + { + GL.DrawElements(BeginMode.Triangles, (int)pcmd.ElemCount, DrawElementsType.UnsignedShort, (int)pcmd.IdxOffset * sizeof(ushort)); + } + CheckGLError("Draw"); + } + } + } + + GL.Disable(EnableCap.Blend); + GL.Disable(EnableCap.ScissorTest); + + // Reset state + GL.BindTexture(TextureTarget.Texture2D, prevTexture2D); + GL.ActiveTexture((TextureUnit)prevActiveTexture); + GL.UseProgram(prevProgram); + GL.BindVertexArray(prevVAO); + GL.Scissor(prevScissorBox[0], prevScissorBox[1], prevScissorBox[2], prevScissorBox[3]); + GL.BindBuffer(BufferTarget.ArrayBuffer, prevArrayBuffer); + GL.BlendEquationSeparate((BlendEquationMode)prevBlendEquationRgb, (BlendEquationMode)prevBlendEquationAlpha); + GL.BlendFuncSeparate( + (BlendingFactorSrc)prevBlendFuncSrcRgb, + (BlendingFactorDest)prevBlendFuncDstRgb, + (BlendingFactorSrc)prevBlendFuncSrcAlpha, + (BlendingFactorDest)prevBlendFuncDstAlpha); + if (prevBlendEnabled) GL.Enable(EnableCap.Blend); else GL.Disable(EnableCap.Blend); + if (prevDepthTestEnabled) GL.Enable(EnableCap.DepthTest); else GL.Disable(EnableCap.DepthTest); + if (prevCullFaceEnabled) GL.Enable(EnableCap.CullFace); else GL.Disable(EnableCap.CullFace); + if (prevScissorTestEnabled) GL.Enable(EnableCap.ScissorTest); else GL.Disable(EnableCap.ScissorTest); + } + + /// + /// Frees all graphics resources used by the renderer. + /// + public void Dispose() + { + GL.DeleteVertexArray(_vertexArray); + GL.DeleteBuffer(_vertexBuffer); + GL.DeleteBuffer(_indexBuffer); + + GL.DeleteTexture(_fontTexture); + GL.DeleteProgram(_shader); + } + + public static void LabelObject(ObjectLabelIdentifier objLabelIdent, int glObject, string name) + { + if (KHRDebugAvailable) + GL.ObjectLabel(objLabelIdent, glObject, name.Length, name); + } + + static bool IsExtensionSupported(string name) + { + int n = GL.GetInteger(GetPName.NumExtensions); + for (int i = 0; i < n; i++) + { + string extension = GL.GetString(StringNameIndexed.Extensions, i); + if (extension == name) return true; + } + + return false; + } + + public static int CreateProgram(string name, string vertexSource, string fragmentSoruce) + { + int program = GL.CreateProgram(); + LabelObject(ObjectLabelIdentifier.Program, program, $"Program: {name}"); + + int vertex = CompileShader(name, ShaderType.VertexShader, vertexSource); + int fragment = CompileShader(name, ShaderType.FragmentShader, fragmentSoruce); + + GL.AttachShader(program, vertex); + GL.AttachShader(program, fragment); + + GL.LinkProgram(program); + + GL.GetProgram(program, GetProgramParameterName.LinkStatus, out int success); + if (success == 0) + { + string info = GL.GetProgramInfoLog(program); + Debug.WriteLine($"GL.LinkProgram had info log [{name}]:\n{info}"); + } + + GL.DetachShader(program, vertex); + GL.DetachShader(program, fragment); + + GL.DeleteShader(vertex); + GL.DeleteShader(fragment); + + return program; + } + + private static int CompileShader(string name, ShaderType type, string source) + { + int shader = GL.CreateShader(type); + LabelObject(ObjectLabelIdentifier.Shader, shader, $"Shader: {name}"); + + GL.ShaderSource(shader, source); + GL.CompileShader(shader); + + GL.GetShader(shader, ShaderParameter.CompileStatus, out int success); + if (success == 0) + { + string info = GL.GetShaderInfoLog(shader); + Debug.WriteLine($"GL.CompileShader for shader '{name}' [{type}] had info log:\n{info}"); + } + + return shader; + } + + public static void CheckGLError(string title) + { + ErrorCode error; + int i = 1; + while ((error = GL.GetError()) != ErrorCode.NoError) + { + Debug.Print($"{title} ({i++}): {error}"); + } + } +} diff --git a/FModel/Helper.cs b/FModel/Helper.cs index 1f4e55e4..a44a3d09 100644 --- a/FModel/Helper.cs +++ b/FModel/Helper.cs @@ -100,4 +100,14 @@ public static class Helper var d = (Math.Abs(d1) + Math.Abs(d2) + 10) * 1.0e-15; return -d < n && d > n; } + + public static float DegreesToRadians(float degrees) + { + return MathF.PI / 180f * degrees; + } + + public static float RadiansToDegrees(float radians) + { + return radians* 180f / MathF.PI; + } } diff --git a/FModel/MainWindow.xaml b/FModel/MainWindow.xaml index b826b2cd..8306f23c 100644 --- a/FModel/MainWindow.xaml +++ b/FModel/MainWindow.xaml @@ -46,7 +46,7 @@ - + @@ -55,7 +55,7 @@ - + @@ -64,7 +64,7 @@ - + @@ -75,7 +75,7 @@ - + @@ -84,7 +84,7 @@ - + @@ -159,6 +159,15 @@ + + + + + + + + + @@ -168,7 +177,7 @@ - + @@ -272,7 +281,7 @@ - @@ -281,7 +290,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FModel/Views/ModelViewer.xaml.cs b/FModel/Views/ModelViewer.xaml.cs deleted file mode 100644 index 07d6a2bd..00000000 --- a/FModel/Views/ModelViewer.xaml.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.ComponentModel; -using System.Windows; -using System.Windows.Input; -using AdonisUI.Controls; -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Assets.Exports.Material; -using FModel.Services; -using FModel.ViewModels; -using HelixToolkit.Wpf.SharpDX; -using MessageBox = AdonisUI.Controls.MessageBox; -using MessageBoxImage = AdonisUI.Controls.MessageBoxImage; - -namespace FModel.Views; - -public partial class ModelViewer -{ - private bool _messageShown; - private ApplicationViewModel _applicationView => ApplicationService.ApplicationView; - - public ModelViewer() - { - DataContext = _applicationView; - InitializeComponent(); - } - - public void Load(UObject export) => _applicationView.ModelViewer.LoadExport(export); - public void Overwrite(UMaterialInstance materialInstance) - { - if (_applicationView.ModelViewer.TryOverwriteMaterial(materialInstance)) - { - _applicationView.CUE4Parse.ModelIsOverwritingMaterial = false; - } - else - { - MessageBox.Show(new MessageBoxModel - { - Text = "An attempt to load a material failed.", - Caption = "Error", - Icon = MessageBoxImage.Error, - Buttons = MessageBoxButtons.OkCancel(), - IsSoundEnabled = false - }); - } - } - - private void OnClosing(object sender, CancelEventArgs e) - { - _applicationView.ModelViewer.Clear(); - _applicationView.ModelViewer.AppendMode = false; - _applicationView.CUE4Parse.ModelIsOverwritingMaterial = false; - MyAntiCrashGroup.ItemsSource = null; // <3 - } - - private async void OnWindowKeyDown(object sender, KeyEventArgs e) - { - switch (e.Key) - { - case Key.W: - _applicationView.ModelViewer.WirefreameToggle(); - break; - case Key.H: - _applicationView.ModelViewer.RenderingToggle(); - break; - case Key.D: - _applicationView.ModelViewer.DiffuseOnlyToggle(); - break; - case Key.M: - _applicationView.ModelViewer.MaterialColorToggle(); - break; - case Key.Decimal: - _applicationView.ModelViewer.FocusOnSelectedMesh(); - break; - case Key.S when Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && Keyboard.Modifiers.HasFlag(ModifierKeys.Shift): - _applicationView.ModelViewer.SaveAsScene(); - break; - case Key.S when Keyboard.Modifiers.HasFlag(ModifierKeys.Control): - await _applicationView.ModelViewer.SaveLoadedModels(); - break; - } - } - - private void OnMouse3DDown(object sender, MouseDown3DEventArgs e) - { - if (!Keyboard.Modifiers.HasFlag(ModifierKeys.Shift) || e.HitTestResult.ModelHit is not CustomMeshGeometryModel3D m) return; - _applicationView.ModelViewer.SelectedModel.SelectedGeometry = m; - MaterialsListName.ScrollIntoView(m); - } - - private void OnFocusClick(object sender, RoutedEventArgs e) - => _applicationView.ModelViewer.FocusOnSelectedMesh(); - - private void OnCopyClick(object sender, RoutedEventArgs e) - => _applicationView.ModelViewer.CopySelectedMaterialName(); - - private async void Save(object sender, RoutedEventArgs e) - => await _applicationView.ModelViewer.SaveLoadedModels(); - - private void OnOverwriteMaterialClick(object sender, RoutedEventArgs e) - { - _applicationView.CUE4Parse.ModelIsOverwritingMaterial = true; - if (!_messageShown) - { - MessageBox.Show(new MessageBoxModel - { - Text = "Simply extract a material once FModel will be brought to the foreground. This message will be shown once per Model Viewer's lifetime, close it to begin.", - Caption = "How To Overwrite Material?", - Icon = MessageBoxImage.Information, - IsSoundEnabled = false - }); - _messageShown = true; - } - - MainWindow.YesWeCats.Activate(); - } -} \ No newline at end of file diff --git a/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs b/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs index d3a8a91a..dd323ca6 100644 --- a/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs +++ b/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs @@ -75,8 +75,8 @@ public class GamePathVisualLineText : VisualLineText } else { - await _threadWorkerView.Begin(_ => - _applicationView.CUE4Parse.ExtractAndScroll(fullPath, obj)); + await _threadWorkerView.Begin(cancellationToken => + _applicationView.CUE4Parse.ExtractAndScroll(cancellationToken, fullPath, obj)); } }; return a; diff --git a/FModel/Views/Resources/Converters/BoolToFillModeConverter.cs b/FModel/Views/Resources/Converters/BoolToFillModeConverter.cs deleted file mode 100644 index 3a85a8c7..00000000 --- a/FModel/Views/Resources/Converters/BoolToFillModeConverter.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; -using SharpDX.Direct3D11; - -namespace FModel.Views.Resources.Converters; - -public class BoolToFillModeConverter : IValueConverter -{ - public static readonly BoolToFillModeConverter Instance = new(); - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value switch - { - FillMode.Solid => true, - _ => false - }; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value switch - { - true => FillMode.Solid, - _ => FillMode.Wireframe - }; - } -} \ No newline at end of file diff --git a/FModel/Views/Resources/Converters/DateTimeToStringConverter.cs b/FModel/Views/Resources/Converters/DateTimeToStringConverter.cs deleted file mode 100644 index 1d525940..00000000 --- a/FModel/Views/Resources/Converters/DateTimeToStringConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; - -namespace FModel.Views.Resources.Converters; - -public class DateTimeToStringConverter : IValueConverter -{ - public static readonly DateTimeToStringConverter Instance = new(); - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value is not DateTime dateTime ? value : $"{dateTime.ToLongDateString()}, {dateTime.ToShortTimeString()}"; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/FModel/Views/Resources/Converters/StringToGameConverter.cs b/FModel/Views/Resources/Converters/StringToGameConverter.cs index 1b1b6456..58f9e095 100644 --- a/FModel/Views/Resources/Converters/StringToGameConverter.cs +++ b/FModel/Views/Resources/Converters/StringToGameConverter.cs @@ -31,6 +31,7 @@ public class StringToGameConverter : IValueConverter 578080 => FGame.TslGame, 677620 => FGame.PortalWars, 1172620 => FGame.Athena, + 1665460 => FGame.eFootball, _ => FGame.Unknown }; return ret == FGame.Unknown ? value : ret.GetDescription(); diff --git a/FModel/Views/Resources/Converters/TagToColorConverter.cs b/FModel/Views/Resources/Converters/TagToColorConverter.cs deleted file mode 100644 index ae0d4cfd..00000000 --- a/FModel/Views/Resources/Converters/TagToColorConverter.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; -using System.Windows.Media; -using HelixToolkit.Wpf.SharpDX; - -namespace FModel.Views.Resources.Converters; - -public class TagToColorConverter : IValueConverter -{ - public static readonly TagToColorConverter Instance = new(); - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is not PBRMaterial material) - return new SolidColorBrush(Colors.Red); - - return new SolidColorBrush(Color.FromScRgb( - material.AlbedoColor.Alpha, material.AlbedoColor.Red, - material.AlbedoColor.Green, material.AlbedoColor.Blue)); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/FModel/Views/Resources/Resources.xaml b/FModel/Views/Resources/Resources.xaml index 984f2454..a08001b4 100644 --- a/FModel/Views/Resources/Resources.xaml +++ b/FModel/Views/Resources/Resources.xaml @@ -4,7 +4,6 @@ xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:controls="clr-namespace:FModel.Views.Resources.Controls" xmlns:soundOut="clr-namespace:CSCore.SoundOut;assembly=CSCore" - xmlns:sharpDx="clr-namespace:SharpDX.Direct3D11;assembly=SharpDX.Direct3D11" xmlns:audioControls="clr-namespace:FModel.Views.Resources.Controls.Aup" xmlns:converters="clr-namespace:FModel.Views.Resources.Converters" xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit" @@ -65,7 +64,7 @@ M8.12 19.3c.39.39 1.02.39 1.41 0L12 16.83l2.47 2.47c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41l-3.17-3.17c-.39-.39-1.02-.39-1.41 0l-3.17 3.17c-.4.38-.4 1.02-.01 1.41zm7.76-14.6c-.39-.39-1.02-.39-1.41 0L12 7.17 9.53 4.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.03 0 1.42l3.17 3.17c.39.39 1.02.39 1.41 0l3.17-3.17c.4-.39.4-1.03.01-1.42z M12 5.83l2.46 2.46c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L12.7 3.7c-.39-.39-1.02-.39-1.41 0L8.12 6.88c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 5.83zm0 12.34l-2.46-2.46c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41l3.17 3.18c.39.39 1.02.39 1.41 0l3.17-3.17c.39-.39.39-1.02 0-1.41-.39-.39-1.02-.39-1.41 0L12 18.17z M11.71,17.99C8.53,17.84,6,15.22,6,12c0-3.31,2.69-6,6-6c3.22,0,5.84,2.53,5.99,5.71l-2.1-0.63C15.48,9.31,13.89,8,12,8 c-2.21,0-4,1.79-4,4c0,1.89,1.31,3.48,3.08,3.89L11.71,17.99z M22,12c0,0.3-0.01,0.6-0.04,0.9l-1.97-0.59C20,12.21,20,12.1,20,12 c0-4.42-3.58-8-8-8s-8,3.58-8,8s3.58,8,8,8c0.1,0,0.21,0,0.31-0.01l0.59,1.97C12.6,21.99,12.3,22,12,22C6.48,22,2,17.52,2,12 C2,6.48,6.48,2,12,2S22,6.48,22,12z M18.23,16.26l2.27-0.76c0.46-0.15,0.45-0.81-0.01-0.95l-7.6-2.28 c-0.38-0.11-0.74,0.24-0.62,0.62l2.28,7.6c0.14,0.47,0.8,0.48,0.95,0.01l0.76-2.27l3.91,3.91c0.2,0.2,0.51,0.2,0.71,0l1.27-1.27 c0.2-0.2,0.2-0.51,0-0.71L18.23,16.26z - M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm8 7h-5v12c0 .55-.45 1-1 1s-1-.45-1-1v-5h-2v5c0 .55-.45 1-1 1s-1-.45-1-1V9H4c-.55 0-1-.45-1-1s.45-1 1-1h16c.55 0 1 .45 1 1s-.45 1-1 1z + M1.8 6q-.525 0-.887-.35Q.55 5.3.55 4.8V4q0-1.425 1.012-2.438Q2.575.55 4 .55h.8q.5 0 .85.362.35.363.35.888 0 .5-.35.85T4.8 3H4q-.425 0-.712.287Q3 3.575 3 4v.8q0 .5-.35.85T1.8 6ZM4 23.45q-1.425 0-2.438-1.012Q.55 21.425.55 20v-.8q0-.5.363-.85.362-.35.887-.35.5 0 .85.35t.35.85v.8q0 .425.288.712Q3.575 21 4 21h.8q.5 0 .85.35t.35.85q0 .525-.35.887-.35.363-.85.363Zm15.2 0q-.5 0-.85-.363-.35-.362-.35-.887 0-.5.35-.85t.85-.35h.8q.425 0 .712-.288Q21 20.425 21 20v-.8q0-.5.35-.85t.85-.35q.525 0 .888.35.362.35.362.85v.8q0 1.425-1.012 2.438Q21.425 23.45 20 23.45ZM22.2 6q-.5 0-.85-.35T21 4.8V4q0-.425-.288-.713Q20.425 3 20 3h-.8q-.5 0-.85-.35T18 1.8q0-.525.35-.888.35-.362.85-.362h.8q1.425 0 2.438 1.012Q23.45 2.575 23.45 4v.8q0 .5-.362.85-.363.35-.888.35ZM12 17.35l1-.575v-4.1l3.55-2.075V9.425l-1-.575L12 10.925 8.45 8.85l-1 .575V10.6L11 12.675v4.1Zm-1.325 2.325-4.55-2.65q-.625-.35-.975-.963-.35-.612-.35-1.337V9.45q0-.725.35-1.337.35-.613.975-.963l4.55-2.65Q11.3 4.15 12 4.15t1.325.35l4.55 2.65q.625.35.975.963.35.612.35 1.337v5.275q0 .725-.35 1.337-.35.613-.975.963l-4.55 2.65q-.625.35-1.325.35t-1.325-.35Z M11 9h2v2h-2V9zm-2 2h2v2H9v-2zm4 0h2v2h-2v-2zm2-2h2v2h-2V9zM7 9h2v2H7V9zm12-6H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 18H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2zm2-7h-2v2h2v2h-2v-2h-2v2h-2v-2h-2v2H9v-2H7v2H5v-2h2v-2H5V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v5z M12 4C7 4 2.73 7.11 1 11.5 2.73 15.89 7 19 12 19s9.27-3.11 11-7.5C21.27 7.11 17 4 12 4zm0 12.5c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24zM2.71 3.16c-.39.39-.39 1.02 0 1.41l1.97 1.97C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l2.72 2.72c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L4.13 3.16c-.39-.39-1.03-.39-1.42 0zM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5zm2.97-5.33c-.15-1.4-1.25-2.49-2.64-2.64l2.64 2.64z @@ -638,91 +637,6 @@ - - - - - - - - - - - - - - - - - -