using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ETDiscordPresence
{
internal class GameReader : IDisposable
{
private const int ADDRESS = 0x014D3650;
private const int ONLINE = 0x013E0B80;
private const int CSTRING = 0x013EA2B1;
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(int hObject);
[DllImport("kernel32.dll")]
private static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
private readonly Process process;
private readonly IntPtr handle;
public GameReader(Process process)
{
this.process = process;
handle = OpenProcess(0x0010, false, process.Id);
}
public GameWatcher.ServerInfo? Read()
{
var address = new byte[128];
var addressRead = -1;
var online = new byte[1];
var onlineRead = -1;
var cstring = new byte[1024];
var cstringRead = -1;
if (!ReadProcessMemory((int) handle, ONLINE, online, online.Length, ref onlineRead) ||
onlineRead != online.Length ||
online[0] != 1)
{
return null;
}
if (!ReadProcessMemory((int) handle, ADDRESS, address, address.Length, ref addressRead) ||
addressRead != address.Length ||
!ReadProcessMemory((int) handle, CSTRING, cstring, cstring.Length, ref cstringRead) ||
cstringRead != cstring.Length)
{
return null;
}
try
{
var addressValue = System.Text.Encoding.Default.GetString(address).TrimEnd((char) 0);
var cstringValues = System.Text.Encoding.Default.GetString(cstring).TrimEnd((char) 0).Split('\\');
string hostname = null;
for (var i = 1; i + 1 < cstringValues.Length; i += 2)
{
if (cstringValues[i].ToLower() != "sv_hostname")
{
continue;
}
hostname = cstringValues[i + 1];
break;
}
if (hostname == null || addressValue.Length == 0)
{
return null;
}
var hostnameClean = "";
for (var i = 0; i < hostname.Length; i++)
{
if (hostname[i] == '^' && i + 1 < hostname.Length)
{
i++;
continue;
}
hostnameClean += hostname[i];
}
if (hostnameClean.Length == 0)
{
return null;
}
return new GameWatcher.ServerInfo
{
Address = addressValue,
Hostname = hostnameClean,
};
}
catch
{
return null;
}
}
public void Dispose()
{
CloseHandle((int) handle);
process?.Dispose();
}
}
}