diff --git a/dark-skin.sln b/dark-skin.sln index df76373..8c703d7 100644 --- a/dark-skin.sln +++ b/dark-skin.sln @@ -1,25 +1,37 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.2003 -MinimumVisualStudioVersion = 10.0.40219.1 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29201.188 +MinimumVisualStudioVersion = 15.0.26124.0 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dark-skin", "dark-skin\dark-skin.csproj", "{7C0252E3-4D08-445B-9D3C-1DD49620BBF4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Debug|x64.ActiveCfg = Debug|Any CPU + {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Debug|x64.Build.0 = Debug|Any CPU + {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Debug|x86.ActiveCfg = Debug|Any CPU + {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Debug|x86.Build.0 = Debug|Any CPU {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Release|Any CPU.Build.0 = Release|Any CPU + {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Release|x64.ActiveCfg = Release|Any CPU + {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Release|x64.Build.0 = Release|Any CPU + {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Release|x86.ActiveCfg = Release|Any CPU + {7C0252E3-4D08-445B-9D3C-1DD49620BBF4}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A491635B-D3B4-4E23-890A-91DAFB42D199} + SolutionGuid = {EF525544-E552-4928-9F1B-9848C7A4DCE5} EndGlobalSection EndGlobal diff --git a/dark-skin/CVDump.cs b/dark-skin/CVDump.cs index 75a127a..fe60455 100644 --- a/dark-skin/CVDump.cs +++ b/dark-skin/CVDump.cs @@ -16,12 +16,12 @@ public class CVDump : IDisposable { public void Execute(string exePath, DataReceivedEventHandler outputCallback, DataReceivedEventHandler errorCallback, string arguments = "-headers -p") { var startOptions = new ProcessStartInfo { - FileName = cvDumpExe, - Arguments = string.Format("{0} {1}", arguments, exePath), - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true + FileName = cvDumpExe, + Arguments = string.Format("{0} {1}", arguments, exePath), + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true }; var process = Process.Start(startOptions); diff --git a/dark-skin/FodyWeavers.xml b/dark-skin/FodyWeavers.xml new file mode 100644 index 0000000..49b26bf --- /dev/null +++ b/dark-skin/FodyWeavers.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dark-skin/FodyWeavers.xsd b/dark-skin/FodyWeavers.xsd new file mode 100644 index 0000000..44a5374 --- /dev/null +++ b/dark-skin/FodyWeavers.xsd @@ -0,0 +1,111 @@ + + + + + + + + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with line breaks. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with line breaks. + + + + + The order of preloaded assemblies, delimited with line breaks. + + + + + + This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. + + + + + Controls if .pdbs for reference assemblies are also embedded. + + + + + Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. + + + + + As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. + + + + + Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. + + + + + Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with |. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with |. + + + + + The order of preloaded assemblies, delimited with |. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/dark-skin/Options.cs b/dark-skin/Options.cs new file mode 100644 index 0000000..a5f895a --- /dev/null +++ b/dark-skin/Options.cs @@ -0,0 +1,22 @@ +using CommandLine; + +namespace DarkSkin { + + [Verb("enable", HelpText = "Unlock the dark skin.")] + class EnableOptions : BaseOption { } + + [Verb("disable", HelpText = "Revert the skin to the original.")] + class DisableOptions : BaseOption { } + + [Verb("findhex", HelpText = "Find and list the addresses and hexes of the GetSkinIdx methods")] + class FindHexOptions : BaseOption { } + + class BaseOption { + [Value(0, MetaName = "unityExe", Default = ".")] + public string InputFile { get; set; } + + [Option('f', "fast-enumerator", Default = false, HelpText = "Use fast file enumeration to search for executables, otherwise use recursive enumeration.")] + public bool FastEnumerator { get; set; } + } + +} \ No newline at end of file diff --git a/dark-skin/Program.cs b/dark-skin/Program.cs index 7f4c630..62b462a 100644 --- a/dark-skin/Program.cs +++ b/dark-skin/Program.cs @@ -1,17 +1,14 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Text.RegularExpressions; using CodeProject; +using CommandLine; namespace DarkSkin { public static class Program { - private static bool m_toEnable = true; - private static bool m_useFastFileEnumerator = false; - public static ParallelQuery Tap(this ParallelQuery source, Action action) { return source.Select(item => { action(item); @@ -26,120 +23,130 @@ public static class Program { }); } - private static void FindHex(string unity) { - - using(var cvDump = new CVDump()) { - var regex = new Regex(@"^.+:\s*\[(?
[0-9a-fA-F]{4}):(?[0-9a-fA-F]{8})\].*GetSkinIdx.*$"); - - cvDump.Execute(unity, - (args, e) => { // Stdout - if (string.IsNullOrWhiteSpace(e.Data)) - return; - - var match = regex.Match(e.Data); - - if (match.Success) { - var section = match.Groups["section"]; - var addr = long.Parse(match.Groups["addr"].ToString(), System.Globalization.NumberStyles.HexNumber); - - addr += 0x400; // Section offset - - Console.WriteLine(e.Data); + private static void FindHex(string unityPath, bool useFastFileEnumerator) { - using(new TempConsoleColor(ConsoleColor.DarkGreen)) - Console.WriteLine("Found GetSkinIdx at section {0} and address {1:X8}", section, addr); + if (useFastFileEnumerator) + using(new TempConsoleColor(ConsoleColor.DarkYellow)) + Console.WriteLine("Using fast file enumeration"); - try { - using(var file = File.OpenRead(unity)) { - var buffer = new byte[0x80]; // 45 is a random number that might be enought - - file.Seek(addr, SeekOrigin.Begin); - file.Read(buffer, 0, buffer.Length); + using(var cvDump = new CVDump()) { + var obj = new object(); - var formattedBytes = UnitySkin.FormatBytes(buffer); - Console.WriteLine("x64 Routine:"); - Console.WriteLine(formattedBytes); - } - } catch (Exception ex) { - Console.Error.WriteLine("Failed to open Unity executable"); - Console.Error.WriteLine(ex); - } - } - }, (args, e) => { // Stderr - Console.Error.WriteLine(e.Data); + GetUnityInstallations(Path.GetFullPath(unityPath), useFastFileEnumerator) + .AsParallel() + .Where(exe => File.Exists(exe)) + .ForAll(unity => { + cvDump.Execute(unity, + (args, e) => { // Stdout + if (string.IsNullOrWhiteSpace(e.Data)) + return; + + var regex = new Regex(@"^.+:\s*\[(?
[0-9a-fA-F]{4}):(?[0-9a-fA-F]{8})\].*GetSkinIdx.*$"); + var match = regex.Match(e.Data); + + if (match.Success) + lock(obj) { + var section = match.Groups["section"]; + var addr = long.Parse(match.Groups["addr"].ToString(), System.Globalization.NumberStyles.HexNumber); + + addr += 0x400; // Section offset + + Console.WriteLine(e.Data); + + using(new TempConsoleColor(ConsoleColor.DarkGreen)) + Console.WriteLine("Found GetSkinIdx at section {0} and address {1:X8}", section, addr); + + try { + using(var file = File.OpenRead(unity)) { + var buffer = new byte[0x80]; // This should be enough to reach the "ret" op + + file.Seek(addr, SeekOrigin.Begin); + file.Read(buffer, 0, buffer.Length); + + var formattedBytes = UnitySkin.FormatBytes(buffer); + Console.WriteLine("x64 Routine:"); + Console.WriteLine(formattedBytes); + } + } catch (Exception ex) { + Console.Error.WriteLine("Failed to open Unity executable"); + Console.Error.WriteLine(ex); + } + } + }, (args, e) => { // Stderr + using(new TempConsoleColor(ConsoleColor.DarkRed)) + Console.Error.WriteLine(e.Data); + }); }); - } } - private static void Main(params string[] args) { - - var findHex = args.Contains("findHex"); - var unityArg = findHex ? args[Array.IndexOf(args, "findHex") + 1] : ""; - - if (findHex) { - FindHex(unityArg); - return; - } + private static void Run(bool toEnable, string unityPath, bool useFastFileEnumerator) { + Console.WriteLine("Fetching unity installations..."); + + if (useFastFileEnumerator) + using(new TempConsoleColor(ConsoleColor.DarkYellow)) + Console.WriteLine("Using fast file enumeration"); + + GetUnityInstallations(Path.GetFullPath(unityPath), useFastFileEnumerator) + .AsParallel() + .Where(exe => File.Exists(exe)) + .Select(exe => new UnitySkin(exe)) + .Where(unity => unity.OffsetOfSkinFlags != -1 && unity.SkinIndex != -1) + .Where(unity => { + var shouldChange = (toEnable && unity.IsWhiteSkin) || (!toEnable && unity.IsDarkSkin); + if (!shouldChange) + unity.Log("Skin already applied, ignoring"); + return shouldChange; + }) + .ForAll(unity => unity.SetDarkSkinEnable(toEnable)); + } - var toEnable = args.Contains("enable"); - var toDisable = args.Contains("disable"); - var help = args.Contains("-h") || args.Contains("--help"); - - if (toEnable == toDisable || help) { - if (!help) - Console.Error.WriteLine("Invalid parameters"); - Console.WriteLine("Usage:"); - Console.WriteLine(" dark-skin.exe enable | disable [options]"); - Console.WriteLine(""); - Console.WriteLine(" findHex unityExe Find the address of the GetSkinIdx method for a particular Unity version"); - Console.WriteLine("-h, --help Show this screen"); - Console.WriteLine("-f, --fast-enumerator Use fast file enumeration, otherwise use recursive enumeration"); - return; + private static void HandleException(Exception ex) { + using(new TempConsoleColor(ConsoleColor.DarkRed)) { + Console.Error.WriteLine("\nError"); + Console.Error.WriteLine(ex); } + } - m_toEnable = toEnable; - m_useFastFileEnumerator = args.Contains("-f") || args.Contains("--fast-enumerator"); + private static int Main(params string[] args) { Console.Title = "Dark Skin for Unity"; - try { - - // try { - // var exeBytes = File.ReadAllBytes(@"C:\Unity\2019.2.0a11\Editor\Unity.exe"); - // var functionOffset = 0x00AB6CF0; - // var baseAddr = 0x400; - // Console.WriteLine(UnitySkin.FormatBytes(exeBytes, functionOffset + baseAddr, 1000)); - // } catch (Exception e) { - // Console.WriteLine(e); - // } finally { - // Console.ReadLine(); - // } - - Console.WriteLine("Fetching unity installations..."); - - GetUnityInstallations(Environment.CurrentDirectory) - .AsParallel() - .Where(exe => File.Exists(exe)) - .Select(exe => new UnitySkin(exe)) - .Where(unity => unity.OffsetOfSkinFlags != -1 && unity.SkinIndex != -1) - .Where(unity => { - var shouldChange = (toEnable && unity.IsWhiteSkin) || (!toEnable && unity.IsDarkSkin); - if (!shouldChange) - unity.Log("Skin already applied, ignoring"); - return shouldChange; - }) - .ForAll(unity => unity.SetDarkSkinEnable(toEnable)); - - } catch (Exception e) { - Console.Error.WriteLine("\nError"); - Console.Error.WriteLine(e); - } + return Parser.Default.ParseArguments(args) + .MapResult( + (EnableOptions opts) => { + try { + Run(true, opts.InputFile, opts.FastEnumerator); + return 0; + } catch (Exception ex) { + HandleException(ex); + return 1; + } + }, + (DisableOptions opts) => { + try { + Run(false, opts.InputFile, opts.FastEnumerator); + return 0; + } catch (Exception ex) { + HandleException(ex); + return 1; + } + }, + (FindHexOptions opts) => { + try { + FindHex(opts.InputFile, opts.FastEnumerator); + return 0; + } catch (Exception ex) { + HandleException(ex); + return 1; + } + }, + (errs) => 1); } - private static List GetUnityInstallations(string root) { + private static List GetUnityInstallations(string root, bool useFastFileEnumerator) { - if (m_useFastFileEnumerator) + if (useFastFileEnumerator) return FastDirectoryEnumerator.EnumerateFiles(root, "Unity.exe", SearchOption.AllDirectories) .AsParallel() .Tap(file => Console.WriteLine("Found {0}", file.Path)) @@ -156,7 +163,7 @@ public static class Program { } foreach (var directory in Directory.EnumerateDirectories(root)) - folders.AddRange(GetUnityInstallations(directory)); + folders.AddRange(GetUnityInstallations(directory, useFastFileEnumerator)); return folders; } diff --git a/dark-skin/UnitySkin.cs b/dark-skin/UnitySkin.cs index 5af8a87..33db65b 100644 --- a/dark-skin/UnitySkin.cs +++ b/dark-skin/UnitySkin.cs @@ -6,6 +6,8 @@ namespace DarkSkin { public class UnitySkin { + private static readonly object obj = new object(); + public static readonly string[] WHITE_HEX = new [] { "84 C0 75 08 33 C0 48 83 C4 20 5B C3 8B 03 48 83 C4 20 5B C3", // <= 2018.2 "84 C0 75 08 33 C0 48 83 C4 30 5B C3 8B 03 48 83 C4 30 5B C3", // == 2018.3 @@ -63,7 +65,10 @@ public class UnitySkin { EnsureBackup(); try { + lock(obj) + using(new TempConsoleColor(ConsoleColor.DarkGreen)) Log("Applying {0} skin...", enable ? "dark" : "white"); + using(var stream = File.Open(UnityExe, FileMode.Open)) { stream.Position = OffsetOfSkinFlags; stream.Write(enable ? darkBytes[SkinIndex] : whiteBytes[SkinIndex], 0, (enable ? darkBytes[SkinIndex] : whiteBytes[SkinIndex]).Length); diff --git a/dark-skin/dark-skin.csproj b/dark-skin/dark-skin.csproj index 95b9098..6047350 100644 --- a/dark-skin/dark-skin.csproj +++ b/dark-skin/dark-skin.csproj @@ -1,6 +1,10 @@ - - + + + + + Debug AnyCPU @@ -8,12 +12,13 @@ Exe DarkSkin dark-skin - v4.5 + v4.7.1 512 true true - + + AnyCPU true full @@ -23,7 +28,8 @@ prompt 4 - + + AnyCPU pdbonly true @@ -32,29 +38,54 @@ prompt 4 + + UnityIcon.ico + + + + + + + ..\packages\CommandLineParser.2.4.3\lib\netstandard2.0\CommandLine.dll + + + ..\packages\Costura.Fody.3.3.3\lib\net40\Costura.dll + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/dark-skin/dark-skin.csproj.user b/dark-skin/dark-skin.csproj.user index 0e53f81..d006fcf 100644 --- a/dark-skin/dark-skin.csproj.user +++ b/dark-skin/dark-skin.csproj.user @@ -1,7 +1,8 @@  - - enable + + findHex C:\Unity\2019.3.0a11\Editor\Unity.exe C:\Unity\ + true \ No newline at end of file diff --git a/dark-skin/packages.config b/dark-skin/packages.config new file mode 100644 index 0000000..dde3857 --- /dev/null +++ b/dark-skin/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file