From a51e44a3d08bf77b9551f21f4ab397afd2c18d66 Mon Sep 17 00:00:00 2001 From: moddedmcplayer <76910334+moddedmcplayer@users.noreply.github.com> Date: Sat, 25 Jun 2022 14:54:43 +0200 Subject: [PATCH 1/3] Patcher --- Scope.Installer/Installer.cs | 29 ++++++----- Scope.Patcher/Deserializer.cs | 66 +++++++++++++++++++++++++ Scope.Patcher/Models/Patch.cs | 16 ++++++ Scope.Patcher/Models/PatchCollection.cs | 28 +++++++++++ Scope.Patcher/Patcher.cs | 60 ++++++++++++++++++++++ Scope.sln | 6 +++ 6 files changed, 190 insertions(+), 15 deletions(-) create mode 100644 Scope.Patcher/Deserializer.cs create mode 100644 Scope.Patcher/Models/Patch.cs create mode 100644 Scope.Patcher/Models/PatchCollection.cs create mode 100644 Scope.Patcher/Patcher.cs diff --git a/Scope.Installer/Installer.cs b/Scope.Installer/Installer.cs index 6d73e8e..7558f5f 100644 --- a/Scope.Installer/Installer.cs +++ b/Scope.Installer/Installer.cs @@ -53,9 +53,7 @@ public static async Task Main(string[] args) string gameFolder = await GetSLFolder(); if (!Directory.Exists(gameFolder) || Directory.GetFiles(gameFolder).Contains("SCPSL.exe")) { - Console.WriteLine("Could not find the game folder, aborting."); - Console.Read(); - Environment.Exit(0); + Error("Could not find the game folder, aborting."); } try @@ -68,18 +66,14 @@ public static async Task Main(string[] args) if (hash != ZipHash) { - Console.WriteLine("The archive hash does not match!"); - Console.Read(); - Environment.Exit(0); + Error("The archive hash does not match!"); } Console.WriteLine("Extracting files..."); var archive = new ZipArchive(download); if (archive.Entries.All(x => !x.FullName.StartsWith("ScopeStuff"))) { - Console.WriteLine("Unable to find archive contents, is the installer outdated?"); - Console.Read(); - Environment.Exit(0); + Error("Unable to find archive contents, is the installer outdated?"); } Console.WriteLine("Moving files..."); @@ -137,9 +131,7 @@ public static async Task Main(string[] args) } catch (Exception ex) { - Console.WriteLine(ex); - Console.Read(); - Environment.Exit(0); + Error(ex.ToString()); } } @@ -170,9 +162,7 @@ private static async Task Download(string url) throw; } - Console.WriteLine("Check your internet connection and Scope server status and try again"); - Console.Read(); - Environment.Exit(0); + Error("Check your internet connection and Scope server status and try again"); } return stream; @@ -274,5 +264,14 @@ private static async Task GetSLFolder() return SLPath; } + + public static void Error(string message) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(message); + Console.ResetColor(); + Console.Read(); + Environment.Exit(0); + } } } \ No newline at end of file diff --git a/Scope.Patcher/Deserializer.cs b/Scope.Patcher/Deserializer.cs new file mode 100644 index 0000000..4b22ee0 --- /dev/null +++ b/Scope.Patcher/Deserializer.cs @@ -0,0 +1,66 @@ +namespace Scope.Patcher +{ + using System.Collections.Generic; + using System.IO; + using Models; + + public static class Deserializer + { + public static PatchCollection Read(string patchFile) + { + if (!File.Exists(patchFile)) + { + throw new FileNotFoundException(nameof(patchFile)); + } + + var patches = Deserialize1337(File.ReadAllLines(patchFile), out var name); + return new PatchCollection(name, patches); + } + + private static IEnumerable Deserialize1337(string[] lines, out string assemblyname) + { + string file = null; + List result = new List(); + + foreach (var line in lines) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + if (line.StartsWith(">")) + { + if (file is null) + { + file = line.Substring(1); + continue; + } + else + { + break; + } + } + + if (file is null) + { + throw new InvalidDataException("Patch file is not valid!"); + } + + var splitData = line.Replace("->", ":").Split(':'); + if (splitData.Length != 3) + { + throw new InvalidDataException("Unable to read patch data!"); + } + + result.Add(new Patch( + uint.Parse(splitData[0], System.Globalization.NumberStyles.HexNumber), + byte.Parse(splitData[1], System.Globalization.NumberStyles.HexNumber), + byte.Parse(splitData[1], System.Globalization.NumberStyles.HexNumber))); + } + + assemblyname = file; + return result; + } + } +} \ No newline at end of file diff --git a/Scope.Patcher/Models/Patch.cs b/Scope.Patcher/Models/Patch.cs new file mode 100644 index 0000000..f3e6afe --- /dev/null +++ b/Scope.Patcher/Models/Patch.cs @@ -0,0 +1,16 @@ +namespace Scope.Patcher.Models +{ + public struct Patch + { + public uint Address { get; private set; } + public byte OldValue { get; private set; } + public byte NewValue { get; private set; } + + public Patch(uint address, byte oldValue, byte newValue) + { + Address = address; + OldValue = oldValue; + NewValue = newValue; + } + } +} \ No newline at end of file diff --git a/Scope.Patcher/Models/PatchCollection.cs b/Scope.Patcher/Models/PatchCollection.cs new file mode 100644 index 0000000..ba5eea9 --- /dev/null +++ b/Scope.Patcher/Models/PatchCollection.cs @@ -0,0 +1,28 @@ +namespace Scope.Patcher.Models +{ + using System; + using System.Collections.Generic; + using System.IO; + using Newtonsoft.Json; + + public class PatchCollection : List + { + public string AssemblyName { get; internal set; } + public bool CanPatch => AssemblyName is not null && Count > 0; + + public PatchCollection() + { + } + + public PatchCollection(string assemblyName, IEnumerable patches) + { + if(patches == null) + { + throw new ArgumentNullException(nameof(patches)); + } + + AddRange(patches); + AssemblyName = assemblyName; + } + } +} \ No newline at end of file diff --git a/Scope.Patcher/Patcher.cs b/Scope.Patcher/Patcher.cs new file mode 100644 index 0000000..8c20504 --- /dev/null +++ b/Scope.Patcher/Patcher.cs @@ -0,0 +1,60 @@ +namespace Scope.Patcher +{ + using System; + using System.IO; + using Models; + + public static class Patcher + { + public static void Patch(this PatchCollection patches, string path, out bool expected, bool checkOld = false, bool backup = false) + { + if (!File.Exists(path)) + { + throw new FileNotFoundException(nameof(path)); + } + + if (patches.Count < 1) + { + throw new IndexOutOfRangeException(nameof(patches)); + } + + expected = true; + var data = File.ReadAllBytes(path); + foreach (var patch in patches) + { + if(patch.Address > data.Length) + { + continue; + } + + if(data[patch.Address] != patch.OldValue) + { + if (checkOld) + { + throw new Exception( + $"Data does not match (expected: {patch.OldValue}, actual: {data[patch.Address]})"); + } + else + { + expected = false; + } + } + + data[patch.Address] = patch.NewValue; + } + + if(backup) + { + Console.WriteLine("Backup already exists, continue? (y/n)"); + var input = Console.ReadLine()?.ToLower(); + if(input != "y") + { + return; + } + File.Copy(path, $"{path}.old", true); + } + + File.WriteAllBytes(path, data); + } + } +} \ No newline at end of file diff --git a/Scope.sln b/Scope.sln index e538a39..0c11703 100644 --- a/Scope.sln +++ b/Scope.sln @@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scope.Launcher", "Scope.Launcher\Scope.Launcher.csproj", "{C162996D-0CA0-482A-A76D-EC53B3F11BFE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scope.Patcher", "Scope.Patcher\Scope.Patcher.csproj", "{DB836450-BD75-433D-BAF6-82695ED8E0C6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -31,6 +33,10 @@ Global {C162996D-0CA0-482A-A76D-EC53B3F11BFE}.Debug|Any CPU.Build.0 = Debug|Any CPU {C162996D-0CA0-482A-A76D-EC53B3F11BFE}.Release|Any CPU.ActiveCfg = Release|Any CPU {C162996D-0CA0-482A-A76D-EC53B3F11BFE}.Release|Any CPU.Build.0 = Release|Any CPU + {DB836450-BD75-433D-BAF6-82695ED8E0C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB836450-BD75-433D-BAF6-82695ED8E0C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB836450-BD75-433D-BAF6-82695ED8E0C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB836450-BD75-433D-BAF6-82695ED8E0C6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From ecc91bcc2909751b2f2fc59995bf9e25ea91cc61 Mon Sep 17 00:00:00 2001 From: moddedmcplayer Date: Thu, 21 Jul 2022 19:34:04 +0200 Subject: [PATCH 2/3] probably want that one --- Scope.Installer/Scope.Installer.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Scope.Installer/Scope.Installer.csproj b/Scope.Installer/Scope.Installer.csproj index 6f88fc0..33fbec6 100644 --- a/Scope.Installer/Scope.Installer.csproj +++ b/Scope.Installer/Scope.Installer.csproj @@ -21,6 +21,10 @@ + + + + bin\Debug\Scope.Installer.xml $(NoWarn),1573,1591,1712 From 73004f3e62947dc4c43147ca2f495ac47c7b25ac Mon Sep 17 00:00:00 2001 From: moddedmcplayer <76910334+moddedmcplayer@users.noreply.github.com> Date: Sun, 19 Feb 2023 09:40:03 +0100 Subject: [PATCH 3/3] uncommited --- Scope.Installer/Installer.cs | 1 + Scope.Patcher/Models/PatchCollection.cs | 8 +- Scope.Patcher/Program.cs | 109 +++++++++++++++++++++++ Scope.Patcher/Properties/AssemblyInfo.cs | 35 ++++++++ Scope.Patcher/Scope.Patcher.csproj | 65 ++++++++++++++ Scope.Patcher/packages.config | 4 + global.json | 7 ++ 7 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 Scope.Patcher/Program.cs create mode 100644 Scope.Patcher/Properties/AssemblyInfo.cs create mode 100644 Scope.Patcher/Scope.Patcher.csproj create mode 100644 Scope.Patcher/packages.config create mode 100644 global.json diff --git a/Scope.Installer/Installer.cs b/Scope.Installer/Installer.cs index 7558f5f..f94ba10 100644 --- a/Scope.Installer/Installer.cs +++ b/Scope.Installer/Installer.cs @@ -24,6 +24,7 @@ namespace Scope.Installer /// public class Installer { + // TODO /// /// The URL to download the Scope files from. /// diff --git a/Scope.Patcher/Models/PatchCollection.cs b/Scope.Patcher/Models/PatchCollection.cs index ba5eea9..eed8826 100644 --- a/Scope.Patcher/Models/PatchCollection.cs +++ b/Scope.Patcher/Models/PatchCollection.cs @@ -8,7 +8,7 @@ public class PatchCollection : List { public string AssemblyName { get; internal set; } - public bool CanPatch => AssemblyName is not null && Count > 0; + public bool CanPatch => this.AssemblyName is not null && this.Count > 0; public PatchCollection() { @@ -16,13 +16,13 @@ public PatchCollection() public PatchCollection(string assemblyName, IEnumerable patches) { - if(patches == null) + if (patches == null) { throw new ArgumentNullException(nameof(patches)); } - AddRange(patches); - AssemblyName = assemblyName; + this.AddRange(patches); + this.AssemblyName = assemblyName; } } } \ No newline at end of file diff --git a/Scope.Patcher/Program.cs b/Scope.Patcher/Program.cs new file mode 100644 index 0000000..2fc74a1 --- /dev/null +++ b/Scope.Patcher/Program.cs @@ -0,0 +1,109 @@ +namespace Scope.Patcher +{ + using System; + using System.IO; + using System.Linq; + using System.Text.RegularExpressions; + using Models; + using Newtonsoft.Json; + + internal class Program + { + public static void Main(string[] args) + { + if (args.Length < 1) + { + Console.WriteLine("Usage: Scope.Patcher.exe "); + Console.WriteLine("Usage: Scope.Patcher.exe "); + } + + if (args.Length == 1) + { + if (!File.Exists(args.ElementAt(0))) + { + Error("Unable to find file: " + args.ElementAt(0)); + } + + try + { + var collection = Deserializer.Read(args.ElementAt(0)); + if (!collection.CanPatch) + { + Error("The file is incomplete!, aborting..."); + } + var json = JsonSerializer.Create(); + using (TextWriter tw = new StreamWriter("patches.json")) + { + using var jw = new JsonTextWriter(tw); + jw.WriteComment(collection.AssemblyName); + json.Serialize(jw, collection); + } + } + catch (InvalidDataException e) + { + Error("The patch file is invalid: " + e.Message); + } + catch (Exception e) + { + Error("An error occurred while reading the patch file: " + e.Message); + } + } + + if (args.Length == 2) + { + if (!File.Exists(args.ElementAt(0)) || !File.Exists(args.ElementAt(1))) + { + Error("Unable to find files!"); + } + + PatchCollection collection; + string lines; + using (TextReader tr = new StreamReader(args.ElementAt(0))) + { + lines = tr.ReadToEnd(); + collection = JsonConvert.DeserializeObject(lines); + } + + if (collection is null) + { + Error("Unable to read the file!"); + } + + if (Regex.Match(lines, @"\/\*[^\\]+\*\/", RegexOptions.Multiline | RegexOptions.IgnoreCase).Success) + { + collection!.AssemblyName = lines.Substring(lines.IndexOf(@"/*")); + } + + if (!collection!.CanPatch) + { + Error("The patch file is incomplete!, aborting..."); + } + + collection.Patch(args.ElementAt(1), out var expected); + if (!expected) + { + Warn("Old values didnt match!"); + } + } + } + + internal static void Error(string message, bool exit = false) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(message); + Console.ResetColor(); + if (exit) + { + Console.Read(); + Environment.Exit(0); + } + } + + internal static void Warn(string message) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine(message); + Console.ResetColor(); + } + } +} \ No newline at end of file diff --git a/Scope.Patcher/Properties/AssemblyInfo.cs b/Scope.Patcher/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..49ae9cd --- /dev/null +++ b/Scope.Patcher/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Scope.Patcher")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Scope.Patcher")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("DB836450-BD75-433D-BAF6-82695ED8E0C6")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Scope.Patcher/Scope.Patcher.csproj b/Scope.Patcher/Scope.Patcher.csproj new file mode 100644 index 0000000..8a8188e --- /dev/null +++ b/Scope.Patcher/Scope.Patcher.csproj @@ -0,0 +1,65 @@ + + + + + Debug + AnyCPU + {DB836450-BD75-433D-BAF6-82695ED8E0C6} + Exe + Properties + Scope.Patcher + Scope.Patcher + v4.7.2 + 512 + true + 9 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.13.0.2-beta1\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + diff --git a/Scope.Patcher/packages.config b/Scope.Patcher/packages.config new file mode 100644 index 0000000..a98330f --- /dev/null +++ b/Scope.Patcher/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/global.json b/global.json new file mode 100644 index 0000000..9e5e1fd --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "6.0.0", + "rollForward": "latestMajor", + "allowPrerelease": true + } +} \ No newline at end of file