summaryrefslogtreecommitdiff
path: root/Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared
diff options
context:
space:
mode:
Diffstat (limited to 'Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared')
-rw-r--r--Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/AmongUsModifier.cs247
-rw-r--r--Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Configuration.cs65
-rw-r--r--Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Events/ErrorEventArgs.cs14
-rw-r--r--Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Events/SavedEventArgs.cs16
-rw-r--r--Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Impostor.Patcher.Shared.csproj12
-rw-r--r--Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Innersloth/RegionInfo.cs48
-rw-r--r--Impostor-dev/src/Impostor.Patcher/Impostor.Patcher.Shared/Innersloth/ServerInfo.cs37
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