├── .media
├── Icon.ico
├── Icon.png
├── Icon.psd
├── Preview.jpg
├── console.png
├── gui_1.png
├── gui_2.png
├── gui_3.png
├── gui_4.png
├── DarkSkin.jpg
└── LightSkin.jpg
├── src
├── UnityDarkSkin.App
│ ├── Assets
│ │ └── Icon.ico
│ ├── Core
│ │ ├── ApplicationData.cs
│ │ ├── MessageHelper.cs
│ │ ├── DataManager.cs
│ │ ├── ThreadHelper.cs
│ │ ├── JsonUtility.cs
│ │ └── IOHelper.cs
│ ├── App.xaml.cs
│ ├── UnityDarkSkin.App.csproj
│ ├── Views
│ │ ├── FilesListWindow.xaml
│ │ ├── FilesListWindow.xaml.cs
│ │ ├── MainWindow.xaml
│ │ └── MainWindow.xaml.cs
│ ├── app.manifest
│ └── App.xaml
├── UnityDarkSkin.Lib
│ ├── UnityDarkSkin.Lib.csproj
│ ├── Version.cs
│ ├── Versions.cs
│ └── Patcher.cs
└── UnityDarkSkin.sln
├── LICENSE
├── readme.md
├── .github
└── workflows
│ └── build.yml
└── .gitignore
/.media/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/Icon.ico
--------------------------------------------------------------------------------
/.media/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/Icon.png
--------------------------------------------------------------------------------
/.media/Icon.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/Icon.psd
--------------------------------------------------------------------------------
/.media/Preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/Preview.jpg
--------------------------------------------------------------------------------
/.media/console.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/console.png
--------------------------------------------------------------------------------
/.media/gui_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/gui_1.png
--------------------------------------------------------------------------------
/.media/gui_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/gui_2.png
--------------------------------------------------------------------------------
/.media/gui_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/gui_3.png
--------------------------------------------------------------------------------
/.media/gui_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/gui_4.png
--------------------------------------------------------------------------------
/.media/DarkSkin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/DarkSkin.jpg
--------------------------------------------------------------------------------
/.media/LightSkin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/.media/LightSkin.jpg
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Assets/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluschenko/unity-dark-skin/HEAD/src/UnityDarkSkin.App/Assets/Icon.ico
--------------------------------------------------------------------------------
/src/UnityDarkSkin.Lib/UnityDarkSkin.Lib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Core/ApplicationData.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace UnityDarkSkin.Core
4 | {
5 | [DataContract]
6 | public class ApplicationData
7 | {
8 | [DataMember]
9 | public double WindowWidth;
10 |
11 | [DataMember]
12 | public double WindowHeight;
13 | }
14 | }
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace UnityDarkSkin.App
10 | {
11 | public partial class App : Application
12 | {
13 |
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.Lib/Version.cs:
--------------------------------------------------------------------------------
1 | namespace UnityDarkSkin.Lib
2 | {
3 | public class Version
4 | {
5 | private readonly string _name;
6 | public readonly byte[] LightBytes;
7 | public readonly byte[] DarkBytes;
8 |
9 | public Version(string name, byte[] lightBytes, byte[] darkBytes)
10 | {
11 | _name = name;
12 | LightBytes = lightBytes;
13 | DarkBytes = darkBytes;
14 | }
15 |
16 | public byte[] GetBytes(ThemeType skin)
17 | {
18 | return skin == ThemeType.Light ? LightBytes : DarkBytes;
19 | }
20 |
21 | public override string ToString()
22 | {
23 | return _name;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/UnityDarkSkin.App.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net5.0-windows
6 | true
7 | app.manifest
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Views/FilesListWindow.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 | Lorem ipsum 0
14 | Lorem ipsum 1
15 | Lorem ipsum 2
16 | Lorem ipsum 3
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Core/MessageHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 |
4 | namespace UnityDarkSkin.Core
5 | {
6 | ///
7 | /// Easy templating of System.Windows.MessageBox
8 | ///
9 | public class MessageHelper
10 | {
11 | /// Info message box
12 | public static void Alert(string text, string title = "Alert") =>
13 | MessageBox.Show(text, title, MessageBoxButton.OK, MessageBoxImage.Information);
14 |
15 | /// Warning message box
16 | public static void Warning(string text, string title = "Warning") =>
17 | MessageBox.Show(text, title, MessageBoxButton.OK, MessageBoxImage.Warning);
18 |
19 | /// Error message box
20 | public static void Error(string text, string title = "Error") =>
21 | MessageBox.Show(text, title, MessageBoxButton.OK, MessageBoxImage.Error);
22 |
23 | /// Displayes exception content
24 | public static void ThrowException(Exception exception) =>
25 | Error(exception.ToString(), exception.GetType().Name);
26 | }
27 | }
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Core/DataManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace UnityDarkSkin.Core
5 | {
6 | public class DataManager
7 | {
8 | private string Path { get; set; }
9 |
10 | public DataManager(string path)
11 | {
12 | Path = path;
13 | }
14 |
15 | public T Load(Action onError)
16 | {
17 | if (File.Exists(Path))
18 | {
19 | try
20 | {
21 | string data = File.ReadAllText(Path);
22 | return JsonUtility.FromJson(data);
23 | }
24 | catch (Exception ex)
25 | {
26 | onError?.Invoke(ex);
27 | }
28 | }
29 |
30 | return Activator.CreateInstance();
31 | }
32 |
33 | public void Save(T obj, Action onError)
34 | {
35 | try
36 | {
37 | string data = JsonUtility.ToJson(obj);
38 | File.WriteAllText(Path, data);
39 | }
40 | catch (Exception ex)
41 | {
42 | onError?.Invoke(ex);
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Core/ThreadHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 |
5 | namespace UnityDarkSkin.Core
6 | {
7 | // Singleton thread manager
8 | public static class ThreadHelper
9 | {
10 | private static Thread _thread;
11 | private static readonly Queue Actions = new Queue();
12 |
13 | private static void Init()
14 | {
15 | if (_thread == null)
16 | {
17 | _thread = new Thread(ThreadProc) {IsBackground = true};
18 | _thread.Start();
19 | }
20 | }
21 |
22 | private static void ThreadProc()
23 | {
24 | while (true)
25 | {
26 | if (Actions.Count > 0)
27 | {
28 | Actions.Dequeue()?.Invoke();
29 | }
30 | else
31 | {
32 | Thread.Sleep(1);
33 | }
34 | }
35 | }
36 |
37 | public static void Invoke(Action action)
38 | {
39 | if (action != null)
40 | {
41 | Init();
42 | Actions.Enqueue(action);
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Views/FilesListWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 |
5 | /*
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 | using System.Windows.Data;
12 | using System.Windows.Documents;
13 | using System.Windows.Input;
14 | using System.Windows.Media;
15 | using System.Windows.Media.Imaging;
16 | using System.Windows.Shapes;
17 | */
18 |
19 | namespace UnityDarkSkin.App
20 | {
21 | public partial class FilesListWindow : Window
22 | {
23 | public FilesListWindow(MainWindow Main, string[] files, Action onSelect)
24 | {
25 | InitializeComponent();
26 |
27 | FilesList.Children.Clear();
28 | foreach (string file in files)
29 | {
30 | ComboBoxItem item = new ComboBoxItem() { Content = file, ToolTip = file };
31 | item.MouseDoubleClick += (sender, args) => {
32 | onSelect?.Invoke(file);
33 | //
34 | Close();
35 | Main.Focus();
36 | };
37 |
38 | FilesList.Children.Add(item);
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Core/JsonUtility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using System.Runtime.Serialization.Json;
5 |
6 | namespace UnityDarkSkin.Core
7 | {
8 | public class JsonUtility
9 | {
10 | public static string ToJson(object obj)
11 | {
12 | if (obj == null)
13 | return "";
14 |
15 | var jsonFormatter = new DataContractJsonSerializer(obj.GetType());
16 |
17 | using var stream = new MemoryStream();
18 | jsonFormatter.WriteObject(stream, obj);
19 | return Encoding.UTF8.GetString(stream.ToArray());
20 | }
21 |
22 | public static object FromJson(string json, Type type)
23 | {
24 | if (string.IsNullOrEmpty(json))
25 | return null;
26 | if (type == null)
27 | throw new ArgumentNullException(nameof(type));
28 |
29 | using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
30 | DataContractJsonSerializer jsonFormatter = new DataContractJsonSerializer(type);
31 | return jsonFormatter.ReadObject(stream);
32 | }
33 |
34 | public static T FromJson(string json)
35 | {
36 | return (T) FromJson(json, typeof(T));
37 | }
38 |
39 | }
40 | }
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # UnityDarkSkin
2 |
3 | 
4 |
5 | [](https://github.com/Gluschenko/UnityDarkSkin/actions/workflows/build.yml)
6 |
7 | ## About
8 |
9 | This tool makes Dark Theme in Unity Editor and turns it back to 'terrible' Light Theme, if you wish. It's being useful for Unity Personal, where theme settings are disabled by default. **It is not a crack! It's simply changing a couple of bits in Editor.exe**
10 |
11 | ## Usage
12 |
13 | 1. Compile `UnityDarkSkin.App` with Visual Studio
14 | 2. Run an executable file as **Administrator**
15 |
16 | ## Projects
17 |
18 | | Project | .NET version | Role | Description |
19 | | --- | --- | --- | --- |
20 | | UnityDarkSkin.App | Core 5.0 | WPF application | Advanced functionality via UI |
21 | | UnityDarkSkin.Lib | Core 5.0 | Patcher library | Versions data (byte records) |
22 |
23 | ## Requirements
24 |
25 | * Windows 10 (on newer)
26 | * Visual Studio 2019 (or newer)
27 | * .NET 5.0 SDK
28 |
29 | ## Supported versions
30 |
31 | | Version | Status | Tested on |
32 | | :--- | :---: | :--- |
33 | | 5.3 | ✅ | 5.3.5f1 |
34 | | 5.4 | ✅ | 5.4 |
35 | | 2017.2 | ✅ | 2017.2 |
36 | | 2018.2 | ✅ | 2018.2 |
37 | | 2018.3 | ✅ | 2018.3.0f2 |
38 | | 2018.4 | ✅ | 2018.4.24f1 (LTS) |
39 | | 2019.1 | ✅ | 2019.1.0f2 |
40 | | 2019.2 | ✅ | 2019.2.0f1, 2019.2.14f1 |
41 | | 2019.3 | ✅ | 2019.3.0f1 |
42 | | 2020.1 | ✅ | 2020.1.0f1 |
43 |
44 | ✅ - Supported | ⚠️ - Work in progress | ❌ - Not supported
45 |
46 | ## How it works
47 |
48 | | Before | After |
49 | | :---: | :---: |
50 | |  |  |
51 |
52 | ## Showcase
53 |
54 | | UnityDarkSkin.App | UnityDarkSkin |
55 | | :---: | :---: |
56 | |  |  |
57 | |  | |
58 | |  | |
59 | |  | |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.28729.10
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnityDarkSkin.App", "UnityDarkSkin.App\UnityDarkSkin.App.csproj", "{9D3C51BB-1491-407B-94DE-8F0916DA34FC}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnityDarkSkin.Lib", "UnityDarkSkin.Lib\UnityDarkSkin.Lib.csproj", "{7BD8B9A9-3504-4481-953D-D9A43EFDE3D4}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docs", ".docs", "{17471794-99F6-4302-8F16-88F899C9B25F}"
11 | ProjectSection(SolutionItems) = preProject
12 | ..\readme.md = ..\readme.md
13 | EndProjectSection
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".deploy", ".deploy", "{FCA0ED07-3A9D-4258-BE03-8B79602F8587}"
16 | ProjectSection(SolutionItems) = preProject
17 | ..\.github\workflows\build.yml = ..\.github\workflows\build.yml
18 | EndProjectSection
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {9D3C51BB-1491-407B-94DE-8F0916DA34FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {9D3C51BB-1491-407B-94DE-8F0916DA34FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {9D3C51BB-1491-407B-94DE-8F0916DA34FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {9D3C51BB-1491-407B-94DE-8F0916DA34FC}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {7BD8B9A9-3504-4481-953D-D9A43EFDE3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {7BD8B9A9-3504-4481-953D-D9A43EFDE3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {7BD8B9A9-3504-4481-953D-D9A43EFDE3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {7BD8B9A9-3504-4481-953D-D9A43EFDE3D4}.Release|Any CPU.Build.0 = Release|Any CPU
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | GlobalSection(ExtensibilityGlobals) = postSolution
39 | SolutionGuid = {AC86378C-BEF8-4CEC-AA0A-2AE818DB23D5}
40 | EndGlobalSection
41 | EndGlobal
42 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: .NET Core Desktop
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build:
12 | runs-on: windows-latest
13 | env:
14 | ROOT: ./src
15 |
16 | steps:
17 | - uses: actions/checkout@v2
18 | - name: 📂 Setup .NET Core
19 | uses: actions/setup-dotnet@v1
20 | with:
21 | dotnet-version: 5.0.x
22 |
23 | - name: 📂 Files
24 | working-directory: ${{env.ROOT}}
25 | run: ls -R
26 |
27 | - name: 🚀 Building UnityDarkSkin.App
28 | working-directory: ${{env.ROOT}}/UnityDarkSkin.App
29 | run: dotnet publish -c RELEASE -o out
30 |
31 | - uses: actions/upload-artifact@v2
32 | with:
33 | name: builds
34 | path: ${{env.ROOT}}/**/out
35 | retention-days: 1
36 |
37 | deploy:
38 |
39 | runs-on: ubuntu-latest
40 | needs: build
41 | if: github.ref == 'refs/heads/master'
42 | env:
43 | ROOT: ./src
44 | NUGET_AUTH_TOKEN: ${{secrets.token}}
45 |
46 | steps:
47 | - uses: actions/checkout@v2
48 |
49 | - uses: actions/download-artifact@v2
50 | with:
51 | name: builds
52 | path: ${{env.ROOT}}
53 |
54 | - name: 📂 Setup .NET Core
55 | uses: actions/setup-dotnet@v1
56 | with:
57 | dotnet-version: 5.0.x
58 |
59 | - name: 📂 Pack UnityDarkSkin.App
60 | working-directory: ${{env.ROOT}}/UnityDarkSkin.App/out
61 | run: |
62 | sudo apt-get update
63 | sudo apt-get install zip
64 | zip -r -9 ./UnityDarkSkin.App.zip ./*
65 |
66 | - name: 📂 Files
67 | working-directory: ${{env.ROOT}}
68 | run: ls -R
69 |
70 | - name: Get current date
71 | id: date
72 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
73 |
74 | - name: Create Draft Release
75 | id: create_release
76 | uses: actions/create-release@v1
77 | env:
78 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
79 | with:
80 | tag_name: ${{ steps.date.outputs.date }}
81 | release_name: Shapshot ${{ steps.date.outputs.date }}
82 | draft: true
83 | prerelease: false
84 |
85 | - uses: actions/upload-release-asset@v1.0.1
86 | env:
87 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
88 | with:
89 | upload_url: ${{ steps.create_release.outputs.upload_url }}
90 | asset_path: ./src/UnityDarkSkin.App/out/UnityDarkSkin.App.zip
91 | asset_name: UnityDarkSkin.App.zip
92 | asset_content_type: application/zip
93 |
94 | - uses: eregon/publish-release@v1
95 | env:
96 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
97 | with:
98 | release_id: ${{ steps.create_release.outputs.id }}
99 |
100 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Core/IOHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using Microsoft.WindowsAPICodePack.Dialogs;
6 |
7 | namespace UnityDarkSkin.Core
8 | {
9 | public class IOHelper
10 | {
11 | public static void OpenFolderDialog(string initialDirectory, Action onDone = null, Action onCancel = null)
12 | {
13 | using var dialog = new CommonOpenFileDialog { InitialDirectory = initialDirectory, IsFolderPicker = true };
14 | OpenDialog(dialog, onDone, onCancel);
15 | }
16 |
17 | public static void OpenFileDialog(string initialDirectory, Action onDone = null, Action onCancel = null)
18 | {
19 | using var dialog = new CommonOpenFileDialog { InitialDirectory = initialDirectory };
20 | OpenDialog(dialog, onDone, onCancel);
21 | }
22 |
23 | public static void OpenDialog(CommonOpenFileDialog dialog, Action onDone, Action onCancel = null)
24 | {
25 | var result = dialog.ShowDialog();
26 |
27 | switch (result)
28 | {
29 | case CommonFileDialogResult.Ok:
30 | onDone?.Invoke(dialog.FileName);
31 | break;
32 | case CommonFileDialogResult.Cancel:
33 | onCancel?.Invoke();
34 | break;
35 | }
36 | }
37 |
38 | //
39 |
40 | public static string[] SearchFile(string directory, string fileName, bool recursive = true, bool containsName = false)
41 | {
42 | var files = new List();
43 | InternalSearchFile(ref files, directory, fileName, recursive, containsName);
44 | return files.ToArray();
45 | }
46 |
47 | private static void InternalSearchFile(ref List files, string directory, string fileName, bool recursive, bool containsName)
48 | {
49 | if (Directory.Exists(directory))
50 | {
51 | var subDirs = Array.Empty();
52 | var matchingFiles = Array.Empty();
53 |
54 | try
55 | {
56 | subDirs = Directory.GetDirectories(directory);
57 | //
58 | Func search;
59 | if (containsName)
60 | search = (p) => Path.GetFileName(p).Contains(fileName);
61 | else
62 | search = (p) => Path.GetFileName(p).Equals(fileName);
63 |
64 | matchingFiles = Directory.GetFiles(directory).Where(search).ToArray();
65 | files.AddRange(matchingFiles);
66 | }
67 | catch
68 | {
69 | // Don't care
70 | }
71 | //
72 |
73 | if (recursive)
74 | {
75 | foreach (string dir in subDirs)
76 | {
77 | InternalSearchFile(ref files, dir, fileName, recursive, containsName);
78 | }
79 | }
80 | }
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
53 |
60 |
61 |
62 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Views/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
47 |
51 |
52 |
53 |
54 |
56 |
58 |
59 |
60 |
61 |
65 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.Lib/Versions.cs:
--------------------------------------------------------------------------------
1 | namespace UnityDarkSkin.Lib
2 | {
3 | public static class Versions
4 | {
5 | ///
6 | /// Contains all known byte records for patching
7 | ///
8 | static readonly Version[] versions = new Version[]
9 | {
10 | new Version(
11 | name: "2018.2 & older (x86)",
12 | lightBytes: new byte[] {0x75, 0x04, 0x33, 0xC0, 0x5E, 0xC3, 0x8B, 0x06, 0x5E, 0xC3, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC},
13 | darkBytes: new byte[] {0x74, 0x04, 0x33, 0xC0, 0x5E, 0xC3, 0x8B, 0x06, 0x5E, 0xC3, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC}
14 | ),
15 | new Version(
16 | name: "2018.2 & older (x64)",
17 | lightBytes: new byte[] {0x75, 0x08, 0x33, 0xC0, 0x48, 0x83, 0xC4, 0x20, 0x5B, 0xC3, 0x8B, 0x03, 0x48, 0x83, 0xC4, 0x20, 0x5B, 0xC3},
18 | darkBytes: new byte[] {0x74, 0x08, 0x33, 0xC0, 0x48, 0x83, 0xC4, 0x20, 0x5B, 0xC3, 0x8B, 0x03, 0x48, 0x83, 0xC4, 0x20, 0x5B, 0xC3}
19 | ),
20 | new Version(
21 | name: "2018.3",
22 | lightBytes: new byte[] {0x75, 0x08, 0x33, 0xC0, 0x48, 0x83, 0xC4, 0x30, 0x5B, 0xC3, 0x8B, 0x03, 0x48, 0x83, 0xC4, 0x30},
23 | darkBytes: new byte[] {0x74, 0x08, 0x33, 0xC0, 0x48, 0x83, 0xC4, 0x30, 0x5B, 0xC3, 0x8B, 0x03, 0x48, 0x83, 0xC4, 0x30}
24 | ),
25 | new Version(
26 | name: "2018.4",
27 | lightBytes: new byte[] {0x74, 0x04, 0x33, 0xC0, 0xEB, 0x02, 0x8B, 0x03, 0x48, 0x8B, 0x4C, 0x24, 0x58, 0x48},
28 | darkBytes: new byte[] {0x75, 0x04, 0x33, 0xC0, 0xEB, 0x02, 0x8B, 0x03, 0x48, 0x8B, 0x4C, 0x24, 0x58, 0x48}
29 | ),
30 | new Version(
31 | name: "2019.1",
32 | lightBytes: new byte[] {0x74, 0x04, 0x33, 0xC0, 0xEB, 0x02, 0x8B, 0x07},
33 | darkBytes: new byte[] {0x75, 0x04, 0x33, 0xC0, 0xEB, 0x02, 0x8B, 0x07}
34 | ),
35 | new Version(
36 | name: "2019.2",
37 | lightBytes: new byte[] {0x75, 0x15, 0x33, 0xC0, 0xEB, 0x13, 0x90, 0x49},
38 | darkBytes: new byte[] {0x74, 0x15, 0x33, 0xC0, 0xEB, 0x13, 0x90, 0x49}
39 | ),
40 |
41 | /*
42 | Orig:
43 | 00007FF7188778EA 39 05 0C 1F 9A 06 7F 36 80 3D FF 1E 9A 06 00 75
44 | 00007FF7188778FA 15 33 C0 EB 13 90 49 FF C0 42 80 3C 03 00 0F 84
45 | Mod:
46 | 00007FF7188778EA 3B C0 90 90 90 90 7F 36 80 3D FF 1E 9A 06 00 74
47 | 00007FF7188778FA 15 33 C0 EB 13 90 49 FF C0 42 80 3C 03 00 0F 84
48 | */
49 | new Version(
50 | name: "2019.2.14",
51 | lightBytes: new byte[]
52 | {
53 | 0x39, 0x05, 0x0C, 0x1F, 0x9A, 0x06, 0x7F, 0x36, 0x80, 0x3D, 0xFF, 0x1E, 0x9A, 0x06, 0x00, 0x75,
54 | 0x15, 0x33, 0xC0, 0xEB, 0x13, 0x90, 0x49, 0xFF, 0xC0, 0x42, 0x80, 0x3C, 0x03, 0x00, 0x0F, 0x84
55 | },
56 | darkBytes: new byte[]
57 | {
58 | 0x3B, 0xC0, 0x90, 0x90, 0x90, 0x90, 0x7F, 0x36, 0x80, 0x3D, 0xFF, 0x1E, 0x9A, 0x06, 0x00, 0x74,
59 | 0x15, 0x33, 0xC0, 0xEB, 0x13, 0x90, 0x49, 0xFF, 0xC0, 0x42, 0x80, 0x3C, 0x03, 0x00, 0x0F, 0x84
60 | }
61 | ),
62 |
63 | /*
64 | Orig:
65 | 00007FF6D1447FB9 75 15 33 C0 EB 13 90 49 FF C0 42 80 3C 03 00 0F
66 | Mod:
67 | 00007FF6D1447FB9 74 15 33 C0 EB 13 90 49 FF C0 42 80 3C 03 00 0F
68 | */
69 | new Version(
70 | name: "2019.3",
71 | lightBytes: new byte[] { 0x75, 0x15, 0x33, 0xC0, 0xEB, 0x13, 0x90, 0x49, 0xFF, 0xC0, 0x42, 0x80, 0x3C, 0x03, 0x00, 0x0F },
72 | darkBytes: new byte[] { 0x74, 0x15, 0x33, 0xC0, 0xEB, 0x13, 0x90, 0x49, 0xFF, 0xC0, 0x42, 0x80, 0x3C, 0x03, 0x00, 0x0F }
73 | ),
74 |
75 | /*
76 | Orig:
77 | 022B 433C: 75 14 33 C0 48 83 C4 40 5B C3 48 FF C2 41 80 3C
78 | Mod:
79 | 022B 433C: 74 14 33 C0 48 83 C4 40 5B C3 48 FF C2 41 80 3C
80 | */
81 | new Version (
82 | name: "2020.1 (beta)",
83 | lightBytes: new byte[] { 0x75, 0x14, 0x33, 0xC0, 0x48, 0x83, 0xC4, 0x40, 0x5B, 0xC3, 0x48, 0xFF, 0xC2, 0x41, 0x80, 0x3C },
84 | darkBytes: new byte[] { 0x74, 0x14, 0x33, 0xC0, 0x48, 0x83, 0xC4, 0x40, 0x5B, 0xC3, 0x48, 0xFF, 0xC2, 0x41, 0x80, 0x3C }
85 | ),
86 |
87 | /*
88 | Orig:
89 | 59 F5 05 7F 25 80 3D E4 59 F5 05 00 75 14 33 C0
90 | Mod:
91 | 59 F5 05 7F 25 80 3D E4 59 F5 05 00 74 14 33 C0
92 | */
93 | new Version (
94 | name: "2020.1",
95 | lightBytes: new byte[] { 0x59, 0xF5, 0x05, 0x7F, 0x25, 0x80, 0x3D, 0xE4, 0x59, 0xF5, 0x05, 0x00, 0x75, 0x14, 0x33, 0xC0 },
96 | darkBytes: new byte[] { 0x59, 0xF5, 0x05, 0x7F, 0x25, 0x80, 0x3D, 0xE4, 0x59, 0xF5, 0x05, 0x00, 0x74, 0x14, 0x33, 0xC0 }
97 | ),
98 | };
99 |
100 | public static Version[] Get() => versions;
101 | }
102 | }
--------------------------------------------------------------------------------
/src/UnityDarkSkin.Lib/Patcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 |
5 | namespace UnityDarkSkin.Lib
6 | {
7 | public class Patcher
8 | {
9 | public string FilePath { get; }
10 | public Version CurrentVersion { get; set; }
11 | public ThemeType CurrentTheme { get; private set; }
12 |
13 | private bool IsLoaded
14 | {
15 | get => Data != null;
16 | }
17 |
18 | private byte[] Data { get; set; }
19 |
20 | private int _offset = 0;
21 |
22 | public Patcher(string path)
23 | {
24 | FilePath = path;
25 | }
26 |
27 | public void Reset()
28 | {
29 | _offset = 0;
30 | }
31 |
32 | public void Load()
33 | {
34 | if (!File.Exists(FilePath))
35 | throw new FileNotFoundException("File doesn't exist");
36 |
37 | Data = File.ReadAllBytes(FilePath);
38 | }
39 |
40 | public ThemeType SetTheme(ThemeType theme)
41 | {
42 | if (!File.Exists(FilePath))
43 | throw new FileNotFoundException("File doesn't exist");
44 |
45 | if (CurrentVersion == null)
46 | throw new InvalidOperationException("Version is not detected");
47 |
48 | if (_offset == 0)
49 | DetectTheme(CurrentVersion);
50 |
51 | var bytes = CurrentVersion.GetBytes(theme);
52 | if (_offset + bytes.Length < Data.Length)
53 | {
54 | for (int i = 0; i < bytes.Length; ++i)
55 | {
56 | Data[_offset + i] = bytes[i];
57 | }
58 | }
59 |
60 | return DetectTheme(CurrentVersion);
61 | }
62 |
63 | // Search in bytes: O(N)
64 | // Returns offset of first byte in query or -1
65 | private int Search(byte[] search, int offset = 0)
66 | {
67 | if (!IsLoaded)
68 | throw new InvalidOperationException("File is not loaded");
69 | //
70 | int num = -1;
71 | if (Data.Length > 0 &&
72 | search.Length > 0 &&
73 | offset <= Data.Length - search.Length &&
74 | Data.Length >= search.Length)
75 | {
76 | for (int i = offset; i <= Data.Length - search.Length; ++i)
77 | {
78 | if (Data[i] == search[0])
79 | {
80 | if (Data.Length > 1)
81 | {
82 | bool flag = true;
83 | for (int j = 1; j < search.Length; ++j)
84 | {
85 | if (Data[i + j] != search[j])
86 | {
87 | flag = false;
88 | break;
89 | }
90 | }
91 |
92 | if (flag)
93 | {
94 | num = i;
95 | break;
96 | }
97 | }
98 | else
99 | {
100 | num = i;
101 | break;
102 | }
103 | }
104 | }
105 | }
106 |
107 | return num;
108 | }
109 |
110 | public Version DetectVersion()
111 | {
112 | if (!IsLoaded)
113 | throw new InvalidOperationException("File is not loaded");
114 | //
115 | foreach (Version version in Versions.Get().Reverse())
116 | {
117 | int light = Search(version.LightBytes);
118 | int dark = Search(version.DarkBytes);
119 |
120 | bool isLight = light != -1;
121 | bool isDark = dark != -1;
122 |
123 | if (isLight || isDark)
124 | {
125 | _offset = Math.Max(light, dark);
126 |
127 | CurrentVersion = version;
128 | return version;
129 | }
130 | }
131 |
132 | return null;
133 | }
134 |
135 | public ThemeType DetectTheme(Version version)
136 | {
137 | bool light = Search(version.LightBytes, _offset) != -1;
138 | bool dark = Search(version.DarkBytes, _offset) != -1;
139 |
140 | if (light || dark)
141 | CurrentTheme = light ? ThemeType.Light : ThemeType.Dark;
142 | else
143 | CurrentTheme = ThemeType.None;
144 |
145 | return CurrentTheme;
146 | }
147 |
148 | public void Save()
149 | {
150 | if (!IsLoaded)
151 | throw new InvalidOperationException("File is not loaded");
152 |
153 | File.WriteAllBytes(FilePath, Data);
154 | }
155 |
156 | public void MakeBackup()
157 | {
158 | if (!IsLoaded)
159 | throw new InvalidOperationException("File is not loaded");
160 |
161 | string fileDir = Path.GetDirectoryName(FilePath);
162 | string fileName = Path.GetFileName(FilePath);
163 |
164 | DateTime date = DateTime.Now;
165 | string NewFileName = $"Backup_{date.Day}-{date.Month}-{date.Year}_{date.Hour}-{date.Minute}-{date.Second}_{fileName}";
166 | string newPath = Path.Combine(fileDir, NewFileName);
167 |
168 | File.WriteAllBytes(newPath, Data);
169 | }
170 |
171 | public void RestoreBackup(string file)
172 | {
173 | if (!IsLoaded)
174 | throw new InvalidOperationException("File is not loaded");
175 |
176 | File.Delete(FilePath);
177 | File.Move(file, FilePath);
178 |
179 | //throw new NotImplementedException();
180 | }
181 | }
182 |
183 | public enum ThemeType
184 | {
185 | None = 0,
186 | Light = 1,
187 | Dark = 2
188 | }
189 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 | # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
235 | **/wwwroot/lib/
236 |
237 | # RIA/Silverlight projects
238 | Generated_Code/
239 |
240 | # Backup & report files from converting an old project file
241 | # to a newer Visual Studio version. Backup files are not needed,
242 | # because we have git ;-)
243 | _UpgradeReport_Files/
244 | Backup*/
245 | UpgradeLog*.XML
246 | UpgradeLog*.htm
247 | ServiceFabricBackup/
248 | *.rptproj.bak
249 |
250 | # SQL Server files
251 | *.mdf
252 | *.ldf
253 | *.ndf
254 |
255 | # Business Intelligence projects
256 | *.rdl.data
257 | *.bim.layout
258 | *.bim_*.settings
259 | *.rptproj.rsuser
260 | *- Backup*.rdl
261 |
262 | # Microsoft Fakes
263 | FakesAssemblies/
264 |
265 | # GhostDoc plugin setting file
266 | *.GhostDoc.xml
267 |
268 | # Node.js Tools for Visual Studio
269 | .ntvs_analysis.dat
270 | node_modules/
271 |
272 | # Visual Studio 6 build log
273 | *.plg
274 |
275 | # Visual Studio 6 workspace options file
276 | *.opt
277 |
278 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
279 | *.vbw
280 |
281 | # Visual Studio LightSwitch build output
282 | **/*.HTMLClient/GeneratedArtifacts
283 | **/*.DesktopClient/GeneratedArtifacts
284 | **/*.DesktopClient/ModelManifest.xml
285 | **/*.Server/GeneratedArtifacts
286 | **/*.Server/ModelManifest.xml
287 | _Pvt_Extensions
288 |
289 | # Paket dependency manager
290 | .paket/paket.exe
291 | paket-files/
292 |
293 | # FAKE - F# Make
294 | .fake/
295 |
296 | # JetBrains Rider
297 | .idea/
298 | *.sln.iml
299 |
300 | # CodeRush personal settings
301 | .cr/personal
302 |
303 | # Python Tools for Visual Studio (PTVS)
304 | __pycache__/
305 | *.pyc
306 |
307 | # Cake - Uncomment if you are using it
308 | # tools/**
309 | # !tools/packages.config
310 |
311 | # Tabs Studio
312 | *.tss
313 |
314 | # Telerik's JustMock configuration file
315 | *.jmconfig
316 |
317 | # BizTalk build output
318 | *.btp.cs
319 | *.btm.cs
320 | *.odx.cs
321 | *.xsd.cs
322 |
323 | # OpenCover UI analysis results
324 | OpenCover/
325 |
326 | # Azure Stream Analytics local run output
327 | ASALocalRun/
328 |
329 | # MSBuild Binary and Structured Log
330 | *.binlog
331 |
332 | # NVidia Nsight GPU debugger configuration file
333 | *.nvuser
334 |
335 | # MFractors (Xamarin productivity tool) working folder
336 | .mfractor/
337 |
338 | # Local History for Visual Studio
339 | .localhistory/
340 |
341 | # BeatPulse healthcheck temp database
342 | healthchecksdb
343 |
344 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
345 | MigrationBackup/
346 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
36 |
37 |
40 |
41 |
42 |
77 |
78 |
82 |
83 |
87 |
88 |
89 |
90 |
91 |
118 |
119 |
120 |
121 |
122 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/src/UnityDarkSkin.App/Views/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Windows;
4 | using System.Windows.Controls;
5 | using System.Windows.Media.Effects;
6 | using UnityDarkSkin.Lib;
7 | using UnityDarkSkin.Core;
8 | using Version = UnityDarkSkin.Lib.Version;
9 |
10 | namespace UnityDarkSkin.App
11 | {
12 | public partial class MainWindow : Window
13 | {
14 | // Internal preferances management
15 | public ApplicationData Data { get; private set; } // Contains current application state (e.g. window size)
16 | public DataManager Manager { get; private set; } // Сontrols data loading
17 | private readonly string PrefsFile = "prefs.json"; // Relative path to preferances file
18 |
19 | private string StartPath = @"C:\Program Files\Unity";
20 | private const string EditorFileName = "Unity.exe";
21 |
22 | private Patcher Patcher;
23 |
24 | public enum Section
25 | {
26 | StartScreen,
27 | PatchScreen
28 | }
29 |
30 | public MainWindow()
31 | {
32 | InitializeComponent();
33 |
34 | // Application data management
35 | Manager = new DataManager(PrefsFile);
36 | LoadData();
37 | Closed += (sender, args) => SaveData();
38 |
39 | //
40 | Navigate(Section.StartScreen);
41 | //
42 |
43 | DirectoryTextBox.PreviewMouseDoubleClick += (sender, args) => ChooseDirectoryButton_Click(sender, null);
44 |
45 | // Versions combo box
46 | foreach (var version in Versions.Get())
47 | {
48 | VersionsCombo.Items.Add(version);
49 | }
50 | VersionsCombo.SelectionChanged += VersionsCombo_SelectionChanged;
51 |
52 | // Resets thumbs by default
53 | SetThemeThumbs(ThemeType.None);
54 | }
55 |
56 | // Load & Save methods
57 |
58 | public void LoadData()
59 | {
60 | Data = Manager.Load(MessageHelper.ThrowException);
61 | //
62 | if (Data.WindowWidth > 0)
63 | Width = Data.WindowWidth;
64 | if (Data.WindowHeight > 0)
65 | Height = Data.WindowHeight;
66 | }
67 |
68 | public void SaveData()
69 | {
70 | Data.WindowWidth = Width;
71 | Data.WindowHeight = Height;
72 | //
73 | Manager.Save(Data, MessageHelper.ThrowException);
74 | }
75 |
76 | // Event handlers
77 |
78 | private void OpenButton_Click(object sender, RoutedEventArgs e)
79 | {
80 | IOHelper.OpenFolderDialog(StartPath, OnFolderChosen);
81 | }
82 |
83 | private void ChooseDirectoryButton_Click(object sender, RoutedEventArgs e)
84 | {
85 | IOHelper.OpenFolderDialog(DirectoryTextBox.Text, OnFolderChosen);
86 | }
87 |
88 | private void VersionsCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
89 | {
90 | if (Patcher != null)
91 | {
92 | var version = (Version)VersionsCombo.SelectedItem;
93 | if (Patcher.CurrentVersion != version)
94 | {
95 | Patcher.CurrentVersion = version;
96 | Patcher.Reset();
97 | OnVersionDetected();
98 | }
99 | }
100 | }
101 |
102 | private void SwitchThemeButton_Click(object sender, RoutedEventArgs e)
103 | {
104 | if (Patcher != null)
105 | {
106 | Freeze("Switching theme...");
107 | ThreadHelper.Invoke(() => {
108 | ThemeType newTheme = Patcher.CurrentTheme == ThemeType.Light ? ThemeType.Dark : ThemeType.Light;
109 | ThemeType theme = Patcher.SetTheme(newTheme);
110 |
111 | if (newTheme == theme)
112 | {
113 | try
114 | {
115 | Patcher.Save();
116 | }
117 | catch (Exception ex)
118 | {
119 | MessageHelper.Error($"Could not to save {EditorFileName}! Check access permissions.\n\nDetails:\n\n{ex}");
120 | }
121 | }
122 | else
123 | {
124 | MessageHelper.Error("Could not to switch theme");
125 | }
126 |
127 | Dispatcher.Invoke(() => {
128 | Freeze(false);
129 | SetThemeThumbs(theme);
130 | });
131 | });
132 | }
133 | }
134 |
135 | private void MakeBackupButton_Click(object sender, RoutedEventArgs e)
136 | {
137 | Freeze("Making a new backup...");
138 | ThreadHelper.Invoke(() => {
139 | try
140 | {
141 | Patcher?.MakeBackup();
142 | }
143 | catch (Exception ex)
144 | {
145 | MessageHelper.ThrowException(ex);
146 | }
147 |
148 | Dispatcher.Invoke(() => {
149 | Freeze(false);
150 | });
151 | });
152 | }
153 |
154 | private void RestoreBackupButton_Click(object sender, RoutedEventArgs e)
155 | {
156 | string path = System.IO.Path.GetDirectoryName(Patcher.FilePath);
157 |
158 | Freeze("Searching backups...");
159 | ThreadHelper.Invoke(() => {
160 | var files = IOHelper.SearchFile(path, "Backup_", true, true);
161 |
162 | Dispatcher.Invoke(() => {
163 | Freeze(false);
164 | OnBackupFilesFound(files);
165 | });
166 | });
167 | }
168 |
169 | private void RunButton_Click(object sender, RoutedEventArgs e)
170 | {
171 | if (File.Exists(Patcher.FilePath))
172 | {
173 | try
174 | {
175 | System.Diagnostics.Process.Start(Patcher.FilePath);
176 | }
177 | catch (Exception ex)
178 | {
179 | MessageHelper.ThrowException(ex);
180 | }
181 | }
182 | else
183 | {
184 | MessageHelper.Error($"File {Path.GetFileName(Patcher.FilePath)} does not exist!");
185 | }
186 | }
187 |
188 | // Async callbacks
189 | private void OnFolderChosen(string path)
190 | {
191 | Navigate(Section.PatchScreen);
192 |
193 | DirectoryTextBox.Text = path;
194 |
195 | Freeze("Searching files...");
196 | ThreadHelper.Invoke(() => {
197 | var files = IOHelper.SearchFile(path, EditorFileName);
198 | Dispatcher.Invoke(() => {
199 | Freeze(false);
200 | OnFilesFound(files);
201 | });
202 | });
203 | }
204 |
205 | private void OnFilesFound(string[] files)
206 | {
207 | //Alert(string.Join("\n", files));
208 |
209 | if (files.Length > 0)
210 | {
211 | Freeze("Choose a file in another window");
212 | FilesListWindow win = new FilesListWindow(this, files, OnSelectFile) { Owner = this };
213 | win.Show();
214 | win.Focus();
215 | win.Closed += (s, e) => Freeze(false);
216 | }
217 | else
218 | {
219 | MessageHelper.Error($"There is no {EditorFileName}. Try choose another folder");
220 | }
221 | }
222 |
223 | private void OnSelectFile(string file)
224 | {
225 | Patcher = null;
226 | GC.Collect();
227 | //
228 | Patcher = new Patcher(file);
229 |
230 | Freeze("Loading...");
231 | ThreadHelper.Invoke(() => {
232 | Patcher.Load();
233 | Dispatcher.Invoke(() => {
234 | Freeze(false);
235 | OnFileLoaded();
236 | });
237 | });
238 | }
239 |
240 | private void OnFileLoaded()
241 | {
242 | Freeze("Detecting version...");
243 | ThreadHelper.Invoke(() => {
244 | Version version = Patcher.DetectVersion();
245 |
246 | Dispatcher.Invoke(() => {
247 | Freeze(false);
248 |
249 | if (version != null)
250 | {
251 | VersionsCombo.SelectedItem = version;
252 | OnVersionDetected();
253 | }
254 | else
255 | {
256 | MessageHelper.Error("This version is not supported. Try another version of Unity");
257 | }
258 | });
259 | });
260 | }
261 |
262 | private void OnVersionDetected()
263 | {
264 | Freeze("Detecting theme...");
265 | ThreadHelper.Invoke(() => {
266 | ThemeType theme = ThemeType.None;
267 |
268 | if (Patcher?.CurrentVersion != null)
269 | theme = Patcher.DetectTheme(Patcher.CurrentVersion);
270 |
271 | Dispatcher.Invoke(() => {
272 | Freeze(false);
273 | SetThemeThumbs(theme);
274 | if (theme == ThemeType.None)
275 | {
276 | MessageHelper.Error("Could not find signature");
277 | }
278 | });
279 | });
280 | }
281 |
282 | private void OnBackupFilesFound(string[] files)
283 | {
284 | if (files.Length > 0)
285 | {
286 | Freeze("Choose a file in another window");
287 | FilesListWindow win = new FilesListWindow(this, files, RestoreBackup) { Owner = this };
288 | win.Show();
289 | win.Focus();
290 | win.Closed += (s, e) => Freeze(false);
291 | }
292 | else
293 | {
294 | MessageHelper.Error($"There is no backup files. Make a first backup!");
295 | }
296 |
297 | void RestoreBackup(string path)
298 | {
299 | try
300 | {
301 | Patcher.RestoreBackup(path);
302 | OnSelectFile(Patcher.FilePath);
303 | }
304 | catch (Exception ex)
305 | {
306 | MessageHelper.ThrowException(ex);
307 | }
308 | }
309 | }
310 |
311 | // Thumbs
312 | public void ToggleThumb(Label thumb, bool state)
313 | {
314 | thumb.Style = (Style)Application.Current.FindResource(state ? "ThemeThumbSelected" : "ThemeThumb");
315 | }
316 |
317 | public void SetThemeThumbs(ThemeType skin)
318 | {
319 | ToggleThumb(LightTheme, skin == ThemeType.Light);
320 | ToggleThumb(DarkTheme, skin == ThemeType.Dark);
321 | }
322 |
323 | // Sections behaviour
324 | public void Navigate(Section section)
325 | {
326 | StartScreen.Visibility = section == Section.StartScreen ? Visibility.Visible : Visibility.Hidden;
327 | PatchScreen.Visibility = section == Section.PatchScreen ? Visibility.Visible : Visibility.Hidden;
328 | }
329 |
330 | public void Freeze(bool state)
331 | {
332 | ProcessState.Content = "Processing...";
333 | ProcessingScreen.Visibility = state ? Visibility.Visible : Visibility.Hidden;
334 | PatchScreen.IsEnabled = !state;
335 | PatchScreen.Effect = state ? new BlurEffect() { Radius = 10, KernelType = KernelType.Gaussian } : null;
336 |
337 | /*if (state)
338 | {
339 | ProcessingScreen.Visibility = Visibility.Visible;
340 | PatchScreen.IsEnabled = false;
341 | PatchScreen.Effect = new BlurEffect() { Radius = 10, KernelType = KernelType.Gaussian };
342 | }
343 | else
344 | {
345 | ProcessingScreen.Visibility = Visibility.Hidden;
346 | PatchScreen.IsEnabled = true;
347 | PatchScreen.Effect = null;
348 | }*/
349 | }
350 |
351 | public void Freeze(string text)
352 | {
353 | Freeze(true);
354 | ProcessState.Content = text;
355 | }
356 | }
357 | }
358 |
--------------------------------------------------------------------------------