diff options
author | chai <chaifix@163.com> | 2020-12-30 20:59:04 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2020-12-30 20:59:04 +0800 |
commit | e9ea621b93fbb58d9edfca8375918791637bbd52 (patch) | |
tree | 19ce3b1c1f2d51eda6878c9d0f2c9edc27f13650 /Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared |
+init
Diffstat (limited to 'Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared')
7 files changed, 439 insertions, 0 deletions
diff --git a/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/AmongUsModifier.cs b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/AmongUsModifier.cs new file mode 100644 index 0000000..95f5524 --- /dev/null +++ b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/AmongUsModifier.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Gameloop.Vdf; +using Gameloop.Vdf.Linq; +using Impostor.Patcher.Shared.Events; +using Impostor.Patcher.Shared.Innersloth; +using ErrorEventArgs = Impostor.Patcher.Shared.Events.ErrorEventArgs; + +namespace Impostor.Patcher.Shared +{ + public class AmongUsModifier + { + private const uint AppId = 945360; + public const string DefaultRegionName = "Impostor"; + public const ushort DefaultPort = 22023; + + private readonly string _amongUsDir; + private readonly string _regionFile; + + public string RegionName { get; set; } = DefaultRegionName; + + public AmongUsModifier() + { + var appData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "..", "LocalLow"); + + if (!Directory.Exists(appData)) + { + appData = FindProtonAppData(); + } + + if (appData == null) + return; + + _amongUsDir = Path.Combine(appData, "Innersloth", "Among Us"); + _regionFile = Path.Combine(_amongUsDir, "regionInfo.dat"); + } + + private string FindProtonAppData() + { + string steamApps; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + steamApps = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".steam", "steam", "steamapps"); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + steamApps = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Steam", "steamapps"); + } + else + { + return null; + } + + if (!Directory.Exists(steamApps)) + return null; + + var libraries = new List<string> + { + steamApps + }; + + var vdf = Path.Combine(steamApps, "libraryfolders.vdf"); + if (File.Exists(vdf)) + { + var libraryFolders = VdfConvert.Deserialize(File.ReadAllText(vdf)); + + foreach (var libraryFolder in libraryFolders.Value.Children<VProperty>()) + { + if (!int.TryParse(libraryFolder.Key, out _)) + continue; + + libraries.Add(Path.Combine(libraryFolder.Value.Value<string>(), "steamapps")); + } + } + + foreach (var library in libraries) + { + var path = Path.Combine(library, "compatdata", AppId.ToString(), "pfx", "drive_c", "users", "steamuser", "AppData", "LocalLow"); + if (Directory.Exists(path)) + { + return path; + } + } + + return null; + } + + public async Task<bool> SaveIpAsync(string input) + { + // Filter out whitespace. + input = input.Trim(); + + // Split port from ip. + // Only IPv4 is supported so just do it simple. + var ip = string.Empty; + var port = DefaultPort; + + var parts = input.Split(':'); + if (parts.Length >= 1) + { + ip = parts[0]; + } + + if (parts.Length >= 2) + { + ushort.TryParse(parts[1], out port); + } + + // Check if a valid IP address was entered. + if (!IPAddress.TryParse(ip, out var ipAddress)) + { + // Attempt to resolve DNS. + try + { + var hostAddresses = await Dns.GetHostAddressesAsync(ip); + if (hostAddresses.Length == 0) + { + OnError("Invalid IP Address entered"); + return false; + } + + // Use first IPv4 result. + ipAddress = hostAddresses.First(x => x.AddressFamily == AddressFamily.InterNetwork); + } + catch (SocketException) + { + OnError("Failed to resolve hostname."); + return false; + } + } + + // Only IPv4. + if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) + { + OnError("Invalid IP Address entered, only IPv4 is allowed."); + return false; + } + + return WriteIp(ipAddress, port); + } + + /// <summary> + /// Writes an IP Address to the Among Us region file. + /// </summary> + /// <param name="ipAddress">The IPv4 address to write.</param> + /// <param name="port"></param> + private bool WriteIp(IPAddress ipAddress, ushort port) + { + if (ipAddress == null || + ipAddress.AddressFamily != AddressFamily.InterNetwork) + { + throw new ArgumentException(nameof(ipAddress)); + } + + if (!Directory.Exists(_amongUsDir)) + { + OnError("Among Us directory was not found, is it installed? Try running it once."); + return false; + } + + using (var file = File.Open(_regionFile, FileMode.Create, FileAccess.Write)) + using (var writer = new BinaryWriter(file)) + { + var ip = ipAddress.ToString(); + var region = new RegionInfo(RegionName, ip, new[] + { + new ServerInfo($"{RegionName}-Master-1", ip, port) + }); + + region.Serialize(writer); + + OnSaved(ip, port); + return true; + } + } + + /// <summary> + /// Loads the existing region info from the Among Us. + /// </summary> + public bool TryLoadRegionInfo(out RegionInfo regionInfo) + { + regionInfo = null; + + if (!File.Exists(_regionFile)) + { + return false; + } + + using (var file = File.Open(_regionFile, FileMode.Open, FileAccess.Read)) + using (var reader = new BinaryReader(file)) + { + try + { + regionInfo = RegionInfo.Deserialize(reader); + return true; + } + catch (Exception exception) + { + OnError("Couldn't parse region info\n" + exception); + return false; + } + } + } + + /// <summary> + /// Loads the existing IP Address from the Among Us region file + /// if it was set by Impostor before. + /// </summary> + public bool TryLoadIp(out string ipAddress) + { + ipAddress = null; + + if (!TryLoadRegionInfo(out var regionInfo)) + { + return false; + } + + if ((regionInfo.Name == RegionName || regionInfo.Name == DefaultRegionName) && regionInfo.Servers.Count >= 1) + { + ipAddress = regionInfo.Servers.ElementAt(0).Ip; + return true; + } + + return false; + } + + private void OnError(string message) + { + Error?.Invoke(this, new ErrorEventArgs(message)); + } + + private void OnSaved(string ipAddress, ushort port) + { + Saved?.Invoke(this, new SavedEventArgs(ipAddress, port)); + } + + public event EventHandler<ErrorEventArgs> Error; + public event EventHandler<SavedEventArgs> Saved; + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Configuration.cs b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Configuration.cs new file mode 100644 index 0000000..b256156 --- /dev/null +++ b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Configuration.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Impostor.Patcher.Shared +{ + public class Configuration + { + private const string FileRecentIps = @"recent_ips.txt"; + private const int MaxRecentIps = 5; + + private readonly string _baseDir; + private readonly string _recentIpsPath; + private readonly List<string> _recentIps; + + public Configuration() + { + var appData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)); + + _baseDir = Path.Combine(appData, "Impostor"); + _recentIpsPath = Path.Combine(_baseDir, FileRecentIps); + _recentIps = new List<string>(); + } + + public IReadOnlyList<string> RecentIps => _recentIps; + + public void Load() + { + if (File.Exists(_recentIpsPath)) + { + _recentIps.AddRange(File.ReadAllLines(_recentIpsPath)); + } + } + + public void Save() + { + Directory.CreateDirectory(_baseDir); + + if (!Directory.Exists(_baseDir)) + { + return; + } + + if (_recentIps.Count > 0) + { + File.WriteAllLines(_recentIpsPath, _recentIps); + } + } + + public void AddIp(string ip) + { + if (_recentIps.Contains(ip)) + { + _recentIps.Remove(ip); + } + + _recentIps.Insert(0, ip); + + if (_recentIps.Count > MaxRecentIps) + { + _recentIps.RemoveAt(MaxRecentIps); + } + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Events/ErrorEventArgs.cs b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Events/ErrorEventArgs.cs new file mode 100644 index 0000000..7211d5d --- /dev/null +++ b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Events/ErrorEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace Impostor.Patcher.Shared.Events +{ + public class ErrorEventArgs : EventArgs + { + public ErrorEventArgs(string message) + { + Message = message; + } + + public string Message { get; } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Events/SavedEventArgs.cs b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Events/SavedEventArgs.cs new file mode 100644 index 0000000..c91d071 --- /dev/null +++ b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Events/SavedEventArgs.cs @@ -0,0 +1,16 @@ +using System; + +namespace Impostor.Patcher.Shared.Events +{ + public class SavedEventArgs : EventArgs + { + public SavedEventArgs(string ipAddress, ushort port) + { + IpAddress = ipAddress; + Port = port; + } + + public string IpAddress { get; } + public ushort Port { get; } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Impostor.Patcher.Shared.csproj b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Impostor.Patcher.Shared.csproj new file mode 100644 index 0000000..e480870 --- /dev/null +++ b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Impostor.Patcher.Shared.csproj @@ -0,0 +1,12 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks> + <Version>1.0.0</Version> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Gameloop.Vdf" Version="0.6.1" /> + </ItemGroup> + +</Project> diff --git a/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Innersloth/RegionInfo.cs b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Innersloth/RegionInfo.cs new file mode 100644 index 0000000..01b74d1 --- /dev/null +++ b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Innersloth/RegionInfo.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.IO; + +namespace Impostor.Patcher.Shared.Innersloth +{ + public class RegionInfo + { + public RegionInfo(string name, string ping, IReadOnlyList<ServerInfo> servers) + { + Name = name; + Ping = ping; + Servers = servers; + } + + public string Name { get; } + public string Ping { get; } + public IReadOnlyList<ServerInfo> Servers { get; } + + public void Serialize(BinaryWriter writer) + { + writer.Write(0); + writer.Write(Name); + writer.Write(Ping); + writer.Write(Servers.Count); + + foreach (var server in Servers) + { + server.Serialize(writer); + } + } + + public static RegionInfo Deserialize(BinaryReader reader) + { + var unknown = reader.ReadInt32(); + var name = reader.ReadString(); + var ping = reader.ReadString(); + var servers = new List<ServerInfo>(); + var serverCount = reader.ReadInt32(); + + for (var i = 0; i < serverCount; i++) + { + servers.Add(ServerInfo.Deserialize(reader)); + } + + return new RegionInfo(name, ping, servers); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Innersloth/ServerInfo.cs b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Innersloth/ServerInfo.cs new file mode 100644 index 0000000..7203c84 --- /dev/null +++ b/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Innersloth/ServerInfo.cs @@ -0,0 +1,37 @@ +using System.IO; +using System.Net; + +namespace Impostor.Patcher.Shared.Innersloth +{ + public class ServerInfo + { + public string Name { get; } + public string Ip { get; } + public ushort Port { get; } + + public ServerInfo(string name, string ip, ushort port) + { + Name = name; + Ip = ip; + Port = port; + } + + public void Serialize(BinaryWriter writer) + { + writer.Write(Name); + writer.Write(IPAddress.Parse(Ip).GetAddressBytes()); + writer.Write(Port); + writer.Write(0); + } + + public static ServerInfo Deserialize(BinaryReader reader) + { + var name = reader.ReadString(); + var ip = new IPAddress(reader.ReadBytes(4)).ToString(); + var port = reader.ReadUInt16(); + var unknown = reader.ReadInt32(); + + return new ServerInfo(name, ip, port); + } + } +}
\ No newline at end of file |