forked from quidrex/DSDeaths
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
189 lines (160 loc) · 6.8 KB
/
Program.cs
File metadata and controls
189 lines (160 loc) · 6.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Web.Script.Serialization;
namespace DSDeaths {
public class Game {
public string process { get; set; }
public int[] offsets { get; set; }
public bool isWow64 { get; set; } // true = game is a 32-bit process and runs in 64-bit emulation
}
class Program {
const int PROCESS_WM_READ = 0x0010;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(
IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWow64Process(
[In] IntPtr hProcess,
[Out] out bool wow64Process
);
static bool WriteDeaths(in string template, in string formatToken, in int deaths) {
try {
string s = template.Replace(formatToken, deaths.ToString());
File.WriteAllText("deaths.txt", s);
} catch (IOException) {
return false;
}
return true;
}
static bool PeekMemory(in IntPtr handle, in IntPtr baseAddress, in Game game, ref int value) {
long address = baseAddress.ToInt64();
byte[] buffer = new byte[8];
int discard = 0;
foreach (int offset in game.offsets) {
address += offset;
if (!ReadProcessMemory(handle, (IntPtr)address, buffer, 8, ref discard)) {
return false;
}
address = game.isWow64 ? BitConverter.ToInt32(buffer, 0) : BitConverter.ToInt64(buffer, 0);
if (address == 0) {
return false;
}
}
value = (int)address;
return true;
}
static bool ScanProcesses(in Dictionary<string, Game> games, ref Process proc, ref Game game) {
foreach (var kv in games) {
Game g = kv.Value;
Process[] process = Process.GetProcessesByName(g.process);
if (process.Length != 0) {
IsWow64Process(process[0].Handle, out bool isWow64);
// used to differtiate between regular ds2 and sotfs ds2
if (isWow64 != g.isWow64)
{
continue;
}
Print("Found: " + kv.Key);
proc = process[0];
game = g;
return true;
}
}
return false;
}
static void Print(in string msg)
{
string time = DateTime.Now.ToString("t");
Console.WriteLine(time + " " + msg);
}
static void Main() {
const string configPath = "config.json";
const string templatePath = "template.txt";
const string templateFormatToken = "$deaths";
string template = "";
Console.WriteLine("-----------------------------------WARNING-----------------------------------");
Console.WriteLine(" Does NOT work with Elden Ring if Easy Anti-Cheat (EAC) is running.");
Console.WriteLine(" Possible risk of BANS by trying to use with EAC enabled or disabling EAC.");
Console.WriteLine(" USE AT YOUR OWN RISK.");
Console.WriteLine("-----------------------------------WARNING-----------------------------------\n");
Console.WriteLine("DSDeaths - https://github.com/Quidrex/DSDeaths");
Console.WriteLine("INFO: Use the template.txt file to define a custom text.");
Console.WriteLine(" " + templateFormatToken + " will be replaced with the actual deaths.\n");
// try to load the game configs
string json;
if (File.Exists(configPath))
{
// file found
json = File.ReadAllText(configPath);
}
else
{
// file not found, create default config file
json = Config.json;
Print(configPath + " created");
File.WriteAllText(configPath, json);
}
// convert the json to an c# object
JavaScriptSerializer jss = new JavaScriptSerializer();
Dictionary<string, Game> games;
try
{
games = jss.Deserialize<Dictionary<string, Game>>(json);
}
catch(Exception)
{
Print("Could not load " + configPath + ". Using default config.");
games = jss.Deserialize<Dictionary<string, Game>>(Config.json);
}
// try to load the template text
if (File.Exists(templatePath))
{
// file found
template = File.ReadAllText(templatePath);
}
else
{
// file not found, create empty template text file
Print(templatePath + " created.");
File.Create(templatePath).Dispose();
}
// template file is empty, use no template text
if (string.IsNullOrEmpty(template)){
template = templateFormatToken;
}
Console.CancelKeyPress += delegate {
WriteDeaths(template, templateFormatToken, 0);
};
while (true) {
WriteDeaths(template, templateFormatToken, 0);
Print("Waiting for the game to start...");
Process proc = null;
Game game = null;
while (!ScanProcesses(games, ref proc, ref game)) {
Thread.Sleep(500);
}
IntPtr handle = OpenProcess(PROCESS_WM_READ, false, proc.Id);
IntPtr baseAddress = proc.MainModule.BaseAddress;
int oldValue = 0, value = 0;
while (!proc.HasExited) {
if (PeekMemory(handle, baseAddress, game, ref value)) {
if (value != oldValue) {
oldValue = value;
WriteDeaths(template, templateFormatToken, value); ;
}
Thread.Sleep(500);
}
}
Print("Game was closed.\n");
Thread.Sleep(2000);
}
}
}
}