├── .gitignore ├── Console ├── App.config ├── Config.cs ├── Console.csproj ├── Log.cs ├── Main.cs ├── Marks.cs ├── Starter.cs └── Utils.cs ├── LICENSE ├── README.md ├── Repository.json ├── Repository_beta_13.json ├── UnityModManager.sln ├── UnityModManager ├── Config.cs ├── Doorstop.cs ├── Fixes.cs ├── Games.cs ├── Graphics.cs ├── Injector.cs ├── Json.cs ├── KeyBinding.cs ├── Log.cs ├── ModEntry.cs ├── ModInfo.cs ├── ModManager.cs ├── ModSettings.cs ├── Properties │ ├── Resources.Designer.cs │ └── Resources.resx ├── Repository.cs ├── TextureReplacer.cs ├── ToggleGroup.cs ├── UI.cs ├── UIDraw.cs ├── UnityModManager.csproj ├── Updates.cs ├── Utils.cs ├── VectorsInt.cs └── Window_GUI.cs ├── UnityModManagerApp ├── DownloadExtraFiles.Designer.cs ├── DownloadExtraFiles.cs ├── DownloadExtraFiles.resx ├── DownloadMod.Designer.cs ├── DownloadMod.cs ├── DownloadMod.resx ├── Form.Designer.cs ├── Form.cs ├── Form.resx ├── Log.cs ├── Mods.cs ├── Program.cs ├── Properties │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── SetFolder.Designer.cs ├── SetFolder.cs ├── SetFolder.resx ├── Tools │ └── SteamHelper.cs ├── UnityModManagerApp.csproj ├── Updates.cs ├── Utils.cs ├── app.config ├── icon.ico └── images │ └── dragdropfiles.png ├── Updater ├── Config.cs ├── Form.Designer.cs ├── Form.cs ├── Form.resx ├── Program.cs ├── Properties │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Updater.csproj ├── Utils.cs └── app.config └── lib ├── Harmony ├── 1.2 │ ├── 0Harmony-1.2.dll │ └── 0Harmony12.dll └── 2.2 │ └── 0Harmony.dll ├── Ionic.Zip.dll ├── Newtonsoft.Json.dll ├── System.Xml.dll ├── dnlib.dll ├── libraries ├── winhttp_x64.dll └── winhttp_x86.dll /.gitignore: -------------------------------------------------------------------------------- 1 | */UnityEngine* 2 | */obj 3 | */bin 4 | .vs 5 | */*.csproj.user -------------------------------------------------------------------------------- /Console/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Console/Config.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Xml.Serialization; 7 | using Newtonsoft.Json; 8 | 9 | namespace UnityModManagerNet.ConsoleInstaller 10 | { 11 | public enum ModStatus { NotInstalled, Installed } 12 | public enum InstallType { Assembly, DoorstopProxy, /*Config,*/ Count } 13 | 14 | public enum UpdateCheckingMode { Manually, OnceDay, Everytime, Count } 15 | 16 | public class ModInfo : UnityModManager.ModInfo 17 | { 18 | [JsonIgnore] 19 | public ModStatus Status; 20 | 21 | [JsonIgnore] 22 | public Dictionary AvailableVersions = new Dictionary(); 23 | 24 | [JsonIgnore] 25 | public Version ParsedVersion; 26 | 27 | [JsonIgnore] 28 | public string Path; 29 | 30 | public bool IsValid() 31 | { 32 | if (string.IsNullOrEmpty(Id)) 33 | { 34 | return false; 35 | } 36 | if (string.IsNullOrEmpty(DisplayName)) 37 | { 38 | DisplayName = Id; 39 | } 40 | if (ParsedVersion == null) 41 | { 42 | ParsedVersion = Utils.ParseVersion(Version); 43 | } 44 | 45 | return true; 46 | } 47 | 48 | public bool EqualsVersion(ModInfo other) 49 | { 50 | return other != null && Id.Equals(other.Id) && Version.Equals(other.Version); 51 | } 52 | } 53 | 54 | [XmlRoot("Config")] 55 | public class GameInfo 56 | { 57 | [XmlAttribute] 58 | public string Name; 59 | public string Folder; 60 | public string ModsDirectory; 61 | public string ModInfo; 62 | public string GameExe; 63 | public string EntryPoint; 64 | public string StartingPoint; 65 | public string UIStartingPoint; 66 | public string TextureReplacingPoint; 67 | public string SessionStartPoint; 68 | public string SessionStopPoint; 69 | public string GameVersionPoint; 70 | public string MinimalManagerVersion; 71 | public string OldPatchTarget; 72 | /// 73 | /// [0.21.8] 74 | /// 75 | public string Comment; 76 | //public string MachineConfig; 77 | public string ExtraFilesUrl; 78 | public string HarmonyVersion; 79 | 80 | public override string ToString() 81 | { 82 | return Name; 83 | } 84 | 85 | public static string filepathInGame; 86 | 87 | public void ExportToGame() 88 | { 89 | try 90 | { 91 | using (var writer = new StreamWriter(filepathInGame)) 92 | { 93 | new XmlSerializer(typeof(GameInfo)).Serialize(writer, this); 94 | } 95 | } 96 | catch (Exception e) 97 | { 98 | Log.Print($"Error file creating '{filepathInGame}'."); 99 | throw e; 100 | } 101 | } 102 | 103 | public static GameInfo ImportFromGame() 104 | { 105 | try 106 | { 107 | using (var stream = File.OpenRead(filepathInGame)) 108 | { 109 | return new XmlSerializer(typeof(GameInfo)).Deserialize(stream) as GameInfo; 110 | } 111 | } 112 | catch (Exception e) 113 | { 114 | Log.Print(e.ToString()); 115 | Log.Print($"Can't read file '{filepathInGame}'."); 116 | return null; 117 | } 118 | } 119 | } 120 | 121 | public sealed class Config 122 | { 123 | public const string filename = "UnityModManagerConfig.xml"; 124 | 125 | public string Repository; 126 | public string HomePage; 127 | 128 | [XmlElement] 129 | public GameInfo[] GameInfo; 130 | 131 | public static void Create() 132 | { 133 | var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filename); 134 | try 135 | { 136 | using (var writer = new StreamWriter(filepath)) 137 | { 138 | var config = new Config() 139 | { 140 | GameInfo = new GameInfo[] 141 | { 142 | new GameInfo 143 | { 144 | Name = "Game", 145 | Folder = "Folder", 146 | ModsDirectory = "Mods", 147 | ModInfo = "Info.json", 148 | EntryPoint = "[Assembly-CSharp.dll]App.Awake", 149 | GameExe = "Game.exe" 150 | } 151 | } 152 | }; 153 | var serializer = new XmlSerializer(typeof(Config)); 154 | serializer.Serialize(writer, config); 155 | } 156 | 157 | Log.Print($"'{filename}' auto created."); 158 | } 159 | catch (Exception e) 160 | { 161 | Log.Print(e.ToString()); 162 | } 163 | } 164 | 165 | public static Config Load() 166 | { 167 | var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filename); 168 | if (File.Exists(filepath)) 169 | { 170 | try 171 | { 172 | using (var stream = File.OpenRead(filepath)) 173 | { 174 | var serializer = new XmlSerializer(typeof(Config)); 175 | var result = serializer.Deserialize(stream) as Config; 176 | 177 | foreach(var file in new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).GetFiles("UnityModManagerConfig*.xml", SearchOption.TopDirectoryOnly)) 178 | { 179 | if (file.Name != filename) 180 | { 181 | try 182 | { 183 | using (var localStream = File.OpenRead(file.FullName)) 184 | { 185 | var localResult = serializer.Deserialize(localStream) as Config; 186 | if (localResult.GameInfo == null) 187 | continue; 188 | var concatanatedArray = new GameInfo[result.GameInfo.Length + localResult.GameInfo.Length]; 189 | result.GameInfo.CopyTo(concatanatedArray, 0); 190 | localResult.GameInfo.CopyTo(concatanatedArray, result.GameInfo.Length); 191 | result.GameInfo = concatanatedArray; 192 | } 193 | } 194 | catch (Exception e) 195 | { 196 | Log.Print(e.ToString()); 197 | } 198 | } 199 | } 200 | 201 | OnDeserialize(result); 202 | return result; 203 | } 204 | } 205 | catch (Exception e) 206 | { 207 | Log.Print(e.ToString() + Environment.NewLine + filename); 208 | } 209 | } 210 | else 211 | { 212 | Log.Print($"'{filename}' not found."); 213 | } 214 | return null; 215 | } 216 | 217 | private static void OnDeserialize(Config value) 218 | { 219 | 220 | } 221 | } 222 | 223 | public sealed class Param 224 | { 225 | [Serializable] 226 | public class GameParam 227 | { 228 | [XmlAttribute] 229 | public string Name; 230 | public string Path; 231 | public InstallType InstallType = InstallType.DoorstopProxy; 232 | public DateTime LastUpdateCheck; 233 | } 234 | 235 | public string APIkey; 236 | public UpdateCheckingMode UpdateCheckingMode = UpdateCheckingMode.OnceDay; 237 | public string LastSelectedGame; 238 | public int WindowHeight; 239 | public List GameParams = new List(); 240 | 241 | public const string filename = "Params.xml"; 242 | 243 | static GameParam CreateGameParam(GameInfo gameInfo) 244 | { 245 | return new GameParam { Name = gameInfo.Name }; 246 | } 247 | 248 | public GameParam GetGameParam(GameInfo gameInfo) 249 | { 250 | var result = GameParams.FirstOrDefault(x => x.Name == gameInfo.Name); 251 | if (result == null) 252 | { 253 | result = CreateGameParam(gameInfo); 254 | GameParams.Add(result); 255 | } 256 | return result; 257 | } 258 | 259 | public void Sync(GameInfo[] gameInfos) 260 | { 261 | int i = 0; 262 | while (i < GameParams.Count) 263 | { 264 | if (gameInfos.Any(x => x.Name == GameParams[i].Name)) 265 | { 266 | i++; 267 | } 268 | else 269 | { 270 | GameParams.RemoveAt(i); 271 | } 272 | } 273 | } 274 | 275 | public void Save() 276 | { 277 | var dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UnityModManagerNet"); 278 | if (!Directory.Exists(dir)) 279 | { 280 | Directory.CreateDirectory(dir); 281 | } 282 | var path = Path.Combine(dir, filename); 283 | try 284 | { 285 | using (var writer = new StreamWriter(path)) 286 | { 287 | var serializer = new XmlSerializer(typeof(Param)); 288 | serializer.Serialize(writer, this); 289 | } 290 | } 291 | catch (Exception e) 292 | { 293 | Log.Print(e.ToString() + Environment.NewLine + path); 294 | } 295 | } 296 | 297 | public static Param Load() 298 | { 299 | var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UnityModManagerNet", filename); 300 | if (File.Exists(path)) 301 | { 302 | try 303 | { 304 | using (var stream = File.OpenRead(path)) 305 | { 306 | var serializer = new XmlSerializer(typeof(Param)); 307 | return serializer.Deserialize(stream) as Param; 308 | } 309 | } 310 | catch (Exception e) 311 | { 312 | Log.Print(e.ToString() + Environment.NewLine + path); 313 | } 314 | } 315 | return new Param(); 316 | } 317 | } 318 | 319 | 320 | } 321 | -------------------------------------------------------------------------------- /Console/Console.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Exe 7 | net48 8 | latest 9 | 10 | UnityModManager 11 | Console 12 | UnityModManagerNet 13 | Copyright © 2021-$([System.DateTime]::Now.ToString('yyyy')) 14 | 15 | 16 | 17 | true 18 | en-001 19 | True 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | $(Pkgdnlib)\lib\net35\dnlib.dll 32 | 33 | 34 | $(PkgLib_Harmony)\lib\net48\0Harmony.dll 35 | 36 | 37 | 38 | 39 | 40 | ..\lib\UnityEngine.dll 41 | 42 | 43 | ..\lib\UnityEngine.UI.dll 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Console/Log.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace UnityModManagerNet.ConsoleInstaller 5 | { 6 | public class Log 7 | { 8 | public static Log Instance { get; set; } 9 | protected static bool firstLine = true; 10 | public const string fileLog = "Log.txt"; 11 | protected static StreamWriter stream; 12 | 13 | public static void Print(string str, bool append = false) 14 | { 15 | Instance.Write(str, append); 16 | } 17 | 18 | public static void Append(string str) 19 | { 20 | Print(str, true); 21 | } 22 | 23 | public static void Init() where T: Log 24 | { 25 | try 26 | { 27 | Instance = Activator.CreateInstance(); 28 | File.Delete(fileLog); 29 | stream = new StreamWriter(new FileStream(fileLog, FileMode.OpenOrCreate, FileAccess.Write)); 30 | stream.AutoFlush = true; 31 | } 32 | catch (UnauthorizedAccessException) 33 | { 34 | Print("Write error, insufficient permissions. Try to run app as administrator."); 35 | } 36 | } 37 | 38 | public virtual void Write(string str, bool append = false) 39 | { 40 | if (append) 41 | { 42 | Console.Write(str); 43 | } 44 | else 45 | { 46 | if (firstLine) 47 | { 48 | firstLine = false; 49 | str = $"[{DateTime.Now.ToShortTimeString()}] {str}"; 50 | } 51 | else 52 | { 53 | str = $"\r\n[{DateTime.Now.ToShortTimeString()}] {str}"; 54 | } 55 | Console.Write(str); 56 | } 57 | 58 | stream?.Write(str); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Console/Marks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace UnityModManagerNet.Marks 7 | { 8 | public static class IsDirty 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Console/Starter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | 5 | namespace UnityModManagerNet.Injection 6 | { 7 | public class UnityModManagerStarter 8 | { 9 | public static void Start() 10 | { 11 | //Injector.Run(); 12 | 13 | try 14 | { 15 | var file = Path.Combine(Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), "UnityModManager"), "UnityModManager.dll"); 16 | Console.WriteLine("[Assembly] Loading UnityModManager by " + file); 17 | 18 | var assembly = Assembly.LoadFrom(file); 19 | var injector = assembly.GetType("UnityModManagerNet.Injector"); 20 | injector.GetMethod("Run", BindingFlags.Static | BindingFlags.Public).Invoke(null, new object[] { false }); 21 | } 22 | catch (Exception e) 23 | { 24 | Console.WriteLine(e.ToString()); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 newman55 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unity-mod-manager 2 | This mod adds modding support for games created on Unity engine. 3 | 4 | [**Home page**](https://www.nexusmods.com/site/mods/21) 5 | 6 | [**Dropbox**](https://www.dropbox.com/s/wz8x8e4onjdfdbm/UnityModManager.zip?dl=1) 7 | 8 | ### Libraries used in the project. 9 | - [Harmony](https://github.com/pardeike/Harmony/wiki/Utilities) 10 | - [HarmonyProxy](https://github.com/spacehamster/HarmonyProxy) 11 | - [UnityDoorstop](https://github.com/NeighTools/UnityDoorstop) 12 | - [dnlib](https://github.com/0xd4d/dnlib) 13 | - [Json.NET](https://www.newtonsoft.com/json) 14 | - [TinyJson](https://github.com/zanders3/json) 15 | - [Ionic.Zip](https://archive.codeplex.com/?p=dotnetzip) 16 | -------------------------------------------------------------------------------- /Repository.json: -------------------------------------------------------------------------------- 1 | { 2 | "Releases": 3 | [ 4 | {"Id": "UnityModManager", "Version": "0.12.7", "DownloadUrl": "https://www.dropbox.com/s/2fipc0zhp0q4z7m/umm_update.zip?dl=1"} 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /Repository_beta_13.json: -------------------------------------------------------------------------------- 1 | { 2 | "Releases": 3 | [ 4 | {"Id": "UnityModManager", "Version": "0.32.4", "DownloadUrl": "https://www.dropbox.com/s/wt18wcq5six02ku/umm_update.zip?dl=1"} 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /UnityModManager.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31912.275 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Updater", "Updater\Updater.csproj", "{0EEC949D-BF5C-4F12-9743-0A635ABC8DE4}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityModManagerApp", "UnityModManagerApp\UnityModManagerApp.csproj", "{6E8F8B45-2A2D-4E5A-8573-3559337E68F3}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityModManager", "UnityModManager\UnityModManager.csproj", "{A1FCAE3E-523E-4E33-8860-DD39DDDF0F62}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{7151CE06-904E-485D-8BC3-A7174A9D5D29}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {0EEC949D-BF5C-4F12-9743-0A635ABC8DE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {0EEC949D-BF5C-4F12-9743-0A635ABC8DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {0EEC949D-BF5C-4F12-9743-0A635ABC8DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {0EEC949D-BF5C-4F12-9743-0A635ABC8DE4}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {6E8F8B45-2A2D-4E5A-8573-3559337E68F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {6E8F8B45-2A2D-4E5A-8573-3559337E68F3}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {6E8F8B45-2A2D-4E5A-8573-3559337E68F3}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {6E8F8B45-2A2D-4E5A-8573-3559337E68F3}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {A1FCAE3E-523E-4E33-8860-DD39DDDF0F62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {A1FCAE3E-523E-4E33-8860-DD39DDDF0F62}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {A1FCAE3E-523E-4E33-8860-DD39DDDF0F62}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {A1FCAE3E-523E-4E33-8860-DD39DDDF0F62}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {7151CE06-904E-485D-8BC3-A7174A9D5D29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {7151CE06-904E-485D-8BC3-A7174A9D5D29}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {7151CE06-904E-485D-8BC3-A7174A9D5D29}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {7151CE06-904E-485D-8BC3-A7174A9D5D29}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {5BE2057F-67FB-4E45-B83B-E3225C71C644} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /UnityModManager/Config.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Xml.Serialization; 5 | using UnityEngine; 6 | 7 | namespace UnityModManagerNet 8 | { 9 | public partial class UnityModManager 10 | { 11 | public sealed class Param 12 | { 13 | [Serializable] 14 | public class Mod 15 | { 16 | [XmlAttribute] 17 | public string Id; 18 | [XmlAttribute] 19 | public bool Enabled = true; 20 | public KeyBinding Hotkey = new KeyBinding(); 21 | } 22 | 23 | public static KeyBinding DefaultHotkey = new KeyBinding { keyCode = KeyCode.F10, modifiers = 1 }; 24 | public static KeyBinding EscapeHotkey = new KeyBinding { keyCode = KeyCode.Escape }; 25 | public KeyBinding Hotkey = new KeyBinding(); 26 | public int CheckUpdates = 1; 27 | public int ShowOnStart = 1; 28 | public float WindowWidth; 29 | public float WindowHeight; 30 | public float UIScale = 1f; 31 | public string UIFont = null; 32 | public DateTime LastUpdateCheck; 33 | 34 | public List ModParams = new List(); 35 | 36 | static readonly string filepath = Path.Combine(Path.GetDirectoryName(typeof(Param).Assembly.Location), "Params.xml"); 37 | 38 | public void Save() 39 | { 40 | try 41 | { 42 | ModParams.Clear(); 43 | foreach (var mod in modEntries) 44 | { 45 | ModParams.Add(new Mod { Id = mod.Info.Id, Enabled = mod.Enabled, Hotkey = mod.Hotkey }); 46 | } 47 | using (var writer = new StreamWriter(filepath)) 48 | { 49 | var serializer = new XmlSerializer(typeof(Param)); 50 | serializer.Serialize(writer, this); 51 | } 52 | } 53 | catch (Exception e) 54 | { 55 | Logger.Error($"Can't write file '{filepath}'."); 56 | Debug.LogException(e); 57 | } 58 | } 59 | 60 | public static Param Load() 61 | { 62 | if (File.Exists(filepath)) 63 | { 64 | try 65 | { 66 | using (var stream = File.OpenRead(filepath)) 67 | { 68 | var serializer = new XmlSerializer(typeof(Param)); 69 | var result = serializer.Deserialize(stream) as Param; 70 | 71 | return result; 72 | } 73 | } 74 | catch (Exception e) 75 | { 76 | Logger.Error($"Can't read file '{filepath}'."); 77 | Debug.LogException(e); 78 | } 79 | } 80 | return new Param(); 81 | } 82 | 83 | internal void ReadModParams() 84 | { 85 | foreach (var item in ModParams) 86 | { 87 | var mod = FindMod(item.Id); 88 | if (mod != null) 89 | { 90 | mod.Enabled = item.Enabled; 91 | mod.Hotkey = item.Hotkey != null ? item.Hotkey : new KeyBinding(); 92 | } 93 | } 94 | } 95 | } 96 | 97 | [XmlRoot("Param")] 98 | public sealed class InstallerParam 99 | { 100 | public string APIkey; 101 | 102 | public static InstallerParam Load() 103 | { 104 | var filepath = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UnityModManagerNet"), "Params.xml"); 105 | if (File.Exists(filepath)) 106 | { 107 | try 108 | { 109 | using (var stream = File.OpenRead(filepath)) 110 | { 111 | var serializer = new XmlSerializer(typeof(InstallerParam)); 112 | var result = serializer.Deserialize(stream) as InstallerParam; 113 | 114 | return result; 115 | } 116 | } 117 | catch (Exception e) 118 | { 119 | Logger.Error($"Can't read file '{filepath}'."); 120 | Debug.LogException(e); 121 | } 122 | } 123 | return new InstallerParam(); 124 | } 125 | } 126 | 127 | [XmlRoot("Config")] 128 | public class GameInfo 129 | { 130 | [XmlAttribute] 131 | public string Name; 132 | public string Folder; 133 | public string ModsDirectory; 134 | public string ModInfo; 135 | public string EntryPoint; 136 | public string StartingPoint; 137 | public string UIStartingPoint; 138 | public string TextureReplacingPoint; 139 | public string SessionStartPoint; 140 | public string SessionStopPoint; 141 | public string GameExe; 142 | public string GameVersionPoint; 143 | public string MinimalManagerVersion; 144 | 145 | static readonly string filepath = Path.Combine(Path.GetDirectoryName(typeof(GameInfo).Assembly.Location), "Config.xml"); 146 | 147 | public static GameInfo Load() 148 | { 149 | try 150 | { 151 | using (var stream = File.OpenRead(filepath)) 152 | { 153 | var obj = new XmlSerializer(typeof(GameInfo)).Deserialize(stream) as GameInfo; 154 | if (!string.IsNullOrEmpty(obj.Name)) 155 | { 156 | obj.Name = obj.Name.Replace("&", "&"); 157 | } 158 | if (!string.IsNullOrEmpty(obj.Folder)) 159 | { 160 | obj.Folder = obj.Folder.Replace("&", "&"); 161 | } 162 | if (!string.IsNullOrEmpty(obj.GameExe)) 163 | { 164 | obj.GameExe = obj.GameExe.Replace("&", "&"); 165 | } 166 | 167 | return obj; 168 | } 169 | } 170 | catch (Exception e) 171 | { 172 | Logger.Error($"Can't read file '{filepath}'."); 173 | Debug.LogException(e); 174 | return null; 175 | } 176 | } 177 | } 178 | } 179 | } -------------------------------------------------------------------------------- /UnityModManager/Doorstop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Doorstop 5 | { 6 | public class Entrypoint 7 | { 8 | public static void Start() 9 | { 10 | UnityModManagerNet.UnityModManager.Main(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /UnityModManager/Fixes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using HarmonyLib; 4 | 5 | namespace UnityModManagerNet 6 | { 7 | static class Fixes 8 | { 9 | public static void Apply() 10 | { 11 | if (Environment.Version < new Version(4, 0)) 12 | { 13 | var harmony = new HarmonyLib.Harmony(nameof(UnityModManager)); 14 | var original = typeof(Assembly).GetMethod(nameof(Assembly.GetTypes), BindingFlags.Instance | BindingFlags.Public, null, new Type[0], new ParameterModifier[0]); 15 | var prefix = typeof(Fixes).GetMethod(nameof(Prefix_GetTypes), BindingFlags.Static | BindingFlags.NonPublic); 16 | harmony.Patch(original, new HarmonyMethod(prefix)); 17 | //UnityModManager.Logger.Log(""); 18 | } 19 | } 20 | 21 | static bool Prefix_GetTypes(Assembly __instance, ref Type[] __result) 22 | { 23 | if (__instance.FullName.StartsWith("UnityModManager")) 24 | { 25 | __result = new Type[0]; 26 | return false; 27 | } 28 | return true; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /UnityModManager/Games.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using UnityEngine; 6 | 7 | namespace UnityModManagerNet 8 | { 9 | public partial class UnityModManager 10 | { 11 | class GameScripts 12 | { 13 | readonly static List scripts = new List(); 14 | 15 | public static void Init() 16 | { 17 | var currentGame = Config.Name.Replace(" ", "").Replace(":", ""); 18 | foreach (var t in typeof(GameScripts).GetNestedTypes(BindingFlags.NonPublic)) 19 | { 20 | if (t.IsClass && t.IsSubclassOf(typeof(GameScript)) && t.Name == currentGame) 21 | { 22 | var script = (GameScript)Activator.CreateInstance(t); 23 | scripts.Add(script); 24 | Logger.Log($"Initialize game script {t.Name}"); 25 | } 26 | } 27 | } 28 | 29 | public static void OnBeforeLoadMods() 30 | { 31 | foreach (var o in scripts) 32 | { 33 | try 34 | { 35 | o.OnBeforeLoadMods(); 36 | } 37 | catch (Exception e) 38 | { 39 | Logger.LogException("OnBeforeLoadMods", e); 40 | } 41 | } 42 | } 43 | 44 | public static void OnAfterLoadMods() 45 | { 46 | foreach (var o in scripts) 47 | { 48 | try 49 | { 50 | o.OnAfterLoadMods(); 51 | } 52 | catch (Exception e) 53 | { 54 | Logger.LogException("OnAfterLoadMods", e); 55 | } 56 | } 57 | } 58 | 59 | public static void OnModToggle(ModEntry modEntry, bool value) 60 | { 61 | foreach(var o in scripts) 62 | { 63 | try 64 | { 65 | o.OnModToggle(modEntry, value); 66 | } 67 | catch (Exception e) 68 | { 69 | Logger.LogException("OnModToggle", e); 70 | } 71 | } 72 | } 73 | 74 | public static void OnToggleWindow(bool value) 75 | { 76 | foreach (var o in scripts) 77 | { 78 | try 79 | { 80 | o.OnToggleWindow(value); 81 | } 82 | catch (Exception e) 83 | { 84 | Logger.LogException("OnToggleWindow", e); 85 | } 86 | } 87 | } 88 | 89 | class GameScript 90 | { 91 | public virtual void OnModToggle(ModEntry modEntry, bool value) { } 92 | public virtual void OnBeforeLoadMods() { } 93 | public virtual void OnAfterLoadMods() { } 94 | /// 95 | /// [0.21.3] 96 | /// 97 | public virtual void OnToggleWindow(bool value) { } 98 | } 99 | 100 | // Insert here a class named as game to execute custom script for a game. 101 | 102 | /* 103 | class RiskofRain2 : GameScript 104 | { 105 | public override void OnModToggle(ModEntry modEntry, bool value) 106 | { 107 | if (modEntry.Info.IsCheat) 108 | { 109 | if (value) 110 | { 111 | SetModded(true); 112 | } 113 | else if (modEntries.All(x => x == modEntry || !x.Info.IsCheat)) 114 | { 115 | SetModded(false); 116 | } 117 | } 118 | } 119 | 120 | public override void OnBeforeLoadMods() 121 | { 122 | forbidDisableMods = true; 123 | } 124 | 125 | private static FieldInfo mFieldModded; 126 | public static FieldInfo FieldModded 127 | { 128 | get 129 | { 130 | if (mFieldModded == null) 131 | { 132 | foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 133 | { 134 | if (assembly.ManifestModule.Name == "Assembly-CSharp.dll") 135 | { 136 | mFieldModded = assembly.GetType("RoR2.RoR2Application").GetField("isModded", BindingFlags.Public | BindingFlags.Static); 137 | break; 138 | } 139 | } 140 | } 141 | return mFieldModded; 142 | } 143 | } 144 | 145 | public static bool GetModded() 146 | { 147 | return (bool)FieldModded.GetValue(null); 148 | } 149 | 150 | public static void SetModded(bool value) 151 | { 152 | FieldModded.SetValue(null, value); 153 | } 154 | } 155 | */ 156 | 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /UnityModManager/Injector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | using System.Security.Cryptography; 5 | using System.Text.RegularExpressions; 6 | using UnityEngine; 7 | using HarmonyLib; 8 | 9 | namespace UnityModManagerNet 10 | { 11 | public class Injector 12 | { 13 | public static void Run(bool doorstop = false) 14 | { 15 | if (UnityModManager.initialized) 16 | return; 17 | 18 | try 19 | { 20 | _Run(doorstop); 21 | } 22 | catch (Exception e) 23 | { 24 | Debug.LogException(e); 25 | UnityModManager.OpenUnityFileLog(); 26 | } 27 | } 28 | 29 | private static bool startUiWithManager; 30 | 31 | private static void _Run(bool doorstop) 32 | { 33 | Console.WriteLine(); 34 | Console.WriteLine(); 35 | UnityModManager.Logger.Log("Injection..."); 36 | 37 | if (!UnityModManager.Initialize()) 38 | { 39 | UnityModManager.Logger.Log($"Cancel start due to an error."); 40 | UnityModManager.OpenUnityFileLog(); 41 | return; 42 | } 43 | 44 | Fixes.Apply(); 45 | 46 | if (!string.IsNullOrEmpty(UnityModManager.Config.UIStartingPoint) && UnityModManager.Config.UIStartingPoint != UnityModManager.Config.StartingPoint) 47 | { 48 | if (TryGetEntryPoint(UnityModManager.Config.UIStartingPoint, out var @class, out var method, out var place)) 49 | { 50 | var usePrefix = (place == "before"); 51 | var harmony = new HarmonyLib.Harmony(nameof(UnityModManager)); 52 | var prefix = typeof(Injector).GetMethod(nameof(Prefix_Show), BindingFlags.Static | BindingFlags.NonPublic); 53 | var postfix = typeof(Injector).GetMethod(nameof(Postfix_Show), BindingFlags.Static | BindingFlags.NonPublic); 54 | harmony.Patch(method, usePrefix ? new HarmonyMethod(prefix) : null, !usePrefix ? new HarmonyMethod(postfix) : null); 55 | } 56 | else 57 | { 58 | UnityModManager.OpenUnityFileLog(); 59 | return; 60 | } 61 | } 62 | else 63 | { 64 | startUiWithManager = true; 65 | } 66 | 67 | if (!string.IsNullOrEmpty(UnityModManager.Config.StartingPoint)) 68 | { 69 | if (!doorstop && UnityModManager.Config.StartingPoint == UnityModManager.Config.EntryPoint) 70 | { 71 | UnityModManager.Start(); 72 | if (startUiWithManager) 73 | { 74 | RunUI(); 75 | } 76 | } 77 | else 78 | { 79 | if (TryGetEntryPoint(UnityModManager.Config.StartingPoint, out var @class, out var method, out var place)) 80 | { 81 | var usePrefix = (place == "before"); 82 | var harmony = new HarmonyLib.Harmony(nameof(UnityModManager)); 83 | var prefix = typeof(Injector).GetMethod(nameof(Prefix_Start), BindingFlags.Static | BindingFlags.NonPublic); 84 | var postfix = typeof(Injector).GetMethod(nameof(Postfix_Start), BindingFlags.Static | BindingFlags.NonPublic); 85 | harmony.Patch(method, usePrefix ? new HarmonyMethod(prefix) : null, !usePrefix ? new HarmonyMethod(postfix) : null); 86 | UnityModManager.Logger.Log("Injection successful."); 87 | } 88 | else 89 | { 90 | UnityModManager.Logger.Log("Injection canceled."); 91 | UnityModManager.OpenUnityFileLog(); 92 | return; 93 | } 94 | } 95 | } 96 | else 97 | { 98 | if (startUiWithManager) 99 | { 100 | UnityModManager.Logger.Error($"Can't start UI. UIStartingPoint is not defined."); 101 | UnityModManager.OpenUnityFileLog(); 102 | return; 103 | } 104 | UnityModManager.Start(); 105 | } 106 | 107 | if (!string.IsNullOrEmpty(UnityModManager.Config.TextureReplacingPoint)) 108 | { 109 | if (TryGetEntryPoint(UnityModManager.Config.TextureReplacingPoint, out var @class, out var method, out var place)) 110 | { 111 | var usePrefix = (place == "before"); 112 | var harmony = new HarmonyLib.Harmony(nameof(UnityModManager)); 113 | var prefix = typeof(Injector).GetMethod(nameof(Prefix_TextureReplacing), BindingFlags.Static | BindingFlags.NonPublic); 114 | var postfix = typeof(Injector).GetMethod(nameof(Postfix_TextureReplacing), BindingFlags.Static | BindingFlags.NonPublic); 115 | harmony.Patch(method, usePrefix ? new HarmonyMethod(prefix) : null, !usePrefix ? new HarmonyMethod(postfix) : null); 116 | } 117 | else 118 | { 119 | UnityModManager.OpenUnityFileLog(); 120 | } 121 | } 122 | 123 | if (!string.IsNullOrEmpty(UnityModManager.Config.SessionStartPoint)) 124 | { 125 | if (TryGetEntryPoint(UnityModManager.Config.SessionStartPoint, out var @class, out var method, out var place)) 126 | { 127 | var usePrefix = (place == "before"); 128 | var harmony = new HarmonyLib.Harmony(nameof(UnityModManager)); 129 | var prefix = typeof(Injector).GetMethod(nameof(Prefix_SessionStart), BindingFlags.Static | BindingFlags.NonPublic); 130 | var postfix = typeof(Injector).GetMethod(nameof(Postfix_SessionStart), BindingFlags.Static | BindingFlags.NonPublic); 131 | harmony.Patch(method, usePrefix ? new HarmonyMethod(prefix) : null, !usePrefix ? new HarmonyMethod(postfix) : null); 132 | } 133 | else 134 | { 135 | UnityModManager.Config.SessionStartPoint = null; 136 | UnityModManager.OpenUnityFileLog(); 137 | } 138 | } 139 | 140 | if (!string.IsNullOrEmpty(UnityModManager.Config.SessionStopPoint)) 141 | { 142 | if (TryGetEntryPoint(UnityModManager.Config.SessionStopPoint, out var @class, out var method, out var place)) 143 | { 144 | var usePrefix = (place == "before"); 145 | var harmony = new HarmonyLib.Harmony(nameof(UnityModManager)); 146 | var prefix = typeof(Injector).GetMethod(nameof(Prefix_SessionStop), BindingFlags.Static | BindingFlags.NonPublic); 147 | var postfix = typeof(Injector).GetMethod(nameof(Postfix_SessionStop), BindingFlags.Static | BindingFlags.NonPublic); 148 | harmony.Patch(method, usePrefix ? new HarmonyMethod(prefix) : null, !usePrefix ? new HarmonyMethod(postfix) : null); 149 | } 150 | else 151 | { 152 | UnityModManager.Config.SessionStopPoint = null; 153 | UnityModManager.OpenUnityFileLog(); 154 | } 155 | } 156 | } 157 | 158 | static void RunUI() 159 | { 160 | if (!UnityModManager.UI.Load()) 161 | { 162 | UnityModManager.Logger.Error($"Can't load UI."); 163 | } 164 | UnityModManager.UI.Instance.FirstLaunch(); 165 | } 166 | 167 | static void Prefix_Start() 168 | { 169 | UnityModManager.Start(); 170 | if (startUiWithManager) 171 | { 172 | RunUI(); 173 | } 174 | } 175 | 176 | static void Postfix_Start() 177 | { 178 | UnityModManager.Start(); 179 | if (startUiWithManager) 180 | { 181 | RunUI(); 182 | } 183 | } 184 | 185 | static void Prefix_Show() 186 | { 187 | if (!UnityModManager.UI.Load()) 188 | { 189 | UnityModManager.Logger.Error($"Can't load UI."); 190 | } 191 | if (!UnityModManager.UI.Instance) 192 | { 193 | UnityModManager.Logger.Error("UnityModManager.UI does not exist."); 194 | return; 195 | } 196 | UnityModManager.UI.Instance.FirstLaunch(); 197 | } 198 | 199 | static void Postfix_Show() 200 | { 201 | if (!UnityModManager.UI.Load()) 202 | { 203 | UnityModManager.Logger.Error($"Can't load UI."); 204 | } 205 | if (!UnityModManager.UI.Instance) 206 | { 207 | UnityModManager.Logger.Error("UnityModManager.UI does not exist."); 208 | return; 209 | } 210 | UnityModManager.UI.Instance.FirstLaunch(); 211 | } 212 | 213 | static void Prefix_TextureReplacing() 214 | { 215 | //UnityModManager.ApplySkins(); 216 | } 217 | 218 | static void Postfix_TextureReplacing() 219 | { 220 | //UnityModManager.ApplySkins(); 221 | } 222 | 223 | static void Prefix_SessionStart() 224 | { 225 | foreach (var mod in UnityModManager.modEntries) 226 | { 227 | if (mod.Active && mod.OnSessionStart != null) 228 | { 229 | try 230 | { 231 | mod.OnSessionStart.Invoke(mod); 232 | } 233 | catch (Exception e) 234 | { 235 | mod.Logger.LogException("OnSessionStart", e); 236 | } 237 | } 238 | } 239 | } 240 | 241 | static void Postfix_SessionStart() 242 | { 243 | Prefix_SessionStart(); 244 | } 245 | 246 | static void Prefix_SessionStop() 247 | { 248 | foreach (var mod in UnityModManager.modEntries) 249 | { 250 | if (mod.Active && mod.OnSessionStop != null) 251 | { 252 | try 253 | { 254 | mod.OnSessionStop.Invoke(mod); 255 | } 256 | catch (Exception e) 257 | { 258 | mod.Logger.LogException("OnSessionStop", e); 259 | } 260 | } 261 | } 262 | } 263 | 264 | static void Postfix_SessionStop() 265 | { 266 | Prefix_SessionStop(); 267 | } 268 | 269 | internal static bool TryGetEntryPoint(string str, out Type foundClass, out MethodInfo foundMethod, out string insertionPlace) 270 | { 271 | foundClass = null; 272 | foundMethod = null; 273 | insertionPlace = null; 274 | 275 | if (TryParseEntryPoint(str, out string assemblyName, out _, out _, out _)) 276 | { 277 | foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 278 | { 279 | if (assembly.ManifestModule.Name == assemblyName) 280 | { 281 | return TryGetEntryPoint(assembly, str, out foundClass, out foundMethod, out insertionPlace); 282 | } 283 | } 284 | try 285 | { 286 | var asm = Assembly.Load(assemblyName); 287 | return TryGetEntryPoint(asm, str, out foundClass, out foundMethod, out insertionPlace); 288 | } 289 | catch (Exception e) 290 | { 291 | UnityModManager.Logger.Error($"File '{assemblyName}' cant't be loaded."); 292 | UnityModManager.Logger.LogException(e); 293 | } 294 | 295 | return false; 296 | } 297 | 298 | return false; 299 | } 300 | 301 | internal static bool TryGetEntryPoint(Assembly assembly, string str, out Type foundClass, out MethodInfo foundMethod, out string insertionPlace) 302 | { 303 | foundClass = null; 304 | foundMethod = null; 305 | 306 | if (!TryParseEntryPoint(str, out _, out var className, out var methodName, out insertionPlace)) 307 | { 308 | return false; 309 | } 310 | 311 | foundClass = assembly.GetType(className); 312 | if (foundClass == null) 313 | { 314 | UnityModManager.Logger.Error($"Class '{className}' not found."); 315 | return false; 316 | } 317 | 318 | foundMethod = foundClass.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); 319 | if (foundMethod == null) 320 | { 321 | UnityModManager.Logger.Error($"Method '{methodName}' not found."); 322 | return false; 323 | } 324 | 325 | return true; 326 | } 327 | 328 | internal static bool TryParseEntryPoint(string str, out string assembly, out string @class, out string method, out string insertionPlace) 329 | { 330 | assembly = string.Empty; 331 | @class = string.Empty; 332 | method = string.Empty; 333 | insertionPlace = string.Empty; 334 | 335 | var regex = new Regex(@"(?:(?<=\[)(?'assembly'.+(?>\.dll))(?=\]))|(?:(?'class'[\w|\.]+)(?=\.))|(?:(?<=\.)(?'func'\w+))|(?:(?<=\:)(?'mod'\w+))", RegexOptions.IgnoreCase); 336 | var matches = regex.Matches(str); 337 | var groupNames = regex.GetGroupNames(); 338 | 339 | if (matches.Count > 0) 340 | { 341 | foreach (Match match in matches) 342 | { 343 | foreach (var group in groupNames) 344 | { 345 | if (match.Groups[group].Success) 346 | { 347 | switch (group) 348 | { 349 | case "assembly": 350 | assembly = match.Groups[group].Value; 351 | break; 352 | case "class": 353 | @class = match.Groups[group].Value; 354 | break; 355 | case "func": 356 | method = match.Groups[group].Value; 357 | if (method == "ctor") 358 | method = ".ctor"; 359 | else if (method == "cctor") 360 | method = ".cctor"; 361 | break; 362 | case "mod": 363 | insertionPlace = match.Groups[group].Value.ToLower(); 364 | break; 365 | } 366 | } 367 | } 368 | } 369 | } 370 | 371 | var hasError = false; 372 | 373 | if (string.IsNullOrEmpty(assembly)) 374 | { 375 | hasError = true; 376 | UnityModManager.Logger.Error("Assembly name not found."); 377 | } 378 | 379 | if (string.IsNullOrEmpty(@class)) 380 | { 381 | hasError = true; 382 | UnityModManager.Logger.Error("Class name not found."); 383 | } 384 | 385 | if (string.IsNullOrEmpty(method)) 386 | { 387 | hasError = true; 388 | UnityModManager.Logger.Error("Method name not found."); 389 | } 390 | 391 | if (hasError) 392 | { 393 | UnityModManager.Logger.Error($"Error parsing EntryPoint '{str}'."); 394 | return false; 395 | } 396 | 397 | return true; 398 | } 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /UnityModManager/Log.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | namespace UnityModManagerNet 8 | { 9 | public partial class UnityModManager 10 | { 11 | public partial class ModEntry 12 | { 13 | public class ModLogger 14 | { 15 | protected readonly string Prefix; 16 | protected readonly string PrefixError; 17 | protected readonly string PrefixCritical; 18 | protected readonly string PrefixWarning; 19 | protected readonly string PrefixException; 20 | 21 | public ModLogger(string Id) 22 | { 23 | Prefix = $"[{Id}] "; 24 | PrefixError = $"[{Id}] [Error] "; 25 | PrefixCritical = $"[{Id}] [Critical] "; 26 | PrefixWarning = $"[{Id}] [Warning] "; 27 | PrefixException = $"[{Id}] [Exception] "; 28 | } 29 | 30 | public void Log(string str) 31 | { 32 | UnityModManager.Logger.Log(str, Prefix); 33 | } 34 | 35 | public void Error(string str) 36 | { 37 | UnityModManager.Logger.Log(str, PrefixError); 38 | } 39 | 40 | public void Critical(string str) 41 | { 42 | UnityModManager.Logger.Log(str, PrefixCritical); 43 | } 44 | 45 | public void Warning(string str) 46 | { 47 | UnityModManager.Logger.Log(str, PrefixWarning); 48 | } 49 | 50 | public void NativeLog(string str) 51 | { 52 | UnityModManager.Logger.NativeLog(str, Prefix); 53 | } 54 | 55 | /// 56 | /// [0.17.0] 57 | /// 58 | public void LogException(string key, Exception e) 59 | { 60 | UnityModManager.Logger.LogException(key, e, PrefixException); 61 | } 62 | 63 | /// 64 | /// [0.17.0] 65 | /// 66 | public void LogException(Exception e) 67 | { 68 | UnityModManager.Logger.LogException(null, e, PrefixException); 69 | } 70 | } 71 | } 72 | 73 | public static class Logger 74 | { 75 | const string Prefix = "[Manager] "; 76 | const string PrefixError = "[Manager] [Error] "; 77 | const string PrefixException = "[Manager] [Exception] "; 78 | 79 | public static readonly string filepath = Path.Combine(Path.GetDirectoryName(typeof(GameInfo).Assembly.Location), "Log.txt"); 80 | 81 | public static void NativeLog(string str) 82 | { 83 | NativeLog(str, Prefix); 84 | } 85 | 86 | public static void NativeLog(string str, string prefix) 87 | { 88 | Write(prefix + str, true); 89 | } 90 | 91 | public static void Log(string str) 92 | { 93 | Log(str, Prefix); 94 | } 95 | 96 | public static void Log(string str, string prefix) 97 | { 98 | Write(prefix + str); 99 | } 100 | 101 | public static void Error(string str) 102 | { 103 | Error(str, PrefixError); 104 | } 105 | 106 | public static void Error(string str, string prefix) 107 | { 108 | Write(prefix + str); 109 | } 110 | 111 | /// 112 | /// [0.17.0] 113 | /// 114 | public static void LogException(Exception e) 115 | { 116 | LogException(null, e, PrefixException); 117 | } 118 | 119 | /// 120 | /// [0.17.0] 121 | /// 122 | public static void LogException(string key, Exception e) 123 | { 124 | LogException(key, e, PrefixException); 125 | } 126 | 127 | /// 128 | /// [0.17.0] 129 | /// 130 | public static void LogException(string key, Exception e, string prefix) 131 | { 132 | if (string.IsNullOrEmpty(key)) 133 | Write($"{prefix}{e.GetType().Name} - {e.Message}"); 134 | else 135 | Write($"{prefix}{key}: {e.GetType().Name} - {e.Message}"); 136 | Console.WriteLine(e.ToString()); 137 | } 138 | 139 | private static bool hasErrors; 140 | private static int bufferCapacity = 100; 141 | private static List buffer = new List(bufferCapacity); 142 | internal static int historyCapacity = 200; 143 | internal static List history = new List(historyCapacity * 2); 144 | 145 | private static void Write(string str, bool onlyNative = false) 146 | { 147 | if (str == null) 148 | return; 149 | 150 | Console.WriteLine(str); 151 | 152 | if (onlyNative) 153 | return; 154 | 155 | buffer.Add(str); 156 | history.Add(str); 157 | 158 | if (history.Count >= historyCapacity * 2) 159 | { 160 | var result = history.Skip(historyCapacity).ToArray(); 161 | history.Clear(); 162 | history.AddRange(result); 163 | } 164 | } 165 | 166 | private static float timer; 167 | 168 | internal static void Watcher(float dt) 169 | { 170 | if (buffer.Count >= bufferCapacity || timer > 0.5f) 171 | { 172 | WriteBuffers(); 173 | } 174 | else 175 | { 176 | timer += dt; 177 | } 178 | } 179 | 180 | internal static void WriteBuffers() 181 | { 182 | try 183 | { 184 | if (buffer.Count > 0 && !hasErrors) 185 | { 186 | if (!File.Exists(filepath)) 187 | { 188 | using (File.Create(filepath)) 189 | {; } 190 | } 191 | using (StreamWriter writer = File.AppendText(filepath)) 192 | { 193 | foreach (var str in buffer) 194 | { 195 | writer.WriteLine(str); 196 | } 197 | } 198 | } 199 | } 200 | catch (UnauthorizedAccessException e) 201 | { 202 | hasErrors = true; 203 | Console.WriteLine(PrefixException + e.ToString()); 204 | Console.WriteLine(Prefix + "Uncheck the read-only box from the UnityModManager folder."); 205 | history.Add(PrefixException + e.ToString()); 206 | history.Add(Prefix + "Uncheck the read-only box from the UnityModManager folder."); 207 | } 208 | catch (Exception e) 209 | { 210 | hasErrors = true; 211 | Console.WriteLine(PrefixException + e.ToString()); 212 | history.Add(PrefixException + e.ToString()); 213 | } 214 | 215 | buffer.Clear(); 216 | timer = 0; 217 | } 218 | 219 | public static void Clear() 220 | { 221 | buffer.Clear(); 222 | history.Clear(); 223 | if (File.Exists(filepath) && !hasErrors) 224 | { 225 | try 226 | { 227 | File.Delete(filepath); 228 | } 229 | catch (UnauthorizedAccessException e) 230 | { 231 | hasErrors = true; 232 | Console.WriteLine(PrefixException + e.ToString()); 233 | Console.WriteLine(Prefix + "Uncheck the read-only box from the UnityModManager folder."); 234 | history.Add(PrefixException + e.ToString()); 235 | history.Add(Prefix + "Uncheck the read-only box from the UnityModManager folder."); 236 | } 237 | catch (Exception e) 238 | { 239 | hasErrors = true; 240 | Console.WriteLine(PrefixException + e.ToString()); 241 | history.Add(PrefixException + e.ToString()); 242 | } 243 | } 244 | } 245 | } 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /UnityModManager/ModInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace UnityModManagerNet 7 | { 8 | public partial class UnityModManager 9 | { 10 | public class ModInfo : IEquatable 11 | { 12 | public string Id; 13 | 14 | public string DisplayName; 15 | 16 | public string Author; 17 | 18 | public string Version; 19 | 20 | public string ManagerVersion; 21 | 22 | public string GameVersion; 23 | 24 | public string[] Requirements; 25 | 26 | public string[] LoadAfter; 27 | 28 | public string AssemblyName; 29 | 30 | public string EntryMethod; 31 | 32 | public string HomePage; 33 | 34 | public string Repository; 35 | 36 | public string ContentType; 37 | 38 | /// 39 | /// Used for RoR2 game [0.17.0] 40 | /// 41 | [NonSerialized] 42 | public bool IsCheat = true; 43 | 44 | public static implicit operator bool(ModInfo exists) 45 | { 46 | return exists != null; 47 | } 48 | 49 | public bool Equals(ModInfo other) 50 | { 51 | return Id.Equals(other.Id); 52 | } 53 | 54 | public override bool Equals(object obj) 55 | { 56 | if (ReferenceEquals(null, obj)) 57 | { 58 | return false; 59 | } 60 | return obj is ModInfo modInfo && Equals(modInfo); 61 | } 62 | 63 | public override int GetHashCode() 64 | { 65 | return Id.GetHashCode(); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /UnityModManager/ModSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Xml.Serialization; 7 | 8 | namespace UnityModManagerNet 9 | { 10 | public partial class UnityModManager 11 | { 12 | public class ModSettings 13 | { 14 | /// 15 | /// By default, it will save the data to the Mods/%Name%/Settings.xml [0.31.1] 16 | /// 17 | /// 18 | /// Before version [0.31.1] it was necessary to override 19 | /// 20 | public virtual void Save(ModEntry modEntry) 21 | { 22 | Save(this, modEntry); 23 | } 24 | 25 | public virtual string GetPath(ModEntry modEntry) 26 | { 27 | return Path.Combine(modEntry.Path, "Settings.xml"); 28 | } 29 | 30 | public static void Save(T data, ModEntry modEntry) where T : ModSettings, new() 31 | { 32 | Save(data, modEntry, null); 33 | } 34 | 35 | /// 36 | /// [0.20.0] 37 | /// 38 | public static void Save(T data, ModEntry modEntry, XmlAttributeOverrides attributes) where T : ModSettings, new() 39 | { 40 | var filepath = data.GetPath(modEntry); 41 | try 42 | { 43 | using (var writer = new StreamWriter(filepath)) 44 | { 45 | var serializer = new XmlSerializer(data.GetType(), attributes); 46 | serializer.Serialize(writer, data); 47 | } 48 | } 49 | catch (Exception e) 50 | { 51 | modEntry.Logger.Error($"Can't save {filepath}."); 52 | modEntry.Logger.LogException(e); 53 | } 54 | } 55 | 56 | public static T Load(ModEntry modEntry) where T : ModSettings, new() 57 | { 58 | var t = new T(); 59 | var filepath = t.GetPath(modEntry); 60 | if (File.Exists(filepath)) 61 | { 62 | try 63 | { 64 | using (var stream = File.OpenRead(filepath)) 65 | { 66 | var serializer = new XmlSerializer(typeof(T)); 67 | var result = (T)serializer.Deserialize(stream); 68 | return result; 69 | } 70 | } 71 | catch (Exception e) 72 | { 73 | modEntry.Logger.Error($"Can't read {filepath}."); 74 | modEntry.Logger.LogException(e); 75 | } 76 | } 77 | 78 | return t; 79 | } 80 | 81 | public static T Load(ModEntry modEntry, XmlAttributeOverrides attributes) where T : ModSettings, new() 82 | { 83 | var t = new T(); 84 | var filepath = t.GetPath(modEntry); 85 | if (File.Exists(filepath)) 86 | { 87 | try 88 | { 89 | using (var stream = File.OpenRead(filepath)) 90 | { 91 | var serializer = new XmlSerializer(typeof(T), attributes); 92 | var result = (T)serializer.Deserialize(stream); 93 | return result; 94 | } 95 | } 96 | catch (Exception e) 97 | { 98 | modEntry.Logger.Error($"Can't read {filepath}."); 99 | modEntry.Logger.LogException(e); 100 | } 101 | } 102 | 103 | return t; 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /UnityModManager/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityModManagerNet.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityModManagerNet.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性 51 | /// 重写当前线程的 CurrentUICulture 属性。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /UnityModManager/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /UnityModManager/Repository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace UnityModManagerNet 7 | { 8 | public partial class UnityModManager 9 | { 10 | public class Repository 11 | { 12 | [Serializable] 13 | public class Release : IEquatable 14 | { 15 | public string Id; 16 | public string Version; 17 | public string DownloadUrl; 18 | 19 | public bool Equals(Release other) 20 | { 21 | return Id.Equals(other.Id); 22 | } 23 | 24 | public override bool Equals(object obj) 25 | { 26 | if (ReferenceEquals(null, obj)) 27 | { 28 | return false; 29 | } 30 | return obj is Release obj2 && Equals(obj2); 31 | } 32 | 33 | public override int GetHashCode() 34 | { 35 | return Id.GetHashCode(); 36 | } 37 | } 38 | 39 | public Release[] Releases; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /UnityModManager/TextureReplacer.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Xml.Serialization; 9 | using TinyJson; 10 | using UnityEngine; 11 | using static UnityModManagerNet.UnityModManager.TextureReplacer; 12 | 13 | namespace UnityModManagerNet 14 | { 15 | public partial class UnityModManager 16 | { 17 | internal class TextureReplacer 18 | { 19 | public class Skin 20 | { 21 | public struct conditions 22 | { 23 | public string MaterialName; 24 | public string ObjectName; 25 | public string ObjectComponent; 26 | public string Custom; 27 | [IgnoreJson] public bool IsEmpty => string.IsNullOrEmpty(MaterialName) && string.IsNullOrEmpty(ObjectName) && string.IsNullOrEmpty(ObjectComponent) && string.IsNullOrEmpty(Custom); 28 | } 29 | public class texture 30 | { 31 | public string Path; 32 | public Texture2D Texture; 33 | public Texture2D Previous; 34 | } 35 | 36 | [IgnoreJson] public ModEntry modEntry; 37 | public string Name; 38 | public string Tags; 39 | public conditions Conditions; 40 | [IgnoreJson] public Dictionary textures; 41 | 42 | public override string ToString() 43 | { 44 | return $"{Name} ({modEntry.Info.DisplayName})"; 45 | } 46 | 47 | public void WriteFile(string filePath) 48 | { 49 | try 50 | { 51 | File.WriteAllText(filePath, JSONWriter.ToJson(this)); 52 | } 53 | catch (Exception e) 54 | { 55 | Logger.Error(e.ToString()); 56 | Logger.Error($"Error file creating '{filePath}'."); 57 | } 58 | } 59 | 60 | public static Skin ReadFile(string filePath) 61 | { 62 | try 63 | { 64 | return JSONParser.FromJson(File.ReadAllText(filePath)); 65 | } 66 | catch (Exception e) 67 | { 68 | Logger.Error(e.ToString()); 69 | Logger.Error($"Can't read file '{filePath}'."); 70 | return null; 71 | } 72 | } 73 | 74 | public static implicit operator bool(Skin exists) 75 | { 76 | return exists != null; 77 | } 78 | 79 | public bool Equals(Skin other) 80 | { 81 | return Name.Equals(other.Name) && modEntry.Info.Equals(other.modEntry.Info); 82 | } 83 | 84 | public override bool Equals(object obj) 85 | { 86 | if (ReferenceEquals(null, obj)) 87 | { 88 | return false; 89 | } 90 | return obj is Skin skin && Equals(skin); 91 | } 92 | 93 | public override int GetHashCode() 94 | { 95 | return Name.GetHashCode() + modEntry.Info.GetHashCode(); 96 | } 97 | } 98 | 99 | public static void Start() 100 | { 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /UnityModManager/UnityModManager.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Library 7 | net35 8 | latest 9 | 10 | UnityModManager 11 | UnityModManagerNet 12 | Copyright © 2019-$([System.DateTime]::Now.ToString('yyyy')) 13 | 0.32.4 14 | 15 | 16 | 17 | true 18 | $(SolutionDir)\UnityModManager\bin\$(Configuration)\ 19 | en-001 20 | 0.32.4 21 | 0.32.4 22 | True 23 | UnityModManager 24 | newman55 25 | Mod manager for Unity games. 26 | https://www.nexusmods.com/site/mods/21 27 | https://github.com/newman55/unity-mod-manager/ 28 | unity3d mod manager 29 | MIT 30 | git 31 | 32 | 33 | 34 | 35 | 36 | 37 | all 38 | runtime; build; native; contentfiles; analyzers; buildtransitive 39 | 40 | 41 | 42 | 43 | 44 | ..\lib\UnityEngine.dll 45 | 46 | 47 | ..\lib\UnityEngine.UI.dll 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /UnityModManager/Updates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Net.NetworkInformation; 9 | using System.Reflection; 10 | using System.Text; 11 | using System.Threading; 12 | using UnityEngine; 13 | using UnityEngine.Events; 14 | using UnityEngine.Networking; 15 | using static UnityModManagerNet.UnityModManager; 16 | 17 | namespace UnityModManagerNet 18 | { 19 | public partial class UnityModManager 20 | { 21 | public class NexusModInfo 22 | { 23 | public string name; 24 | public int mod_id; 25 | public string domain_name; 26 | public string version; 27 | } 28 | 29 | private static void CheckModUpdates() 30 | { 31 | Logger.Log("Checking updates."); 32 | 33 | if (!HasNetworkConnection()) 34 | { 35 | Logger.Log("No network connection or firewall blocked."); 36 | return; 37 | } 38 | 39 | Params.LastUpdateCheck = DateTime.Now; 40 | 41 | var urls = new HashSet(); 42 | var nexusUrls = new HashSet(); 43 | 44 | foreach (var modEntry in modEntries) 45 | { 46 | if (!string.IsNullOrEmpty(modEntry.Info.Repository)) 47 | { 48 | urls.Add(modEntry.Info.Repository); 49 | } 50 | 51 | if (!string.IsNullOrEmpty(modEntry.Info.HomePage) && ParseNexusUrl(modEntry.Info.HomePage, out string nexusGame, out string nexusModId)) 52 | { 53 | nexusUrls.Add(modEntry.Info.HomePage); 54 | } 55 | } 56 | 57 | if (urls.Count > 0) 58 | { 59 | foreach (var url in urls) 60 | { 61 | if (unityVersion < new Version(5, 4)) 62 | { 63 | UI.Instance.StartCoroutine(DownloadString_5_3(url, ParseRepository)); 64 | } 65 | else 66 | { 67 | UI.Instance.StartCoroutine(DownloadString(url, ParseRepository)); 68 | } 69 | } 70 | } 71 | 72 | if (nexusUrls.Count > 0) 73 | { 74 | foreach (var url in nexusUrls) 75 | { 76 | if (unityVersion >= new Version(5, 4)) 77 | { 78 | UI.Instance.StartCoroutine(DownloadString(url, ParseNexus)); 79 | } 80 | } 81 | } 82 | } 83 | 84 | private static void ParseRepository(string json, string url) 85 | { 86 | if (string.IsNullOrEmpty(json)) 87 | { 88 | return; 89 | } 90 | 91 | try 92 | { 93 | var repository = TinyJson.JSONParser.FromJson(json); 94 | if (repository != null && repository.Releases != null && repository.Releases.Length > 0) 95 | { 96 | foreach (var release in repository.Releases) 97 | { 98 | if (!string.IsNullOrEmpty(release.Id) && !string.IsNullOrEmpty(release.Version)) 99 | { 100 | var modEntry = FindMod(release.Id); 101 | if (modEntry != null) 102 | { 103 | var ver = ParseVersion(release.Version); 104 | if (modEntry.Version < ver && (modEntry.NewestVersion == null || modEntry.NewestVersion < ver)) 105 | { 106 | modEntry.NewestVersion = ver; 107 | } 108 | } 109 | } 110 | } 111 | } 112 | } 113 | catch (Exception e) 114 | { 115 | Logger.Log(string.Format("Error checking mod updates on '{0}'.", url)); 116 | Logger.Log(e.Message); 117 | } 118 | } 119 | 120 | private static void ParseNexus(string json, string url) 121 | { 122 | if (string.IsNullOrEmpty(json)) 123 | { 124 | return; 125 | } 126 | 127 | try 128 | { 129 | var result = TinyJson.JSONParser.FromJson(json); 130 | if (result != null && !string.IsNullOrEmpty(result.version)) 131 | { 132 | var mod = modEntries.Find((x) => x.Info.HomePage == url); 133 | if (mod != null) 134 | { 135 | var ver = ParseVersion(result.version); 136 | if (mod.Version < ver && (mod.NewestVersion == null || mod.NewestVersion < ver)) 137 | { 138 | mod.NewestVersion = ver; 139 | } 140 | } 141 | } 142 | } 143 | catch (Exception e) 144 | { 145 | Logger.Log(string.Format("Error checking mod updates on '{0}'.", url)); 146 | Logger.Log(e.Message); 147 | } 148 | } 149 | 150 | public static bool HasNetworkConnection() 151 | { 152 | //try 153 | //{ 154 | // using (var ping = new System.Net.NetworkInformation.Ping()) 155 | // { 156 | // return ping.Send("8.8.8.8", 3000).Status == IPStatus.Success; 157 | // } 158 | //} 159 | //catch (Exception e) 160 | //{ 161 | // Console.WriteLine(e.Message); 162 | //} 163 | 164 | try 165 | { 166 | var timeout = 2000; 167 | var ping = new UnityEngine.Ping("8.8.8.8"); 168 | while (!ping.isDone) 169 | { 170 | Thread.Sleep(10); 171 | timeout -= 10; 172 | if (timeout <= 0) 173 | { 174 | return false; 175 | } 176 | } 177 | return true; 178 | } 179 | catch (Exception e) 180 | { 181 | Console.WriteLine(e.Message); 182 | } 183 | 184 | return false; 185 | } 186 | 187 | private static bool nexusApiSupportLogged; 188 | 189 | private static IEnumerator DownloadString(string url, UnityAction handler) 190 | { 191 | var orgUrl = url; 192 | var www = UnityWebRequest.Get(url); 193 | if (ParseNexusUrl(url, out string nexusGame, out string nexusModId)) 194 | { 195 | if (string.IsNullOrEmpty(InstallerParams.APIkey)) 196 | { 197 | if (!nexusApiSupportLogged) 198 | { 199 | nexusApiSupportLogged = true; 200 | Logger.Log($"The nexus api key is missing. Without it, you won't be able to check for updates. You can configure it via the UnityModManager Installer. If you don't need it, just ignore."); 201 | } 202 | yield break; 203 | } 204 | url = $"https://api.nexusmods.com/v1/games/{nexusGame}/mods/{nexusModId}.json"; 205 | www = UnityWebRequest.Get(url); 206 | www.SetRequestHeader("Content-Type", "application/json"); 207 | www.SetRequestHeader("apikey", InstallerParams.APIkey); 208 | www.SetRequestHeader("Application-Version", version.ToString()); 209 | www.SetRequestHeader("Application-Name", "UnityModManager"); 210 | } 211 | yield return www.Send(); 212 | 213 | MethodInfo isError; 214 | var ver = ParseVersion(Application.unityVersion); 215 | if (ver.Major >= 2017) 216 | { 217 | isError = typeof(UnityWebRequest).GetMethod("get_isNetworkError"); 218 | } 219 | else 220 | { 221 | isError = typeof(UnityWebRequest).GetMethod("get_isError"); 222 | } 223 | 224 | if (isError == null || (bool)isError.Invoke(www, null)) 225 | { 226 | Logger.Log(www.error); 227 | Logger.Log(string.Format("Error downloading '{0}'.", url)); 228 | yield break; 229 | } 230 | handler(www.downloadHandler.text, orgUrl); 231 | } 232 | 233 | private static IEnumerator DownloadString_5_3(string url, UnityAction handler) 234 | { 235 | var www = new WWW(url); 236 | yield return www; 237 | 238 | if (!string.IsNullOrEmpty(www.error)) 239 | { 240 | Logger.Log(www.error); 241 | Logger.Log(string.Format("Error downloading '{0}'.", url)); 242 | yield break; 243 | } 244 | 245 | handler(www.text, url); 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /UnityModManager/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text.RegularExpressions; 4 | using System.Threading; 5 | using UnityEngine; 6 | 7 | namespace UnityModManagerNet 8 | { 9 | public partial class UnityModManager 10 | { 11 | public static void OpenUnityFileLog() 12 | { 13 | new Thread(() => 14 | { 15 | Thread.CurrentThread.IsBackground = true; 16 | var folders = new string[] { Application.persistentDataPath, Application.dataPath }; 17 | var files = new string[] { "Player.log", "output_log.txt" }; 18 | foreach (var folder in folders) 19 | { 20 | foreach (var file in files) 21 | { 22 | var filepath = Path.Combine(folder, file); 23 | if (File.Exists(filepath)) 24 | { 25 | Thread.Sleep(500); 26 | Application.OpenURL(filepath); 27 | return; 28 | } 29 | } 30 | } 31 | }).Start(); 32 | } 33 | 34 | public static Version ParseVersion(string str) 35 | { 36 | var array = str.Split('.'); 37 | if (array.Length >= 4) 38 | { 39 | var regex = new Regex(@"\D"); 40 | return new Version(int.Parse(regex.Replace(array[0], "")), int.Parse(regex.Replace(array[1], "")), int.Parse(regex.Replace(array[2], "")), int.Parse(regex.Replace(array[3], ""))); 41 | } 42 | else if (array.Length >= 3) 43 | { 44 | var regex = new Regex(@"\D"); 45 | return new Version(int.Parse(regex.Replace(array[0], "")), int.Parse(regex.Replace(array[1], "")), int.Parse(regex.Replace(array[2], ""))); 46 | } 47 | else if (array.Length >= 2) 48 | { 49 | var regex = new Regex(@"\D"); 50 | return new Version(int.Parse(regex.Replace(array[0], "")), int.Parse(regex.Replace(array[1], ""))); 51 | } 52 | else if (array.Length >= 1) 53 | { 54 | var regex = new Regex(@"\D"); 55 | return new Version(int.Parse(regex.Replace(array[0], "")), 0); 56 | } 57 | 58 | Logger.Error($"Error parsing version {str}"); 59 | return new Version(); 60 | } 61 | 62 | public static bool ParseNexusUrl(string url, out string game, out string id) 63 | { 64 | game = null; 65 | id = null; 66 | var regex = new Regex(@"https:\/\/www\.nexusmods\.com\/(\w+)\/mods\/(\d+)", RegexOptions.IgnoreCase); 67 | var matches = regex.Matches(url); 68 | foreach (Match match in matches) 69 | { 70 | game = match.Groups[1].Value; 71 | id = match.Groups[2].Value; 72 | return true; 73 | } 74 | return false; 75 | } 76 | 77 | public static bool IsUnixPlatform() 78 | { 79 | int p = (int)Environment.OSVersion.Platform; 80 | return (p == 4) || (p == 6) || (p == 128); 81 | } 82 | 83 | public static bool IsMacPlatform() 84 | { 85 | int p = (int)Environment.OSVersion.Platform; 86 | return (p == 6); 87 | } 88 | 89 | public static bool IsLinuxPlatform() 90 | { 91 | int p = (int)Environment.OSVersion.Platform; 92 | return (p == 4) || (p == 128); 93 | } 94 | } 95 | 96 | /// 97 | /// [0.18.0] 98 | /// 99 | public interface ICopyable 100 | { 101 | } 102 | 103 | /// 104 | /// [0.18.0] 105 | /// 106 | [Flags] 107 | public enum CopyFieldMask { Any = 0, Matching = 1, Public = 2, Serialized = 4, SkipNotSerialized = 8, OnlyCopyAttr = 16 }; 108 | 109 | /// 110 | /// [0.18.0] 111 | /// 112 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)] 113 | public class CopyFieldsAttribute : Attribute 114 | { 115 | public CopyFieldMask Mask; 116 | 117 | public CopyFieldsAttribute(CopyFieldMask Mask) 118 | { 119 | this.Mask = Mask; 120 | } 121 | } 122 | 123 | /// 124 | /// [0.18.0] 125 | /// 126 | [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] 127 | public class CopyAttribute : Attribute 128 | { 129 | public string Alias; 130 | 131 | public CopyAttribute() 132 | { 133 | } 134 | 135 | public CopyAttribute(string Alias) 136 | { 137 | this.Alias = Alias; 138 | } 139 | } 140 | 141 | public static partial class Extensions 142 | { 143 | /// 144 | /// [0.18.0] 145 | /// 146 | public static void CopyFieldsTo(this T1 from, ref T2 to) 147 | where T1 : ICopyable, new() 148 | where T2 : new() 149 | { 150 | object obj = to; 151 | Utils.CopyFields(from, obj, CopyFieldMask.OnlyCopyAttr); 152 | to = (T2)obj; 153 | } 154 | } 155 | 156 | public static partial class Utils 157 | { 158 | /// 159 | /// [0.18.0] 160 | /// 161 | public static void CopyFields(object from, object to, CopyFieldMask defaultMask) 162 | where T1 : new() 163 | where T2 : new() 164 | { 165 | CopyFieldMask mask = defaultMask; 166 | foreach (CopyFieldsAttribute attr in typeof(T1).GetCustomAttributes(typeof(CopyFieldsAttribute), false)) 167 | { 168 | mask = attr.Mask; 169 | } 170 | 171 | var fields = typeof(T1).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); 172 | foreach (var f in fields) 173 | { 174 | CopyAttribute a = new CopyAttribute(); 175 | var attributes = f.GetCustomAttributes(typeof(CopyAttribute), false); 176 | if (attributes.Length > 0) 177 | { 178 | foreach (CopyAttribute a_ in attributes) 179 | { 180 | a = a_; 181 | } 182 | } 183 | else 184 | { 185 | if ((mask & CopyFieldMask.OnlyCopyAttr) == 0 && ((mask & CopyFieldMask.SkipNotSerialized) == 0 || !f.IsNotSerialized) 186 | && ((mask & CopyFieldMask.Public) > 0 && f.IsPublic 187 | || (mask & CopyFieldMask.Serialized) > 0 && f.GetCustomAttributes(typeof(SerializeField), false).Length > 0 188 | || (mask & CopyFieldMask.Public) == 0 && (mask & CopyFieldMask.Serialized) == 0)) 189 | { 190 | } 191 | else 192 | { 193 | continue; 194 | } 195 | } 196 | 197 | if (string.IsNullOrEmpty(a.Alias)) 198 | a.Alias = f.Name; 199 | 200 | var f2 = typeof(T2).GetField(a.Alias, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); 201 | if (f2 == null) 202 | { 203 | if ((mask & CopyFieldMask.Matching) == 0) 204 | UnityModManager.Logger.Error($"Field '{typeof(T2).Name}.{a.Alias}' not found"); 205 | continue; 206 | } 207 | if (f.FieldType != f2.FieldType) 208 | { 209 | UnityModManager.Logger.Error($"Fields '{typeof(T1).Name}.{f.Name}' and '{typeof(T2).Name}.{f2.Name}' have different types"); 210 | continue; 211 | } 212 | f2.SetValue(to, f.GetValue(from)); 213 | } 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /UnityModManager/Window_GUI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | namespace UnityModManagerNet 8 | { 9 | public partial class UnityModManager 10 | { 11 | public partial class UI 12 | { 13 | internal class WindowParams 14 | { 15 | public int? Width { get; set; } 16 | public int? Height { get; set; } 17 | } 18 | 19 | internal class Window_GUI 20 | { 21 | internal static readonly List mList = new List(); 22 | internal readonly HashSet mDestroyCounter = new HashSet(); 23 | 24 | private const int MARGIN = 50; 25 | 26 | private int mId; 27 | private Rect mWindowRect; 28 | private Vector2 mScrollPosition; 29 | private int mWidth; 30 | private int mHeight; 31 | private int mRecalculateFrame; 32 | 33 | private bool Recalculating 34 | { 35 | get { return mRecalculateFrame == Time.frameCount; } 36 | } 37 | 38 | private bool mOpened; 39 | public bool Opened 40 | { 41 | get { return mOpened; } 42 | internal set 43 | { 44 | mOpened = value; 45 | if (value) 46 | { 47 | Reset(); 48 | } 49 | } 50 | } 51 | 52 | public WindowParams Params { get; internal set; } 53 | 54 | internal string title; 55 | internal int unique; 56 | internal Action onGui; 57 | internal Action onClose; 58 | 59 | public Window_GUI(Action onGui, Action onClose, string title, int unique, WindowParams @params = null) 60 | { 61 | mId = GetNextWindowId(); 62 | mList.Add(this); 63 | this.onGui = onGui; 64 | this.onClose = onClose; 65 | this.unique = unique; 66 | this.title = title; 67 | Params = @params ?? new WindowParams(); 68 | } 69 | 70 | public void Render() 71 | { 72 | if (Recalculating) 73 | { 74 | mWindowRect = GUILayout.Window(mId, mWindowRect, WindowFunction, "", window); 75 | if (mWindowRect.width > 0) 76 | { 77 | mWidth = (int)(Math.Min(Params.Width ?? mWindowRect.width, Screen.width - MARGIN * 2)); 78 | mHeight = (int)(Math.Min(Params.Height ?? mWindowRect.height, Screen.height - MARGIN * 2)); 79 | mWindowRect.x = (int)(Math.Max(Screen.width - mWidth, 0) / 2); 80 | mWindowRect.y = (int)(Math.Max(Screen.height - mHeight, 0) / 2); 81 | } 82 | } 83 | else 84 | { 85 | mWindowRect = GUILayout.Window(mId, mWindowRect, WindowFunction, "", window, GUILayout.Width(mWidth), GUILayout.Height(mHeight + 10)); 86 | GUI.BringWindowToFront(mId); 87 | } 88 | } 89 | 90 | private void WindowFunction(int windowId) 91 | { 92 | if (title != null) 93 | GUILayout.Label(title, h1); 94 | if (!Recalculating) 95 | mScrollPosition = GUILayout.BeginScrollView(mScrollPosition); 96 | onGui.Invoke(this); 97 | if (!Recalculating) 98 | GUILayout.EndScrollView(); 99 | //if (GUILayout.Button("Close", button)) 100 | // Opened = false; 101 | } 102 | 103 | internal void Reset() 104 | { 105 | mRecalculateFrame = Time.frameCount; 106 | mWindowRect = new Rect(-9000, 0, 0, 0); 107 | } 108 | 109 | /// 110 | /// [] 111 | /// 112 | public void Close() 113 | { 114 | if (!Opened) return; 115 | 116 | Opened = false; 117 | 118 | if (onClose != null) 119 | { 120 | try 121 | { 122 | onClose(); 123 | } 124 | catch (Exception e) 125 | { 126 | Logger.Error("Window.OnClose: " + e.GetType() + " - " + e.Message); 127 | Console.WriteLine(e.ToString()); 128 | } 129 | } 130 | } 131 | } 132 | 133 | /// 134 | /// Creates and displays window. Caches by title or unique. (deferred) [] 135 | /// 136 | internal static void ShowWindow(Action onGui, string title, int unique, WindowParams windowParams = null) 137 | { 138 | ShowWindow(onGui, null, title, unique, windowParams); 139 | } 140 | 141 | /// 142 | /// Creates and displays window. Caches by title or unique saving window parameters. (deferred) [] 143 | /// 144 | internal static void ShowWindow(Action onGui, Action onClose, string title, int unique, WindowParams windowParams = null) 145 | { 146 | if (onGui == null) 147 | { 148 | throw new ArgumentNullException("onGui"); 149 | } 150 | 151 | Window_GUI obj = null; 152 | foreach (var item in Window_GUI.mList) 153 | { 154 | if (unique == 0 && item.title == title || unique != 0 && item.unique == unique) 155 | { 156 | item.Close(); 157 | obj = item; 158 | obj.title = title; 159 | obj.onGui = onGui; 160 | obj.onClose = onClose; 161 | break; 162 | } 163 | } 164 | if (obj == null) 165 | { 166 | obj = new Window_GUI(onGui, onClose, title, unique, windowParams); 167 | } 168 | obj.Opened = true; 169 | } 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /UnityModManagerApp/DownloadExtraFiles.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UnityModManagerNet.Installer 2 | { 3 | partial class DownloadExtraFiles 4 | { 5 | /// 6 | /// Обязательная переменная конструктора. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Освободить все используемые ресурсы. 12 | /// 13 | /// истинно, если управляемый ресурс должен быть удален; иначе ложно. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Код, автоматически созданный конструктором форм Windows 24 | 25 | /// 26 | /// Требуемый метод для поддержки конструктора — не изменяйте 27 | /// содержимое этого метода с помощью редактора кода. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.status = new System.Windows.Forms.Label(); 32 | this.progressBar1 = new System.Windows.Forms.ProgressBar(); 33 | this.SuspendLayout(); 34 | // 35 | // status 36 | // 37 | this.status.Location = new System.Drawing.Point(12, 9); 38 | this.status.Name = "status"; 39 | this.status.Size = new System.Drawing.Size(306, 43); 40 | this.status.TabIndex = 2; 41 | this.status.Text = "Downloading..."; 42 | this.status.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 43 | // 44 | // progressBar1 45 | // 46 | this.progressBar1.Location = new System.Drawing.Point(12, 55); 47 | this.progressBar1.Name = "progressBar1"; 48 | this.progressBar1.Size = new System.Drawing.Size(306, 23); 49 | this.progressBar1.TabIndex = 3; 50 | // 51 | // DownloadExtraFiles 52 | // 53 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 54 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 55 | this.ClientSize = new System.Drawing.Size(330, 94); 56 | this.Controls.Add(this.progressBar1); 57 | this.Controls.Add(this.status); 58 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 59 | this.MaximizeBox = false; 60 | this.MinimizeBox = false; 61 | this.Name = "DownloadExtraFiles"; 62 | this.Text = "Extra Files"; 63 | this.ResumeLayout(false); 64 | 65 | } 66 | 67 | #endregion 68 | 69 | public System.Windows.Forms.Label status; 70 | private System.Windows.Forms.ProgressBar progressBar1; 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /UnityModManagerApp/DownloadExtraFiles.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Text; 10 | using System.Windows.Forms; 11 | using Ionic.Zip; 12 | 13 | namespace UnityModManagerNet.Installer 14 | { 15 | public partial class DownloadExtraFiles : System.Windows.Forms.Form 16 | { 17 | const string downloadFile = "extrafiles.zip"; 18 | string gamePath; 19 | 20 | public DownloadExtraFiles() 21 | { 22 | InitializeComponent(); 23 | } 24 | 25 | public DownloadExtraFiles(string url, string gamePath) 26 | { 27 | this.gamePath = gamePath; 28 | InitializeComponent(); 29 | Start(url); 30 | } 31 | 32 | public void Start(string url) 33 | { 34 | if (string.IsNullOrEmpty(gamePath)) 35 | { 36 | status.Text = "Before select game folder."; 37 | Log.Print("Before select game folder."); 38 | return; 39 | } 40 | 41 | try 42 | { 43 | status.Text = $"Downloading ..."; 44 | Log.Print($"Downloading ..."); 45 | using (var wc = new WebClient()) 46 | { 47 | wc.Encoding = Encoding.UTF8; 48 | wc.DownloadProgressChanged += Wc_DownloadProgressChanged; 49 | wc.DownloadFileCompleted += Wc_DownloadFileCompleted; 50 | wc.DownloadFileAsync(new Uri(url), downloadFile); 51 | } 52 | } 53 | catch (Exception e) 54 | { 55 | status.Text = e.Message; 56 | } 57 | } 58 | 59 | private void Wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 60 | { 61 | progressBar1.Value = e.ProgressPercentage; 62 | } 63 | 64 | private void Wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) 65 | { 66 | if (e.Error != null) 67 | { 68 | status.Text = e.Error.Message; 69 | Log.Print(e.Error.Message); 70 | return; 71 | } 72 | if (!e.Cancelled) 73 | { 74 | var success = false; 75 | try 76 | { 77 | using (var zip = ZipFile.Read(downloadFile)) 78 | { 79 | foreach (var entry in zip.EntriesSorted) 80 | { 81 | if (entry.IsDirectory) 82 | { 83 | Directory.CreateDirectory(Path.Combine(gamePath, entry.FileName)); 84 | } 85 | else 86 | { 87 | Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(gamePath, entry.FileName))); 88 | using (FileStream fs = new FileStream(Path.Combine(gamePath, entry.FileName), FileMode.Create, FileAccess.Write)) 89 | { 90 | entry.Extract(fs); 91 | } 92 | } 93 | } 94 | } 95 | 96 | status.Text = "Done."; 97 | Log.Print("Done."); 98 | success = true; 99 | } 100 | catch (Exception ex) 101 | { 102 | status.Text = ex.Message; 103 | Log.Print(ex.Message); 104 | } 105 | 106 | if (File.Exists(downloadFile)) 107 | { 108 | File.Delete(downloadFile); 109 | } 110 | 111 | if (success) 112 | { 113 | DialogResult = DialogResult.OK; 114 | Close(); 115 | } 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /UnityModManagerApp/DownloadExtraFiles.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /UnityModManagerApp/DownloadMod.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UnityModManagerNet.Installer 2 | { 3 | partial class DownloadMod 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.progressBar = new System.Windows.Forms.ProgressBar(); 32 | this.status = new System.Windows.Forms.Label(); 33 | this.SuspendLayout(); 34 | // 35 | // progressBar 36 | // 37 | this.progressBar.Location = new System.Drawing.Point(12, 68); 38 | this.progressBar.Name = "progressBar"; 39 | this.progressBar.Size = new System.Drawing.Size(310, 29); 40 | this.progressBar.TabIndex = 0; 41 | // 42 | // status 43 | // 44 | this.status.Location = new System.Drawing.Point(12, 7); 45 | this.status.Name = "status"; 46 | this.status.RightToLeft = System.Windows.Forms.RightToLeft.No; 47 | this.status.Size = new System.Drawing.Size(310, 54); 48 | this.status.TabIndex = 1; 49 | this.status.Text = "Downloading"; 50 | this.status.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 51 | // 52 | // DownloadMod 53 | // 54 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 55 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 56 | this.ClientSize = new System.Drawing.Size(334, 111); 57 | this.Controls.Add(this.status); 58 | this.Controls.Add(this.progressBar); 59 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 60 | this.MaximizeBox = false; 61 | this.MinimizeBox = false; 62 | this.Name = "DownloadMod"; 63 | this.ShowIcon = false; 64 | this.ShowInTaskbar = false; 65 | this.Text = "Mod"; 66 | this.TopMost = true; 67 | this.ResumeLayout(false); 68 | 69 | } 70 | 71 | #endregion 72 | 73 | private System.Windows.Forms.ProgressBar progressBar; 74 | private System.Windows.Forms.Label status; 75 | } 76 | } -------------------------------------------------------------------------------- /UnityModManagerApp/DownloadMod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Text; 10 | using System.Windows.Forms; 11 | 12 | namespace UnityModManagerNet.Installer 13 | { 14 | public partial class DownloadMod : Form 15 | { 16 | public UnityModManager.Repository.Release release; 17 | public string tempFilepath { get; private set; } 18 | 19 | public DownloadMod() 20 | { 21 | InitializeComponent(); 22 | } 23 | 24 | public DownloadMod(UnityModManager.Repository.Release release) 25 | { 26 | this.release = release; 27 | InitializeComponent(); 28 | Start(); 29 | } 30 | 31 | private void Start() 32 | { 33 | try 34 | { 35 | var dir = Path.Combine(Path.GetTempPath(), "UnityModManager"); 36 | if (!Directory.Exists(dir)) 37 | Directory.CreateDirectory(dir); 38 | 39 | tempFilepath = Path.Combine(dir, $"{release.Id}.zip"); 40 | 41 | status.Text = $"Downloading {release.Id} {release.Version} ..."; 42 | 43 | using (var wc = new WebClient()) 44 | { 45 | wc.Encoding = Encoding.UTF8; 46 | wc.DownloadProgressChanged += Wc_DownloadProgressChanged; 47 | wc.DownloadFileCompleted += Wc_DownloadFileCompleted; 48 | wc.DownloadFileAsync(new Uri(release.DownloadUrl), tempFilepath); 49 | } 50 | } 51 | catch (Exception e) 52 | { 53 | status.Text = e.Message; 54 | Log.Print(e.Message); 55 | } 56 | } 57 | 58 | private void Wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 59 | { 60 | progressBar.Value = e.ProgressPercentage; 61 | } 62 | 63 | private void Wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) 64 | { 65 | if (e.Error != null) 66 | { 67 | status.Text = e.Error.Message; 68 | Log.Print(e.Error.Message); 69 | return; 70 | } 71 | if (!e.Cancelled) 72 | { 73 | DialogResult = DialogResult.OK; 74 | Close(); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /UnityModManagerApp/DownloadMod.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /UnityModManagerApp/Form.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 213, 11 122 | 123 | 124 | 329, 11 125 | 126 | 127 | 50, 11 128 | 129 | 130 | 510, 11 131 | 132 | -------------------------------------------------------------------------------- /UnityModManagerApp/Log.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace UnityModManagerNet.Installer 5 | { 6 | class Log : ConsoleInstaller.Log 7 | { 8 | public override void Write(string str, bool append = false) 9 | { 10 | if (append) 11 | { 12 | UnityModManagerForm.instance.statusLabel.Text += str; 13 | UnityModManagerForm.instance.statusLabel.ToolTipText += str; 14 | } 15 | else 16 | { 17 | UnityModManagerForm.instance.statusLabel.Text = str; 18 | UnityModManagerForm.instance.statusLabel.ToolTipText = str; 19 | if (firstLine) 20 | { 21 | firstLine = false; 22 | str = $"[{DateTime.Now.ToShortTimeString()}] {str}"; 23 | } 24 | else 25 | { 26 | str = $"\r\n[{DateTime.Now.ToShortTimeString()}] {str}"; 27 | } 28 | } 29 | 30 | UnityModManagerForm.instance.inputLog.AppendText(str); 31 | stream?.Write(str); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UnityModManagerApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace UnityModManagerNet.Installer 7 | { 8 | static class Program 9 | { 10 | /// 11 | /// Главная точка входа для приложения. 12 | /// 13 | [STAThread] 14 | static void Main() 15 | { 16 | Application.EnableVisualStyles(); 17 | Application.SetCompatibleTextRenderingDefault(false); 18 | Application.Run(new UnityModManagerForm()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UnityModManagerApp/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Этот код создан программой. 4 | // Исполняемая версия:4.0.30319.42000 5 | // 6 | // Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае 7 | // повторной генерации кода. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityModManagerNet.Installer.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// Класс ресурса со строгой типизацией для поиска локализованных строк и т.д. 17 | /// 18 | // Этот класс создан автоматически классом StronglyTypedResourceBuilder 19 | // с помощью такого средства, как ResGen или Visual Studio. 20 | // Чтобы добавить или удалить член, измените файл .ResX и снова запустите ResGen 21 | // с параметром /str или перестройте свой проект VS. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Возвращает кэшированный экземпляр ResourceManager, использованный этим классом. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityModManagerNet.Installer.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Перезаписывает свойство CurrentUICulture текущего потока для всех 51 | /// обращений к ресурсу с помощью этого класса ресурса со строгой типизацией. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Поиск локализованного ресурса типа System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap dragdropfiles { 67 | get { 68 | object obj = ResourceManager.GetObject("dragdropfiles", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /UnityModManagerApp/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\images\dragdropfiles.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | -------------------------------------------------------------------------------- /UnityModManagerApp/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Этот код создан программой. 4 | // Исполняемая версия:4.0.30319.42000 5 | // 6 | // Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае 7 | // повторной генерации кода. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityModManagerNet.Installer.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.3.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UnityModManagerApp/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /UnityModManagerApp/SetFolder.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UnityModManagerNet.Installer 2 | { 3 | partial class SetFolder 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.textBox1 = new System.Windows.Forms.TextBox(); 32 | this.button1 = new System.Windows.Forms.Button(); 33 | this.SuspendLayout(); 34 | // 35 | // textBox1 36 | // 37 | this.textBox1.Location = new System.Drawing.Point(12, 13); 38 | this.textBox1.Name = "textBox1"; 39 | this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; 40 | this.textBox1.Size = new System.Drawing.Size(621, 20); 41 | this.textBox1.TabIndex = 0; 42 | // 43 | // button1 44 | // 45 | this.button1.Location = new System.Drawing.Point(285, 39); 46 | this.button1.Name = "button1"; 47 | this.button1.Size = new System.Drawing.Size(75, 23); 48 | this.button1.TabIndex = 1; 49 | this.button1.Text = "Change"; 50 | this.button1.UseVisualStyleBackColor = true; 51 | this.button1.Click += new System.EventHandler(this.button1_Click); 52 | // 53 | // SetFolder 54 | // 55 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 56 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 57 | this.ClientSize = new System.Drawing.Size(645, 69); 58 | this.Controls.Add(this.button1); 59 | this.Controls.Add(this.textBox1); 60 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 61 | this.MaximizeBox = false; 62 | this.MinimizeBox = false; 63 | this.Name = "SetFolder"; 64 | this.Text = "Game folder"; 65 | this.TopMost = true; 66 | this.ResumeLayout(false); 67 | this.PerformLayout(); 68 | 69 | } 70 | 71 | #endregion 72 | private System.Windows.Forms.Button button1; 73 | public System.Windows.Forms.TextBox textBox1; 74 | } 75 | } -------------------------------------------------------------------------------- /UnityModManagerApp/SetFolder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | 12 | namespace UnityModManagerNet.Installer 13 | { 14 | public partial class SetFolder : Form 15 | { 16 | public SetFolder() 17 | { 18 | InitializeComponent(); 19 | } 20 | 21 | public SetFolder(string path) 22 | { 23 | InitializeComponent(); 24 | textBox1.Text = path; 25 | } 26 | 27 | private void button1_Click(object sender, EventArgs e) 28 | { 29 | DialogResult = DialogResult.OK; 30 | Close(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /UnityModManagerApp/SetFolder.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /UnityModManagerApp/Tools/SteamHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | using JetBrains.Annotations; 7 | using Microsoft.Win32; 8 | 9 | namespace UnityModManagerNet.Installer.Tools 10 | { 11 | /// 12 | /// Base on: https://stackoverflow.com/questions/54767662/finding-game-launcher-executables-in-directory-c-sharp 13 | /// 14 | public static class SteamHelper 15 | { 16 | private static ICollection RegistryKeys = new[] { "SOFTWARE\\Wow6432Node\\Valve\\", "SOFTWARE\\VALVE\\" }; 17 | private static ICollection _steamGameDirs = new List(); 18 | 19 | static SteamHelper() 20 | { 21 | if (!Utils.IsWindowsPlatform()) 22 | { 23 | return; 24 | } 25 | UpdateSteamGameDirectories(); 26 | } 27 | 28 | private static void UpdateSteamGameDirectories() 29 | { 30 | _steamGameDirs = RegistryKeys 31 | .Select(v => Registry.LocalMachine.OpenSubKey(v)) 32 | .Where(registryKey => registryKey != null) 33 | .SelectMany( 34 | registryKey => 35 | { 36 | using (registryKey) 37 | { 38 | return GetDirectories(registryKey).ToArray(); 39 | } 40 | } 41 | ) 42 | .Distinct() 43 | .ToList(); 44 | } 45 | 46 | private static IEnumerable GetDirectories(RegistryKey registryKey) 47 | { 48 | foreach (var subKeyName in registryKey.GetSubKeyNames()) 49 | { 50 | using (var subKey = registryKey.OpenSubKey(subKeyName)) 51 | { 52 | if (subKey == null) 53 | { 54 | continue; 55 | } 56 | 57 | var installPath = subKey.GetValue("InstallPath"); 58 | if (installPath == null) 59 | { 60 | continue; 61 | } 62 | 63 | var steamPath = installPath.ToString(); 64 | var configPath = $"{steamPath}/steamapps/libraryfolders.vdf"; 65 | const string driveRegex = @"[A-Z]:\\"; 66 | if (!File.Exists(configPath)) 67 | { 68 | continue; 69 | } 70 | 71 | var configLines = File.ReadAllLines(configPath); 72 | foreach (var item in configLines) 73 | { 74 | var match = Regex.Match(item, driveRegex); 75 | if (item == string.Empty || !match.Success) 76 | { 77 | continue; 78 | } 79 | 80 | var matched = match.ToString(); 81 | var item2 = item.Substring(item.IndexOf(matched, StringComparison.Ordinal)); 82 | item2 = item2.Replace("\\\\", "\\"); 83 | item2 = item2.Replace("\"", "\\steamapps\\common\\"); 84 | yield return item2; 85 | } 86 | 87 | yield return $"{steamPath}\\steamapps\\common\\"; 88 | } 89 | } 90 | } 91 | 92 | public static IEnumerable GetGameDirectories(string gameFolderName) 93 | { 94 | return _steamGameDirs 95 | .Select(v => Path.Combine(v, gameFolderName)) 96 | .Where(v => Directory.Exists(v)); 97 | } 98 | 99 | public static string? GetGameDirectory(string gameFolderName) 100 | { 101 | return GetGameDirectories(gameFolderName).FirstOrDefault(); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /UnityModManagerApp/UnityModManagerApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | WinExe 7 | net48 8 | true 9 | false 10 | latest 11 | enable 12 | 13 | UnityModManagerApp 14 | UnityModManagerNet.Installer 15 | Copyright © 2019-$([System.DateTime]::Now.ToString('yyyy')) 16 | 17 | 18 | 19 | true 20 | AnyCPU 21 | en-001 22 | icon.ico 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ..\lib\UnityEngine.dll 44 | 45 | 46 | ..\lib\UnityEngine.UI.dll 47 | 48 | 49 | $(Pkgdnlib)\lib\net35\dnlib.dll 50 | 51 | 52 | $(PkgLib_Harmony)\lib\net48\0Harmony.dll 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | True 64 | True 65 | Settings.settings 66 | 67 | 68 | 69 | 70 | 71 | SettingsSingleFileGenerator 72 | Settings.Designer.cs 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /UnityModManagerApp/Updates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Windows.Forms; 6 | using Newtonsoft.Json; 7 | using System.Net; 8 | using System.Net.NetworkInformation; 9 | using UnityModManagerNet.ConsoleInstaller; 10 | 11 | namespace UnityModManagerNet.Installer 12 | { 13 | public partial class UnityModManagerForm : Form 14 | { 15 | public class NexusModInfo 16 | { 17 | public string name; 18 | public int mod_id; 19 | public string domain_name; 20 | public string version; 21 | } 22 | 23 | readonly Dictionary> repositories = new Dictionary>(); 24 | readonly Dictionary nexusUpdates = new Dictionary(); 25 | 26 | private void CheckModUpdates() 27 | { 28 | if (selectedGame == null) 29 | return; 30 | 31 | Log.Print("Checking mod updates"); 32 | tabPage2.Enabled = false; 33 | 34 | if (!HasNetworkConnection()) 35 | { 36 | return; 37 | } 38 | 39 | selectedGameParams.LastUpdateCheck = DateTime.Now; 40 | 41 | if (!repositories.ContainsKey(selectedGame)) 42 | repositories.Add(selectedGame, new HashSet()); 43 | 44 | var urls = new HashSet(); 45 | foreach (var mod in mods) 46 | { 47 | if (!string.IsNullOrEmpty(mod.Repository)) 48 | { 49 | urls.Add(mod.Repository); 50 | } 51 | 52 | if (!string.IsNullOrEmpty(param.APIkey) && !string.IsNullOrEmpty(mod.HomePage)) 53 | { 54 | CheckNexus(mod); 55 | } 56 | } 57 | 58 | if (urls.Count > 0) 59 | { 60 | foreach (var url in urls) 61 | { 62 | try 63 | { 64 | using (var wc = new WebClient()) 65 | { 66 | wc.Encoding = System.Text.Encoding.UTF8; 67 | wc.DownloadStringCompleted += (sender, e) => { ModUpdates_DownloadStringCompleted(sender, e, selectedGame, url); }; 68 | wc.DownloadStringAsync(new Uri(url)); 69 | } 70 | } 71 | catch (Exception e) 72 | { 73 | Log.Print(e.Message); 74 | Log.Print($"Error checking mod updates on '{url}' for [{string.Join(",", mods.Where(x => x.Repository == url).Select(x => x.DisplayName).ToArray())}]."); 75 | } 76 | } 77 | } 78 | 79 | RefreshModList(); 80 | tabPage2.Enabled = true; 81 | } 82 | 83 | private void ModUpdates_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e, GameInfo game, string url) 84 | { 85 | if (e.Error != null) 86 | { 87 | Log.Print(e.Error.Message); 88 | return; 89 | } 90 | 91 | if (!e.Cancelled && !string.IsNullOrEmpty(e.Result) && repositories.ContainsKey(game)) 92 | { 93 | try 94 | { 95 | var repository = JsonConvert.DeserializeObject(e.Result); 96 | if (repository == null || repository.Releases == null || repository.Releases.Length == 0) 97 | return; 98 | 99 | listMods.Invoke((MethodInvoker)delegate 100 | { 101 | foreach(var v in repository.Releases) 102 | { 103 | repositories[game].Add(v); 104 | } 105 | if (selectedGame == game) 106 | RefreshModList(); 107 | }); 108 | } 109 | catch (Exception ex) 110 | { 111 | Log.Print(ex.Message); 112 | Log.Print($"Error checking mod updates on '{url}' for [{string.Join(",", mods.Where(x => x.Repository == url).Select(x => x.DisplayName).ToArray())}]."); 113 | } 114 | } 115 | } 116 | 117 | private void CheckNexus(ModInfo modInfo) 118 | { 119 | if (modInfo && Utils.ParseNexusUrl(modInfo.HomePage, out string nexusGame, out string nexusModId)) 120 | { 121 | try 122 | { 123 | var request = WebRequest.Create($"https://api.nexusmods.com/v1/games/{nexusGame}/mods/{nexusModId}.json"); 124 | request.ContentType = "application/json"; 125 | request.Headers.Add("apikey", param.APIkey); 126 | request.Headers.Add("Application-Version", version.ToString()); 127 | request.Headers.Add("Application-Name", "UnityModManager"); 128 | var response = request.GetResponse(); 129 | var reader = new StreamReader(response.GetResponseStream()); 130 | string result = reader.ReadToEnd(); 131 | 132 | NexusModInfo nexusModInfo = JsonConvert.DeserializeObject(result); 133 | if (nexusModInfo != null) 134 | { 135 | nexusUpdates[modInfo] = Utils.ParseVersion(nexusModInfo.version); 136 | } 137 | 138 | reader.Close(); 139 | response.Close(); 140 | } 141 | catch (Exception ex) 142 | { 143 | Log.Print(ex.Message); 144 | } 145 | } 146 | } 147 | 148 | private void CheckLastVersion() 149 | { 150 | if (string.IsNullOrEmpty(config.Repository)) 151 | return; 152 | 153 | Log.Print("Checking for updates."); 154 | 155 | if (!HasNetworkConnection()) 156 | { 157 | Log.Print("No network connection or firewall blocked."); 158 | return; 159 | } 160 | 161 | try 162 | { 163 | using (var wc = new WebClient()) 164 | { 165 | wc.Encoding = System.Text.Encoding.UTF8; 166 | wc.DownloadStringCompleted += LastVersion_DownloadStringCompleted; 167 | wc.DownloadStringAsync(new Uri(config.Repository)); 168 | } 169 | } 170 | catch (Exception e) 171 | { 172 | Log.Print(e.Message); 173 | Log.Print($"Error checking update."); 174 | } 175 | } 176 | 177 | private void LastVersion_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) 178 | { 179 | if (e.Error != null) 180 | { 181 | Log.Print(e.Error.Message); 182 | return; 183 | } 184 | 185 | if (!e.Cancelled && !string.IsNullOrEmpty(e.Result)) 186 | { 187 | try 188 | { 189 | var repository = JsonConvert.DeserializeObject(e.Result); 190 | if (repository == null || repository.Releases == null || repository.Releases.Length == 0) 191 | return; 192 | 193 | var release = repository.Releases.FirstOrDefault(x => x.Id == nameof(UnityModManager)); 194 | if (release != null && !string.IsNullOrEmpty(release.Version)) 195 | { 196 | var ver = Utils.ParseVersion(release.Version); 197 | if (version < ver) 198 | { 199 | //btnDownloadUpdate.Visible = true; 200 | btnDownloadUpdate.Text = $"Download {release.Version}"; 201 | Log.Print($"Update is available."); 202 | } 203 | else 204 | { 205 | Log.Print($"No updates."); 206 | } 207 | } 208 | } 209 | catch (Exception ex) 210 | { 211 | Log.Print(ex.Message); 212 | Log.Print($"Error checking update."); 213 | } 214 | } 215 | } 216 | 217 | public static bool HasNetworkConnection() 218 | { 219 | try 220 | { 221 | using (var ping = new Ping()) 222 | { 223 | return ping.Send("8.8.8.8", 3000).Status == IPStatus.Success; 224 | } 225 | } 226 | catch (Exception e) 227 | { 228 | Log.Print(e.Message); 229 | } 230 | 231 | return false; 232 | } 233 | } 234 | } -------------------------------------------------------------------------------- /UnityModManagerApp/Utils.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Text.RegularExpressions; 3 | 4 | namespace UnityModManagerNet.Installer 5 | { 6 | public class Utils : ConsoleInstaller.Utils 7 | { 8 | public static bool ParseNexusUrl(string url, out string game, out string id) 9 | { 10 | game = null; 11 | id = null; 12 | var regex = new Regex(@"https:\/\/www\.nexusmods\.com\/(\w+)\/mods\/(\d+)", RegexOptions.IgnoreCase); 13 | var matches = regex.Matches(url); 14 | foreach (Match match in matches) 15 | { 16 | game = match.Groups[1].Value; 17 | id = match.Groups[2].Value; 18 | return true; 19 | } 20 | return false; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /UnityModManagerApp/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /UnityModManagerApp/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/UnityModManagerApp/icon.ico -------------------------------------------------------------------------------- /UnityModManagerApp/images/dragdropfiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/UnityModManagerApp/images/dragdropfiles.png -------------------------------------------------------------------------------- /Updater/Config.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Windows.Forms; 7 | using System.Xml.Serialization; 8 | 9 | namespace UnityModManagerNet.Downloader 10 | { 11 | public class Config 12 | { 13 | public string Repository; 14 | } 15 | 16 | public class Repository 17 | { 18 | [Serializable] 19 | public class Release 20 | { 21 | public string Id; 22 | public string Version; 23 | public string DownloadUrl; 24 | } 25 | 26 | public Release[] Releases; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Updater/Form.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UnityModManagerNet.Downloader 2 | { 3 | partial class DownloaderForm 4 | { 5 | /// 6 | /// Обязательная переменная конструктора. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Освободить все используемые ресурсы. 12 | /// 13 | /// истинно, если управляемый ресурс должен быть удален; иначе ложно. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Код, автоматически созданный конструктором форм Windows 24 | 25 | /// 26 | /// Требуемый метод для поддержки конструктора — не изменяйте 27 | /// содержимое этого метода с помощью редактора кода. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.progressBar1 = new System.Windows.Forms.ProgressBar(); 32 | this.status = new System.Windows.Forms.Label(); 33 | this.SuspendLayout(); 34 | // 35 | // progressBar1 36 | // 37 | this.progressBar1.Location = new System.Drawing.Point(12, 55); 38 | this.progressBar1.Name = "progressBar1"; 39 | this.progressBar1.Size = new System.Drawing.Size(306, 23); 40 | this.progressBar1.TabIndex = 0; 41 | // 42 | // status 43 | // 44 | this.status.Location = new System.Drawing.Point(12, 9); 45 | this.status.Name = "status"; 46 | this.status.Size = new System.Drawing.Size(306, 43); 47 | this.status.TabIndex = 1; 48 | this.status.Text = "Downloading..."; 49 | this.status.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 50 | // 51 | // DownloaderForm 52 | // 53 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 54 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 55 | this.ClientSize = new System.Drawing.Size(330, 94); 56 | this.Controls.Add(this.status); 57 | this.Controls.Add(this.progressBar1); 58 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 59 | this.MaximizeBox = false; 60 | this.MinimizeBox = false; 61 | this.Name = "DownloaderForm"; 62 | this.Text = "Updates"; 63 | this.ResumeLayout(false); 64 | 65 | } 66 | 67 | #endregion 68 | 69 | private System.Windows.Forms.ProgressBar progressBar1; 70 | public System.Windows.Forms.Label status; 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /Updater/Form.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Drawing; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Reflection; 9 | using System.Runtime.InteropServices; 10 | using System.Text; 11 | using System.Windows.Forms; 12 | using System.Xml.Serialization; 13 | using Ionic.Zip; 14 | using Newtonsoft.Json; 15 | 16 | namespace UnityModManagerNet.Downloader 17 | { 18 | public partial class DownloaderForm : Form 19 | { 20 | const string updateFile = "update.zip"; 21 | const string configFile = "UnityModManagerConfig.xml"; 22 | const string managerName = "UnityModManager"; 23 | const string managerFile = "UnityModManager.dll"; 24 | const string managerAppName = "UnityModManager"; 25 | const string managerAppFile = "UnityModManager.exe"; 26 | 27 | public DownloaderForm() 28 | { 29 | InitializeComponent(); 30 | Start(); 31 | } 32 | 33 | public void Start() 34 | { 35 | var rewrite = false; 36 | string[] args = Environment.GetCommandLineArgs(); 37 | if (args.Length > 0) 38 | { 39 | rewrite = args.Contains("-rewrite"); 40 | } 41 | 42 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; 43 | 44 | if (!Utils.HasNetworkConnection()) 45 | { 46 | status.Text = $"No network connection."; 47 | return; 48 | } 49 | 50 | try 51 | { 52 | Config config; 53 | using (var stream = File.OpenRead(configFile)) 54 | { 55 | var serializer = new XmlSerializer(typeof(Config)); 56 | config = serializer.Deserialize(stream) as Config; 57 | } 58 | if (config == null || string.IsNullOrEmpty(config.Repository)) 59 | { 60 | status.Text = $"Error parsing '{configFile}'."; 61 | return; 62 | } 63 | if (File.Exists(updateFile)) 64 | { 65 | File.Delete(updateFile); 66 | } 67 | 68 | string result = null; 69 | using (var wc = new WebClient()) 70 | { 71 | wc.Encoding = Encoding.UTF8; 72 | result = wc.DownloadString(new Uri(config.Repository)); 73 | } 74 | var repository = JsonConvert.DeserializeObject(result); 75 | if (repository == null || repository.Releases.Length == 0) 76 | { 77 | status.Text = $"Error parsing '{config.Repository}'."; 78 | return; 79 | } 80 | var release = repository.Releases.FirstOrDefault(x => x.Id == managerName); 81 | if (!rewrite && File.Exists(managerFile)) 82 | { 83 | var managerAssembly = Assembly.ReflectionOnlyLoad(File.ReadAllBytes(managerFile)); 84 | if (Utils.ParseVersion(release.Version) <= managerAssembly.GetName().Version) 85 | { 86 | status.Text = $"No updates."; 87 | return; 88 | } 89 | } 90 | status.Text = $"Downloading {release.Version} ..."; 91 | using (var wc = new WebClient()) 92 | { 93 | wc.Encoding = Encoding.UTF8; 94 | wc.DownloadProgressChanged += Wc_DownloadProgressChanged; 95 | wc.DownloadFileCompleted += Wc_DownloadFileCompleted; 96 | wc.DownloadFileAsync(new Uri(release.DownloadUrl), updateFile); 97 | } 98 | } 99 | catch (Exception e) 100 | { 101 | status.Text = e.Message; 102 | } 103 | } 104 | 105 | private void Wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 106 | { 107 | progressBar1.Value = e.ProgressPercentage; 108 | } 109 | 110 | private void Wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) 111 | { 112 | if (e.Error != null) 113 | { 114 | status.Text = e.Error.Message; 115 | return; 116 | } 117 | if (!e.Cancelled) 118 | { 119 | var success = false; 120 | try 121 | { 122 | foreach (var p in Process.GetProcessesByName(managerAppName)) 123 | { 124 | status.Text = "Waiting for the UnityModManager to close."; 125 | p.CloseMainWindow(); 126 | p.WaitForExit(); 127 | } 128 | using (var zip = ZipFile.Read(updateFile)) 129 | { 130 | foreach (var entry in zip.EntriesSorted) 131 | { 132 | if (entry.IsDirectory) 133 | { 134 | Directory.CreateDirectory(Path.Combine(Environment.CurrentDirectory, entry.FileName)); 135 | } 136 | else 137 | { 138 | Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(Environment.CurrentDirectory, entry.FileName))); 139 | using (FileStream fs = new FileStream(Path.Combine(Environment.CurrentDirectory, entry.FileName), FileMode.Create, FileAccess.Write)) 140 | { 141 | entry.Extract(fs); 142 | } 143 | } 144 | } 145 | } 146 | status.Text = "Done."; 147 | success = true; 148 | } 149 | catch (Exception ex) 150 | { 151 | status.Text = ex.Message; 152 | } 153 | 154 | if (File.Exists(updateFile)) 155 | { 156 | File.Delete(updateFile); 157 | } 158 | 159 | if (success) 160 | { 161 | if (!Utils.IsUnixPlatform() && Process.GetProcessesByName(managerAppName).Length == 0) 162 | { 163 | if (File.Exists(managerAppFile)) 164 | { 165 | SetForegroundWindow(Process.Start(managerAppFile).MainWindowHandle); 166 | } 167 | } 168 | Application.Exit(); 169 | } 170 | } 171 | } 172 | 173 | [DllImport("user32.dll", SetLastError = true)] 174 | private static extern bool SetForegroundWindow(IntPtr hwnd); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /Updater/Form.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Updater/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace UnityModManagerNet.Downloader 7 | { 8 | static class Program 9 | { 10 | [STAThread] 11 | static void Main() 12 | { 13 | Application.EnableVisualStyles(); 14 | Application.SetCompatibleTextRenderingDefault(false); 15 | Application.Run(new DownloaderForm()); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Updater/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Этот код создан программой. 4 | // Исполняемая версия:4.0.30319.42000 5 | // 6 | // Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае 7 | // повторной генерации кода. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityModManagerNet.Downloader.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// Класс ресурса со строгой типизацией для поиска локализованных строк и т.д. 17 | /// 18 | // Этот класс создан автоматически классом StronglyTypedResourceBuilder 19 | // с помощью такого средства, как ResGen или Visual Studio. 20 | // Чтобы добавить или удалить член, измените файл .ResX и снова запустите ResGen 21 | // с параметром /str или перестройте свой проект VS. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Возвращает кэшированный экземпляр ResourceManager, использованный этим классом. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityModManagerNet.Downloader.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Перезаписывает свойство CurrentUICulture текущего потока для всех 51 | /// обращений к ресурсу с помощью этого класса ресурса со строгой типизацией. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Updater/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /Updater/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Этот код создан программой. 4 | // Исполняемая версия:4.0.30319.42000 5 | // 6 | // Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае 7 | // повторной генерации кода. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityModManagerNet.Downloader.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Updater/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Updater/Updater.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | WinExe 7 | net48 8 | true 9 | false 10 | latest 11 | 12 | Downloader 13 | UnityModManagerNet.Downloader 14 | Copyright © 2019-$([System.DateTime]::Now.ToString('yyyy')) 15 | 16 | 17 | 18 | true 19 | en-001 20 | AnyCPU 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ..\lib\UnityEngine.dll 33 | 34 | 35 | ..\lib\UnityEngine.UI.dll 36 | 37 | 38 | $(Pkgdnlib)\lib\net35\dnlib.dll 39 | 40 | 41 | $(PkgLib_Harmony)\lib\net48\0Harmony.dll 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Updater/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net.NetworkInformation; 6 | using System.Text.RegularExpressions; 7 | 8 | namespace UnityModManagerNet.Downloader 9 | { 10 | static class Utils 11 | { 12 | public static Version ParseVersion(string str) 13 | { 14 | var array = str.Split('.', ','); 15 | if (array.Length >= 3) 16 | { 17 | var regex = new Regex(@"\D"); 18 | return new Version(int.Parse(regex.Replace(array[0], "")), int.Parse(regex.Replace(array[1], "")), int.Parse(regex.Replace(array[2], ""))); 19 | } 20 | 21 | return new Version(); 22 | } 23 | 24 | public static bool HasNetworkConnection() 25 | { 26 | try 27 | { 28 | using (var ping = new Ping()) 29 | { 30 | return ping.Send("8.8.8.8", 2000).Status == IPStatus.Success; 31 | } 32 | } 33 | catch (Exception) 34 | { 35 | } 36 | 37 | return false; 38 | } 39 | 40 | public static bool IsUnixPlatform() 41 | { 42 | int p = (int)Environment.OSVersion.Platform; 43 | return (p == 4) || (p == 6) || (p == 128); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Updater/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/Harmony/1.2/0Harmony-1.2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/lib/Harmony/1.2/0Harmony-1.2.dll -------------------------------------------------------------------------------- /lib/Harmony/1.2/0Harmony12.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/lib/Harmony/1.2/0Harmony12.dll -------------------------------------------------------------------------------- /lib/Harmony/2.2/0Harmony.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/lib/Harmony/2.2/0Harmony.dll -------------------------------------------------------------------------------- /lib/Ionic.Zip.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/lib/Ionic.Zip.dll -------------------------------------------------------------------------------- /lib/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/lib/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /lib/System.Xml.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/lib/System.Xml.dll -------------------------------------------------------------------------------- /lib/dnlib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/lib/dnlib.dll -------------------------------------------------------------------------------- /lib/libraries: -------------------------------------------------------------------------------- 1 | https://www.dropbox.com/s/dng4xkf24qzh2cc/UnityEngine.zip?dl=1 -------------------------------------------------------------------------------- /lib/winhttp_x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/lib/winhttp_x64.dll -------------------------------------------------------------------------------- /lib/winhttp_x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newman55/unity-mod-manager/b566410fbce72db2bdb2eaf16293d9e1b50d516f/lib/winhttp_x86.dll --------------------------------------------------------------------------------